ChainScore Labs
All Guides

How Swap Fees Are Distributed to Liquidity Providers

LABS

How Swap Fees Are Distributed to Liquidity Providers

Chainscore © 2025

Core Concepts for Fee Distribution

Understanding the mechanisms that govern how trading fees are collected, allocated, and distributed to liquidity providers.

Fee Accrual

Swap fees are collected from each trade and added directly to the liquidity pool's reserves. This increases the total value of the pool tokens (LP tokens). The fees are not paid out immediately but are accrued proportionally to all LPs based on their share of the pool.

  • Fees are typically a percentage (e.g., 0.3%) of the trade value.
  • They are denominated in the tokens being swapped and remain in the pool.
  • Accrual is continuous and automatic with each transaction.

LP Token Share

A user's ownership in a liquidity pool is represented by LP tokens. The number of tokens you hold defines your share of the pool's total reserves, including accrued fees. When you withdraw liquidity, you receive your proportional share of both the underlying assets and all accumulated fees.

  • Minted when you deposit assets into a pool.
  • Acts as a receipt and ownership certificate.
  • Your fee earnings are directly tied to your percentage of total LP tokens.

Impermanent Loss

Impermanent Loss (IL) is the opportunity cost LPs face when the price of deposited assets changes compared to simply holding them. It occurs because AMMs rebalance pools to maintain a constant product formula. High fees can offset IL, making providing liquidity profitable despite price divergence.

  • IL is "impermanent" if asset prices return to their original ratio.
  • Fee income is the primary counterbalance to this risk.
  • Understanding IL is crucial for evaluating LP profitability.

Concentrated Liquidity

Advanced AMMs like Uniswap V3 allow LPs to provide concentrated liquidity within a custom price range. This increases capital efficiency and fee-earning potential within that range, as fees are only earned on trades occurring at those prices. It requires active management of price ranges.

  • LPs can allocate capital to where most trading volume occurs.
  • Earns higher fees per dollar deposited compared to full-range liquidity.
  • Exposes LPs to greater IL if the price moves outside their set range.

Fee Tier Selection

Many DEXs offer multiple fee tiers (e.g., 0.05%, 0.3%, 1%) for different pool types. The chosen tier affects the fee collected per swap and attracts different trading behaviors. Higher tiers may suit stablecoin pairs or exotic assets, while lower tiers compete for high-volume, efficient swaps.

  • Traders are routed to the pool offering the best effective price (including fees).
  • LP returns depend on the fee rate and the trading volume in that specific pool.
  • Selecting the right tier is a strategic decision for LPs.

Fee Claiming Mechanism

The process for LPs to claim accrued fees varies by protocol. In some models (e.g., Uniswap V2), fees are automatically added to the pool and claimed upon withdrawal. Others (e.g., SushiSwap) may have separate staking contracts or mechanisms to harvest fees without removing liquidity.

  • Some protocols require a separate transaction to "harvest" or compound fees.
  • Understanding the claiming mechanism is essential for optimizing returns and gas costs.
  • Fees may be reinvested (compounded) or taken as profit.

The Fee Collection Process

The mechanism for capturing and storing trading fees within a liquidity pool.

1

Fee Accrual During a Swap

How a portion of each trade is reserved as a fee.

Detailed Instructions

When a user executes a swap on an Automated Market Maker (AMM) like Uniswap V2, a protocol fee is automatically deducted from the input token amount. For example, a standard 0.30% fee on a 1 ETH swap reserves 0.003 ETH. This fee is not sent to a separate address; instead, it is added directly to the pool's reserves. This increases the pool's total liquidity, but the fees are not yet claimable by individual LPs. The critical on-chain calculation occurs within the swap function, where the input amount is multiplied by the fee denominator (e.g., inputAmount * 997 / 1000 for a 0.3% fee, with 3 basis points withheld).

  • Sub-step 1: Calculate Input Amount After Fee: The contract computes amountInWithFee = amountIn * 997.
  • Sub-step 2: Determine Output Amount: Using the constant product formula k = x * y, it solves for amountOut = (reserveOut * amountInWithFee) / (reserveIn * 1000 + amountInWithFee).
  • Sub-step 3: Update Reserves: The contract increases reserveIn by the full amountIn and decreases reserveOut by amountOut. The fee is the difference, now part of the inflated reserves.
solidity
// Simplified snippet from a swap function uint amountInWithFee = amountIn * 997; uint numerator = amountInWithFee * reserveOut; uint denominator = (reserveIn * 1000) + amountInWithFee; amountOut = numerator / denominator; // Reserves are updated with the full amountIn, capturing the fee

Tip: The fee accrues in a pooled, non-linear fashion. Your share of fees grows as your percentage of the total LP tokens increases, but you cannot claim them until you interact with the pool again.

2

Tracking Fees via Virtual Reserves and k

Understanding how fees are represented in the pool's state.

Detailed Instructions

Fees are not tracked in a separate ledger. Instead, they are implicitly accounted for by the growth of the constant product k. The formula k = reserveX * reserveY must remain invariant during a swap for the trader, but the fee causes k to increase for the pool. After a swap where 1 ETH (input) is traded for DAI with a 0.3% fee, the reserveETH increases by 1 ETH, while reserveDAI decreases by slightly less than the pre-fee output would suggest. This results in a new, higher k value. The difference between the old k and new k represents the total accrued fees, denominated in the product of both assets. Monitoring this requires comparing snapshots.

  • Sub-step 1: Query Pool Reserves: Use getReserves() on the pool contract (e.g., 0x...) to get reserve0 and reserve1.
  • Sub-step 2: Calculate Current k: Compute k_current = reserve0 * reserve1.
  • Sub-step 3: Compare to Historical k: Compare k_current to k_previous from a block before the last fee-accruing activity. The increase Δk quantifies collected fees.
solidity
// View function to calculate the pool's current k function getK() public view returns (uint256 k) { (uint112 reserve0, uint112 reserve1, ) = IUniswapV2Pair(poolAddress).getReserves(); k = uint256(reserve0) * uint256(reserve1); }

Tip: This k growth is the fundamental source of LP returns. Your share of this growing pie is proportional to your LP token balance relative to total supply.

3

Fee Realization Upon Liquidity Events

How LPs actually claim their accumulated fees.

Detailed Instructions

Fees become claimable only when a liquidity event occurs: depositing (mint) or withdrawing (burn) liquidity, or through a specialized claimFees function in newer protocols. In Uniswap V2, the mechanism is based on minting new LP tokens proportional to deposited liquidity. When you add liquidity, the contract calculates how many new LP tokens to mint by comparing the deposited amounts to the current reserves. Crucially, it also mints a small extra portion representing your share of the unclaimed fees since the last liquidity event. When you withdraw (burn), you receive your proportional share of both the underlying reserves and all accrued fees. The core math uses the totalSupply of LP tokens and the current reserves.

  • Sub-step 1: For Deposit: The contract calculates liquidity = min((amount0 * totalSupply) / reserve0, (amount1 * totalSupply) / reserve1).
  • Sub-step 2: Mint Tokens: It mints this liquidity amount of LP tokens to the depositor. The min function ensures proper ratio, and the difference from the ideal mint represents fee accrual.
  • Sub-step 3: For Withdrawal: The contract computes amount0 = (liquidity * reserve0) / totalSupply and amount1 = (liquidity * reserve1) / totalSupply, then burns the LP tokens.
solidity
// Simplified mint logic highlighting fee capture function mint(address to) external returns (uint liquidity) { (uint112 _reserve0, uint112 _reserve1,) = getReserves(); uint balance0 = IERC20(token0).balanceOf(address(this)); uint balance1 = IERC20(token1).balanceOf(address(this)); uint amount0 = balance0 - _reserve0; uint amount1 = balance1 - _reserve1; // liquidity minted is based on the *new* balances, which include fees liquidity = min(amount0 * totalSupply / _reserve0, amount1 * totalSupply / _reserve1); _mint(to, liquidity); _update(balance0, balance1, _reserve0, _reserve1); }

Tip: You do not need to withdraw to claim fees; they are automatically compounded into your LP position, increasing its underlying value.

4

Calculating Individual LP Fee Earnings

How to programmatically estimate fees accrued to a specific position.

Detailed Instructions

To estimate unclaimed fees for an LP position, you must compare the LP's share of the pool at two different points in time. The most reliable method uses the mint event emitted when the position was created to capture its initial share. You then calculate what the position's underlying assets would be worth at current reserves and compare it to the value if reserves had grown only from price movement (excluding fees). This involves tracking the totalSupply of LP tokens and the pool reserves. A common approach is to use the formula: fees_owned = (lp_balance / totalSupply) * (reserve_now - reserve_predicted_without_fees).

  • Sub-step 1: Get Position Snapshot: Query the LP token balance for the holder's address and find the totalSupply and reserves (reserve0, reserve1) at the time of their last deposit (from event logs).
  • Sub-step 2: Calculate Predicted Reserves: Using historical price data from an oracle (e.g., Chainlink), calculate what the reserves would be if only price changes affected them, holding k constant.
  • Sub-step 3: Compute Fee Difference: Subtract the predicted reserves from the actual current reserves. Multiply the difference by the LP's share (lp_balance / totalSupply).
javascript
// Pseudocode for estimating fees in a script const currentReserves = await pairContract.getReserves(); const currentTotalSupply = await pairContract.totalSupply(); const lpBalance = await pairContract.balanceOf(userAddress); const userShare = lpBalance / currentTotalSupply; // estimatedReservesWithoutFees comes from external price analysis const feeInToken0 = (currentReserves.reserve0 - estimatedReservesWithoutFees.reserve0) * userShare; const feeInToken1 = (currentReserves.reserve1 - estimatedReservesWithoutFees.reserve1) * userShare;

Tip: This is an estimate. The only way to realize fees with perfect accuracy is to simulate a withdrawal or call a specialized collect function, as in Uniswap V3.

5

Protocol Fee Switch and Treasury Diversion

The optional mechanism for diverting a portion of swap fees to protocol governance.

Detailed Instructions

Some AMMs, like Uniswap V3, implement a protocol fee switch controlled by governance. When activated, a fraction of the swap fees (e.g., 1/6th of the 0.30%, making it 0.05%) is diverted to a designated treasury address instead of accruing to LPs. This is managed by adjusting the fee growth accumulators (feeGrowthGlobal0X128, feeGrowthGlobal1X128) inside the contract. The switch does not change the trader's experience; the total fee taken is the same. However, the portion sent to the treasury is effectively removed from the liquidity pool's k growth, reducing LP returns proportionally. The logic is typically gated behind an onlyGovernance modifier and can be toggled on/off.

  • Sub-step 1: Governance Vote: Token holders vote to activate the fee switch, setting a protocolFee numerator (e.g., 1 for 1/6th).
  • Sub-step 2: Contract Update: The factory or pool contract updates its state to enable fee collection for the protocol.
  • Sub-step 3: Fee Splitting: During swaps, the fee calculation includes a step that mints LP tokens for the protocol or directly transfers a portion of the fee tokens to the treasury address 0x....
solidity
// Example logic for calculating protocol fees in a swap uint256 protocolFee = feeAmount * protocolFeeNumerator / protocolFeeDenominator; uint256 lpFee = feeAmount - protocolFee; // lpFee is added to pool's fee growth accumulators // protocolFee is sent to treasury via _safeTransfer _safeTransfer(tokenIn, treasuryAddress, protocolFee);

Tip: Before providing liquidity, check the protocol's documentation or on-chain state (e.g., factory.protocolFee() ) to see if the fee switch is active, as it impacts your potential yield.

Fee Distribution Models

Understanding Fee Distribution

Fee distribution is the mechanism by which trading fees collected by an Automated Market Maker (AMM) are allocated to liquidity providers (LPs). The primary model is pro-rata distribution, where fees are distributed proportionally to an LP's share of the total liquidity pool. For example, if you provide 1% of the liquidity in a Uniswap V3 pool, you are entitled to 1% of the fees generated from swaps in that pool.

Key Principles

  • Accumulation: Fees are not distributed instantly but accrue as a claimable asset within the pool. LPs must withdraw their liquidity to realize these earnings.
  • Concentrated Liquidity: In protocols like Uniswap V3, fees are only earned on swaps that occur within the specific price range where an LP's capital is active, creating more efficient but complex fee accrual.
  • Impermanent Loss Impact: Earned fees can offset the potential impermanent loss experienced by LPs, making the net profitability calculation crucial.

Example

When you provide liquidity to a Uniswap V2 ETH/USDC pool, 0.30% of every swap fee is added to the pool's reserves. Your share of these fees grows as your LP tokens represent a claim on a larger pool value.

Calculating Your Provider Share

Process for determining your portion of accumulated swap fees from a liquidity pool.

1

Identify Your Position's Share of Total Liquidity

Calculate your proportional ownership of the pool.

Detailed Instructions

First, you must determine your liquidity provider (LP) token balance relative to the total supply. This ratio represents your ownership stake in the pool's assets and, by extension, its accrued fees.

  • Sub-step 1: Query your LP token balance for the specific pool (e.g., UNI-V2 pair) from your wallet address using the token contract's balanceOf function.
  • Sub-step 2: Call the LP token contract's totalSupply function to get the circulating supply.
  • Sub-step 3: Calculate your share: Your Share = Your LP Balance / Total LP Supply. For example, if you hold 150 LP tokens out of a total supply of 10,000, your share is 1.5%.
solidity
// Example Solidity view function logic function calculateUserShare(address user) public view returns (uint256) { uint256 userBalance = IERC20(lpToken).balanceOf(user); uint256 supply = IERC20(lpToken).totalSupply(); return (userBalance * 1e18) / supply; // Share expressed with 18 decimals }

Tip: This share is dynamic. It decreases if you add liquidity during a period of high total supply growth, or increases if you are an early provider and others withdraw.

2

Query the Pool's Total Accumulated Fees

Determine the total fee value stored within the pool contract.

Detailed Instructions

Swap fees are not distributed immediately but accumulate within the pool, increasing the value of the underlying reserves. You need to find the total fees accrued since the last liquidity event or fee collection.

  • Sub-step 1: For a constant product AMM like Uniswap V2, check the current reserves of token0 and token1 via the getReserves function.
  • Sub-step 2: You must know or estimate the "virtual" reserves representing the pool's state if no fees had been collected. This is often tracked externally or can be approximated from the product constant (k) at the time of your deposit.
  • Sub-step 3: The total accrued fees in a token is the difference between the current reserve and the expected reserve without fees. For instance, if the pool should have 100 ETH based on k but actually holds 101 ETH, 1 ETH in fees has accumulated.
javascript
// Example using ethers.js to get reserves const reserves = await pairContract.getReserves(); const reserve0 = reserves[0]; const reserve1 = reserves[1]; // Compare to a snapshot of reserves at a past block to estimate accrued fees.

Tip: Some newer AMM designs (e.g., Uniswap V3) explicitly track fee growth per unit of liquidity (feeGrowthGlobal) outside the reserves, making this calculation more precise.

3

Calculate Your Fee Entitlement

Apply your share to the total fees to find your claim.

Detailed Instructions

Multiply your proportional share from Step 1 by the total accrued fees for each token in the pool. This yields your unclaimed fee entitlement.

  • Sub-step 1: For each token in the pair (token0 and token1), calculate your portion: Your Fees (token0) = Your Share * Total Accrued Fees (token0).
  • Sub-step 2: Ensure calculations use consistent precision. LP shares and reserve values are typically 18 decimals; perform multiplication before division to avoid rounding errors.
  • Sub-step 3: The result is the amount of each token you would receive if all accrued fees were distributed proportionally at this moment. For example, with a 1.5% share and 1 ETH in accrued fees, your entitlement is 0.015 ETH.
solidity
// Pseudocode for calculating entitlement uint256 userShare = (userLpBalance * 1e18) / totalLpSupply; // 18-decimal precision uint256 feesToken0 = (currentReserve0 - invariantReserve0); uint256 userFees0 = (feesToken0 * userShare) / 1e18;

Tip: Your entitlement is not automatically sent to your wallet. You must interact with the pool contract (e.g., burn LP tokens, call a collect function) to claim and realize these fees.

4

Account for Fee Growth Since Your Last Interaction

Track fees earned during your specific position's lifetime.

Detailed Instructions

Your personal fee accrual depends on when you provided liquidity. Advanced AMMs track fee growth per liquidity unit to calculate this precisely.

  • Sub-step 1: When you mint a position (e.g., in Uniswap V3), the contract records the current feeGrowthGlobal0X128 for each token.
  • Sub-step 2: To calculate fees earned, subtract the recorded feeGrowthInsideLast from the current feeGrowthInside for your position's price range.
  • Sub-step 3: Multiply this delta by your position's liquidity amount: Fees = (feeGrowthDelta * liquidity) / 2**128. This gives the exact token amount accrued to your position, independent of other LPs.
solidity
// Uniswap V3 style calculation for fees function getFeesEarned( uint128 liquidity, uint256 feeGrowthInsideLast, uint256 feeGrowthInsideCurrent ) internal pure returns (uint256 feeEarned) { feeEarned = uint256( (feeGrowthInsideCurrent - feeGrowthInsideLast) * liquidity ) / type(uint128).max; }

Tip: For simpler V2-style pools, you must manually compare your share at deposit time versus now, as fees are distributed pro-rata to all current LPs, benefiting those who held longer.

5

Factor in Impermanent Loss on Fee Value

Understand how price movement affects the real value of collected fees.

Detailed Instructions

The USD value of your fee income is not fixed. It depends on the market prices of the tokens when you claim them, which are influenced by the same impermanent loss (IL) dynamics affecting your principal.

  • Sub-step 1: Calculate the current value of your fee entitlement in a stable denomination (e.g., USD) using oracle prices or a DEX spot price.
  • Sub-step 2: Compare this to the value the fees would have had if the token prices had remained stable since accrual began. The difference is the IL impact on fees.
  • Sub-step 3: Recognize that high volatility can lead to a scenario where the dollar value of accrued fees in a depreciating asset is less than expected, even if the token quantity is correct.
javascript
// Example: Valuing fee entitlement const ethFees = 0.015; // From Step 3 const currentEthPrice = 3000; // USD per ETH const feeValueUSD = ethFees * currentEthPrice; // $45 // If ETH price dropped 20% since fees started accruing, value is reduced.

Tip: Providing liquidity in a stablecoin pair or a correlated asset pair minimizes IL, making the fee income more predictable in value terms.

Protocol Fee Distribution Comparison

Comparison of fee distribution mechanisms across major decentralized exchanges.

Distribution MechanismUniswap V3Curve FinanceBalancer V2

Protocol Fee Switch

Off by default, governance-controlled (10-25% of swap fees)

Active, 50% of trading fees to veCRV holders

Active, up to 50% of swap fees to BAL stakers

LP Fee on 0.05% Pool

0.05% (100% to LPs if fee switch off)

0.04% (50% to LPs, 50% to protocol)

0.05% (varies by pool, up to 50% to protocol)

Fee Collection Trigger

Accumulated per-transaction, claimed on position adjustment

Accumulated continuously, claimable via gauge

Accumulated in vault, claimable by withdrawing liquidity

Governance Token Utility

UNI votes control fee switch and rate

veCRV lock determines fee share and gauge weights

BAL staking determines fee share and pool incentives

Fee Tier Flexibility

Multiple tiers (0.01%, 0.05%, 0.3%, 1%) with same distribution logic

Fee is pool-specific, typically 0.04% for stable pools

Customizable per pool, often 0.05% to 1% with customizable split

LP Reward Composability

Fees auto-compound into position as increased liquidity

Fees accrue as claimable 3CRV or native tokens

Fees accrue as underlying tokens, can be restaked manually

SECTION-FAQ

Fee Distribution FAQs

Ready to Start Building?

Let's bring your Web3 vision to life.

From concept to deployment, ChainScore helps you architect, build, and scale secure blockchain solutions.