How AMMs work: How do LIQUIDITY POOLS work?
Impermanent Loss: What Is IMPERMANENT LOSS?
Uniswap v3 introduced the concept of Concentrated liquidity provision in DeFi, giving individual LPs granular control over their capital's price ranges. Instead of providing equivalent liquidity depth as v2 LPs with less capital, v3 LPs can choose to provide greater depth with the same amount of capital as their v2 counterparts. This requires taking on more price risk ("impermanent loss") while supporting greater amounts of trading and earning higher fees.
Individual positions are aggregated into a single pool, forming one combined curve for traders to trade against whilst also allowing multiple fee tiers for LPs to be compensated appropriately for taking on varying degrees of risk.
Limit Orders: LPs can sell one asset for another by adding liquidity to a price range entirely above or below the market price, approximating a fee-earning limit order that executes along a smooth curve.
Oracle: Uniswap's oracles became cheaper to integrate. V3 oracles are capable of providing time-weighted average prices (TWAPs) on demand for any period within the last ~9 days. This removed the need for integrators to checkpoint historical values by themselves.
To understand the core concepts and calculations behind Uniswap V3 we first need to understand how accounting occurs on a tick-by-tick basis.
In Uniswap v3 to implement custom liquidity provision, the space of possible prices is demarcated by discrete ticks. Liquidity providers can provide liquidity in a range between any two ticks (need not be adjacent). Each range can be specified as a pair of signed integer tick indices:
Ticks represent prices at which the virtual liquidity of the contract can change. Prices in liquidity pools are expressed as the price of one of the tokens (token0) in terms of the other token (token1). Conceptually, there is a tick at every price 𝑝 that is an integer power of 1.0001. Identifying ticks by an integer index 𝑖, the price at each is given by
This provides a property of each tick being 1 basis point price movement away from its neighbouring ticks. In Uniswap v3, the contract can be thought of as having virtual reserves — 𝑥 and 𝑦 to describe the contract’s behaviour (between adjacent ticks) as if it followed the constant product formula.
Operationally instead of tracking those virtual reserves, the pool contract tracks two different values: liquidity (𝐿) and the square root of Price (√𝑃). Due to this tracking of √𝑃
the smart contracts end up calculating
^(i/2)
When liquidity is added to a range, if one or both of the ticks is not already used as a bound in an existing position, that tick is initialized. Not every tick can be initialized. The pool is instantiated with a parameter, tickSpacing (t𝑠); only ticks with indexes that are divisible by tickSpacing can be initialized.
For example - If tickSpacing is 2, then only even ticks (...-4, -2, 0, 2, 4...) can be initialized.
Small choices for tickSpacing allow tighter and more precise liquidity ranges but may cause swaps to be more gas-intensive as crossing each initialized tick accrues gas cost for the swapper.
Because of the nature of the price calculations, the gap between adjacent ticks broadens or shortens as we move up/down the tick indices. Displayed below is another example for tokens starting at equivalent prices -
price spacing approximately increases from 5*10^-5 to 6*10^-5 between tick index 1 & 5000
price spacing approximately decreases from 5*10^-5 to 4*10^-5 between tick index -1 & -5000
The higher the tick spacing the lower the precision. Low volatility pairs (e.g. stablecoin pairs) need higher precision because price movements are narrow in such pairs. Medium and high volatility pairs need lower precision since price movements are wide in such pairs. Uniswap allows picking a tick spacing when a pair is deployed to handle this diversity.
For any liquidity provider to create derivative positions or hedge their Uniswap LP positions, they will first and foremost need a methodology to value the current value of their liquidity deployed for market making. Using equations 6.29 & 6.30 from the V3 whitepaper, the number of token0 and token1 in a new LP position will depend on the range determined by the lower tick a, the upper tick b and the price at entry P0.
The value of ∆E is determined by the initial amount token0 (denoted by x0) and token1 (denoted by y0) that is locked into the position when it is established:
Once the position is established, we can compute its Net Liquidity Value by adding the amount of token1 to the amount of token0 times the price P
If the price is above the upper tick b, the Net Liq value of the LP token will converge to the geometric mean √(a*b). When the price is below the lower tick a, the value of the LP token will simply be P times the size of the position. Between the tick ranges a and b, the Net liquidation value can be calculated as
After building the intuition to price one’s LP position we need to understand how profitable is it to deploy liquidity on Uniswap V3. (i.e. we want Fees Collected > Impermanent loss). To do so we can formulate the calculation of Impermanent loss for any V3 LP position for any arbitrary range (a,b).
Source: Impermanent Loss in Uniswap V3
Uniswap v3 has popularized the concept of leveraged liquidity provision - the trading range in which liquidity is provided is reduced and achieves a higher degree of capital efficiency through the elimination of unused collateral. This leverage increases the fees earned, but it also increases the risk taken, i.e., the IL.
We consider a market with liquidity L and amounts x and y of token0 and token1 respectively in a concentrated liquidity position. We set the initial price P of token 0 in terms of token 1 (P = y/x) and consider a price movement to P’ = P*k where k > 0. We also define [a, b] to be the price interval for a concentrated liquidity position. Both P and P' are assumed to be inside this interval. The trading function is defined as
Case 1: Within the price interval
Case 2: Below the lower price limit (a)
Case 3: Above the higher price limit (b)
To read more about the impermanent loss across individual pools and its effects on profitability - Impermanent Loss in Uniswap v3. As we’ve seen in this section, Uniswap V3 leads to leveraged impermanent loss as compared to V2-styled pools. This downside also brings with it an upside in the form of capital efficiency.
Source: Uniswap v3 Capital Efficiency | by Shao
The benefit of the capital efficiency of V3 only exists as LPs of V2 can only provide liquidity throughout the price range (0,∞), while LPs of v3 can customise the range and thus end up with the same amount of liquidity while using far less capital.
To formulate the efficiency, we need to figure out how many units of token0 (x) and token1 (y) on v2 can achieve the same amount of liquidity as providing x’ amount of token0 (or y', or both x’ + y’) on v3, in a price range (a, b), for a < b. To find an answer, these are the steps to go through
What is the common unit that describes the amount of liquidity provided to a Uniswap pool of both versions (v2 and v3)?
What’s the equivalence of x + y on v2 to x' (or y', or x’ + y’) on v3, in a range of (a,b)?
The capital efficiency of v3 compared to v2 is (x + y) / x’ times more efficient.
Liquidity is defined as L , for L = √x * √y = √k in Uniswap v2.
Their V3 Github contains a function getAmount0Delta(). This function helps find out that given an amount of liquidity, to make the price go up from lower to upper, what is the amount of token0 going to be swapped into token1 completely:
Liquidity/sqrt (lower) - Liquidity/sqrt (upper)
For simplicity let’s go through an example with liquidity (L) = 1 to eliminate a variable from our analysis.
When L = √x * √y = 1, we take an arbitrary range (0,2) in the tick index form, which is equal to the price range (1.0001⁰, 1.0001²) = (1, 1.00020001).
In a V2 style pool, If the price of X is to go from 1 to 1.00020001, 0.000099990001 x in the pool has to be swapped into 0.0001 y.
But in the case of the V3-styled pool, we would only require 0.000099990001 x tokens to be deployed to provide liquidity over the two ticks for swapping the same amount of token y to x. This can be confirmed by using the getAmount0Delta formula:
*To understand why only token0 (liquidity in x token) was required: Range orders section
In the example: What is the equivalent amount of V2 tokens (x + y) needed to match 0.000099990001 of X token on v3 in the price range (1, 1.00020001) when there is minimal liquidity in both v2 and v3 pools?
V2 Net liquidity: 1 X + 1 Y = 1 * price of X + 1 = 1 * 1 + 1 = 2
V3 Net liquidity: 0.000099990001 X * price of X = 0.000099990001 * 1 = 0.000099990001
Capital efficiency = 2/0.000099990001 = 20,002
But what if liquidity is ≠ 1?
Let’s take another example where L = 10,000 with x=y= 100 over a similar 2-tick price range
V2 Net liquidity: 100 X + 100 Y = 100 * price of X + 100= 100 * 1 + 100 = 200
V3 Net liquidity: 0.0099990001 X * price of X = 0.0099990001 * 1 = 0.0099990001
Capital efficiency = 200/0.0099990001 = 20,002
This displays how liquidity L is not even part of the equations for capital efficiency, provided the lower and upper bounds of price ranges haven’t changed.
To calculate mathematically:
V2 Net liquidity: x*X + y*Y = x*Price of X + y*1 = x*y/x + y = 2*y
V3 Net liquidity: (1/a - 1/b)*X =(1/a - 1/b)(y/x)
To see proof behind getAmount0Delta formulation: Uniswap v3 Capital Efficiency | by Shao
One of the most commendable features of the Uniswap product is that it calculates the most optimal trade via V2 and V3 pools for any swapper accounting for slippage, gas and depth of liquidity available in individual pools.
Source: Introducing the Auto Router
Uniswap implements what’s called Auto Router, an algorithm that finds the shortest path between two tokens. Moreover, it also splits one payment into multiple smaller payments to find the best average exchange rate. The profit can be as big as 36.84% compared to trades that are not split.
Split routes which execute trades across multiple pools at once
More powerful algorithm that considers a larger data set for larger trades and better prices
Gas cost awareness ensures every added step is net positive for your trade
If there is a better trade available on Uniswap v2 after accounting for gas costs, the Auto Router will automatically switch to v2.
The Auto Router enables efficient execution, especially on large trades. Trade without the Auto Router and an 80Mn USDT: USDC V2 swap could incur a price impact of -62.9%. With the Auto Router, the same trade can be executed with a price impact as low as -0.749%.
One way that the Auto Router achieves better prices is by splitting trades across multiple pools. In the legacy router, a trade was always executed through a single route (with or without multi-hop).
The Auto Router factors in gas costs to optimize your effective price. With the new router, smaller trades will execute more efficiently by minimizing the number of gas-intensive hops to reach the output token, whereas, for larger trades, the base universe of pools to be analysed is expanded for better price execution.
For any reader to dive deeper into the nuts and bolts of the routing logic, the code can be found here: @uniswap/smart-order-router. I will do a code overview and share the detailed logic behind the path-finding algorithm used by the Uniswap router.
Additional reads for trade split, routing logic for aggregators and Uniswap:
Reveal the Secrets of the Aggregator — Problem Analysis and Model Building
Theory vs practice: A deeper look at ETH-USDC liquidity dynamics
We have already discussed the valuation and impermanent loss associated with an LP position. But the payoff diagram for these positions presents another opportunity in the form of options trading and underwriting. Perpetual Uniswap v3 Options can be deployed right now between any two assets, for any expiration time, at any delta.
Short Put Option: As shown below, A uni V3 LP payoff is very similar to the short put option. Deploying a short put option at price K corresponds to locking K token1 (numeraire) at a strike price K.
Long Put option: Just as providing liquidity in a UniV3 pool (E.g. ETH-DAI) is equivalent to shorting a put option if users can take out the same liquidity from an existing LP position with strike price K, the payoff resembles a long Put option.
In theory, long call options can be synthetically created by leveraging the put-call parity and combining a long put with a long asset position. However, we can avoid the creation of a short stock position by inverting the token pair, as explained below.
Short call option: At a fundamental level, a call at strike K in an ETH-DAI pool is identical to a put at strike 1/K in a DAI-ETH pool. Thus to mint a short call, a user would need to provide 1/K ETH liquidity to the ETH-DAI pool at strike price K (Ideally at current price S < K).
Long call option: Similarly, recreating a long call payoff would involve removing 1/K ETH at a strike price K and locking it in an ETH liquidity pool.
Source: How to Create Perpetual Options in Uniswap v3
As mentioned in the section above - Uniswap V3 allows us to write options that can mimic traditional options with any delta or any expiration date. This property of being able to tune the position to reflect various expiration dates is achieved by altering the range of liquidity provided. Given that a normal LP position looks like a covered call:
Scaling factor r = √(b/a)
Time Tr from expiration.
After approximating a Black Scholes call option, At-The-Money we get call value ~
we can now find a relation with effective days to expiration (Tr). Inverting this formula, we can also find what scaling factor rT corresponds to a covered call option a time T from expiration:
Gamma is a measure of the convexity of an asset’s payoff curve. An asset with a positive convexity will increase in value faster as its price increases, while an asset with negative convexity will see diminishing returns as its price increases.
Crypto native primitive Sqeeth is an asset with a payoff where gamma = +2 at all prices. Single-tick Uniswap v3 LP positions can be used to hedge the gamma of payoff curves with positive convexity like Squeeth (ELI5: We can create a fixed negative gamma payoff using Uniswap V3).
To compute the delta and gamma of a Uni v3 LP position, we first start from the value of a Uniswap v3 LP position established between a lower price Pa and an upper price Pb. Then, if we compute the derivative of the payoff as a function of the price S, we obtain:
The Gamma of a single-tick Uni v3 position looks (almost) like a rectangle of width . To keep the area under that curve equal to unity, the height of that rectangle needs to be . It can also be assumed to be a bad approximation to a Dirac delta function.
Since each 1-tick wide LP position have no overlap and they are all independent of one another, they can be used to approximate any payoffs with negative convexity as the linear combination of several 1-tick positions.
Using this, we can easily neutralize the constant gamma=𝚪 (2) of a squeeth between an arbitrary range (A=1.0001^a, B=1.0001^b) by deploying single-tick positions for which the liquidity at tick K is equal to the parameter a
The Uni v3 position will effectively hedge the gamma of a squeeth, but only between the lower and upper prices (Pa, Pb). Deploying several narrow Uniswap v3 LP positions with liquidity given by 2Ki(r²-1)/r would result in a payoff function with gamma = -2 so that:
Even though this framework is out of sync with the rest of the post I wanted to mention this because this provides a unique mechanism to isolate 1 option greek (gamma) and can be utilised by experienced option traders to create unique payoffs.
**Imagine if traders can isolate delta, gamma, vega, and theta for options on-chain and can put them together arbitrarily - to generate new structured products. **
With the understanding of all the tools, payoff structures and formulas discussed in the earlier sections we can now try to hedge Uniswap V3 LP positions to generate real yield via trading fees collected and also limit the potential impermanent loss. We discuss 2 methodologies in this section:
Trying to hedge the entire LP position
To maintain spot-like exposure whilst collecting trading fees)
Caveats
The delta for the Squeeth position is price dependent and might require rebalancing the perpetual hedges on a recurrent time frame. Simultaneously the price of oSQTH will change due to accumulated funding, so to maintain a hedge LPs have to check in occasionally and rebalance
Univ3 positions have no gamma outside of the range, so the hedge needs to reduce to zero. Practically this means we need to run wide ranges or frequently rebalance to ensure the Squeeth gamma has some Uniswap gamma to hedge.
The resulting position will still have some leftover cubic, and higher-order terms since the impermanent loss can be displayed as a Taylor series, and Squeeth only eliminates the second-order term (ETH²)
Under normal circumstances, Squeeth will pay funding from long positions to short. This funding cost that LPs pay for the hedge should be offset by fees collected in UniV3. Uniswap pool implicitly prices the volatility of its assets via % fees, and Squeeth also prices volatility through its funding rate. The hedge will work if Uniswap is pricing higher volatility than Squeeth.
For example, say ETH is $1.5k, and an LP wants to provide 1ETH liquidity in the ETH-USDC pool between 1.5k-2k and want to avoid IL as much as possible. LP can then buy 2 atm puts for hedging LP delta at the Strike price (cost: premium) + 1x long perps @ $1.5k helps hedge the beta of in-the-money put options (cost: Funding rates)
If $ETH is 2k at expiry
LP would have 1.73k(=1.5k*2k) USDC from UniV3 Pool + fees accrued when position was in range + $500 from perp - 2 premiums (about $100-120 for each ATM on Deribit) ~ 2k + LP fees — would’ve had 2k if simply held ETH
If $ETH is 1k at expiry
Assuming the LP was never in range, one would have 1 $ETH + $1k from puts - $500 from perp = 1.5k - premiums (~200-240)
Just In Time Liquidity is an occurrence unique to Uniswap v3’s concentrated liquidity. A large amount of liquidity is added by a JIT bot when they see a large trade in the Ethereum mempool. It is then removed immediately after within the same block. This liquidity added before the trade reduces price impact for the trader by increasing the pool size, but diluting the trading fees distributed amongst existing LPs from the trade. Unlike MEV, where a sandwiched trade causes a loss to the trader, JIT causes a loss to existing LPs.
This is a domain of MEV that I believe will see increasing competition as we progress. The most active pool (USDC-WETH 5bps pool) alone accounts for over half of all JIT liquidity ever supplied.
Two necessary conditions need to be met for an LP to supply JIT liquidity as they result in a limited set of scenarios where it is optimal to provide JIT liquidity:
The expected profit is positive
The LP can outbid other MEV opportunities in Flashbots auctions
1. Expected Profit: JIT LPs’ profit comes from the difference between LP fee earned and the cost of hedging. In the simplest form, the JIT LP supplies liquidity on Uniswap and simultaneously executes an offsetting trade on a different venue (e.g.Binance, FTX) with a lower fee rate to hedge.
Revenue = Liquidity supplied × Uniswap pool fee rate + Price impact on Uniswap pool
Cost = Liquidity supplied × Other Exchange fee rate + Price impact on other exchange + Gas fee of adding liquidity + Gas fee of removing liquidity + Profit share to validator
Additional Points (Revenue Side):
Additional Points (Cost Side):
Fee rates on other exchanges vary, but they are non-negative. Therefore, the first cost item - Liquidity * Other exchange fee rate has a lower bound of 0.
Simultaneously, JIT LP will need to take liquidity(from the order book) while hedging, causing a non-negative price impact. The second cost item, Price impact on other exchanges, can also be reasonably assumed to have a lower bound of 0.
This, combined with the need for expected profit to be positive, we can infer
Profit ≤ Liquidity supplied × Uniswap fee rate − Total gas − Validator share
Validator share ≤ Liquidity supplied × Uniswap fee rate − Total gas
Outbid other MEV opportunities in Flasbots auction: This can only happen when a transaction is more profitable than all other MEV using the same swap (e.g. sandwich attacks, back-running arbitrage, etc).
When compared to a back-running arbitrage strategy where:
Profit = Size of swap × (price impact of swap − Uniswap fee rate) − Total gas − Validator share
Validator share ≤ Size of swap × (price impact of swap − Uniswap fee rate) − Total gas
For a JIT to happen, we need the expected profit from JIT to be greater than or equal to the expected profit from a simple back-running swap:
JIT liquidity supplied × Uniswap fee rate − JIT gas fee ≥ Size of swap × (price impact of swap − Uniswap fee rate) − swap gas fee
Now,
Liquidity supplied in a JIT transaction is at most 100% of the swap (JIT Liquidity supplied ≤ Size of Swap)
JIT involves mint and a burn while backrunning only has one swap, the gas fee for JIT should always be higher (JIT gas fee > Swap gas fee)
JIT liquidity supplied × Uniswap fee rate > JIT liquidity supplied × (price impact of swap−Uniswap fee rate)
2 × Uniswap fee rate > price impact of swap
The right-hand side is basically the price improvement traders get from JIT liquidity. With some of the above assumptions, we have derived an upper bound of price improvement: two times the fee rate in the Uniswap pool that the swap is going through. This is likely not the actual upper bound as instead of simply backrunning the large swap, an MEV searcher could potentially generate more profit from a sandwich attack. But this framework provides us with a good approximation.
When the JIT LP observes an incoming swap. They have two options:
Provide JIT liquidity in the current tick space
Provide JIT liquidity in the next tick space.
The tradeoffs are the following: by choosing option 2 over option 1, the LP is forgoing part of the fee (on the volume required to cross the tick) in exchange for a lower cost basis for the position they are acquiring. The LP would only choose to mint one-sided liquidity in the next tick if the gain from the lowered cost basis is larger than the forgone fee income.
E.g. For token trading at 2$ that a swapper wishes to acquire. JIT LP has also acquired tokens at $2. They can either provide liquidity for a $100,000 trade at 2$ or provide liquidity for a $99,500 at $2.01
Let's denote
The size of the incoming swap to be S
The distance between starting marginal price in the pool and the next tick to be d
The fee rate of the pool to be r
The volume required to cross the tick (/other LP capital deposited between starting marginal price and next tick) to be L
The forgone fee when the LP chooses to mint one-sided in the next tick space is:
The gain from acquiring the remaining position at a lower cost basis is:
The LP would choose to do next tick if the gain is larger than the forgone fee:
Source: Time-vesting with the Uniswap v3 staker — Revert
Liquidity mining APYs on Uniswap V2 forks was prime yield farming opportunity that we enjoyed in 2020. But I do believe that protocols can design liquidity mining programs that are more effective and create better market depth and trading volumes for the amount of incentives given out.
Initial attempts at creating liquidity mining programs on UniV3 were exploited as users adopted a simple strategy of
Depositing liquidity into the minimum possible range
Exiting the position when it went out of range
Selling the received rewards
Swapping into a new minimum range position around the current price
This flaw was evident when Ribbon launched one of the first UniV3 staker programs. It ended with 77% of the rewards going to just 14 accounts, all using the strategy described above. Even after achieving the goal of bootstrapping liquidity, the visible problems of this setup were
Maintaining a large fraction of the liquidity after the incentive programs finish
Distributing the rewards among a wide set of token holders
Attracting liquidity that is more widely distributed along the possible price range
Causing a constant sell pressure by the ultra-concentrated liquidity providers
Potential solutions to these problems can be achieved by:
Adding a minimum range for positions (E.g. a 200+ ticks requirement to qualify for rewards) and stake LP positions. This can help prevent ultra-concentrated positions from even participating in the incentives programs.
Time-vesting the rewards for the incentive programs. They could be vested over a few days or the full duration of the incentive program. This can prevent the 1-tick strategy seen in the RBN pool as these positions were usually out of range a few times per day and wouldn’t accrue rewards for those epochs.
An added benefit of these partnerships could be that liquidity can be sticky even after the completion of the rewards program as end users of these applications are passive LPs (lazy liquidity) and are not optimizing for value extraction
All of the sections discussed wrt Uniswap V3 are interesting but don’t really translate to value generated for UNI token holders. For that purpose, I wanted to share insights from a bankless article about the UNI fee switch and what it can mean for protocol revenue generation.
Source: What if Uniswap Turned On the Fee-Switch? - by Ben Giove
Vote on the fee switch: Snapshot | Fee switch Pilot for Three Pairs
The disconnect between the success of a protocol and the token has been most apparent in Uniswap, after becoming the largest DEX by volume on Ethereum L1 (with 67.9% share). The protocol has generated $1.20B for its LPs over the past year. Nearly every other major spot or derivatives DEX, such as Curve, Balancer, SushiSwap, GMX, dYdX, and Perpetual Protocol, take a cut of LP fees.
Given this, it seems likely that Uniswap also should be able to charge a fee without losing a meaningful percentage of market share. By toggling the fee switch via a governance vote by UNI holders, the Uniswap DAO can accrue between 10-25% of the fees earned by LPs on a pool-by-pool basis. A proposal to turn on the fee switch for three pools has made its way through the Uniswap governance.
The proposal suggests taking the smallest take rate - a 10% cut of LP fees for three pools on Uniswap’s Ethereum deployment. The fee switch will be activated for 120 days (~4 months), and fees earned will accrue to the Uniswap DAO treasury. The selected pools, and their fee-tiers, are as follows:
ETH-DAI (0.05%)
ETH-USDT (0.30%)
ETH-USDC (1%)
We can take a look at how much Uniswap would have earned if the 10% protocol fee was toggled for these three pools. To get a better idea of earnings in different market conditions, we’ll take a look at the fees earned during the past 30, 120 (proposal duration), and 365 days. In addition, we’ll also check what portion of total Uniswap trading volumes and LP fees over these periods was derived from the three pools.
30-Day Trading Volume = $1.31B,
Fees earned by LPs = $2.90M.
Protocol Revenue (if fee switch was on) = $290K or ~$3.48M (annualized)
Largest contributor to fees -> wETH-USDT pool with 74.4% of protocol income
Contribution of 3 pools to total trading volume = 3.08%
Contribution of 3 pools to total fees = 5.71%
120 days
120-Day Trading Volume = $6.41B,
Fees earned by LPs = $16.04M
Protocol Revenue (if fee switch was on) = $1.60M or ~$4.87M (annualized)
Largest contributor to fees -> wETH-USDT pool with 81.7% of protocol income
Contribution of 3 pools to total trading volume = 3.32%
Contribution of 3 pools to total fees = 6.68%
365 days
Past 365-Days Trading Volume = $40.40B,
Fees earned by LPs = $78.19M
Protocol Revenue (if fee switch was on) = $7.82M
Largest contributor to fees -> wETH-USDT pool with 80.8% of protocol income
Contribution of 3 pools to total trading volume = 5.69%
Contribution of 3 pools to total fees = 6.47%
Uniswap would have generated anywhere between $3.48M-$7.82M in annualized profit had the fee switch been turned on for the 3 pools. Most importantly, the 3 pools only accounted for a small portion - making up just 3.08-5.69% of total volumes and 5.71-6.68% of total fees depending on the period.
But what if every single liquidity pool turned on a flat 10% fee for protocol revenue
Uniswap would have earned $5.07M (~$61.68M annualized), $23.99M (~$72.96M annualized), and $120.8M over the 30, 120, and 365-day periods respectively.