Theo Diamandis

Swap Pools

Most CFMMs in practice are implemented as swap pools between exactly two assets.

A simple abstraction

It is often useful to consider CFMMs in terms of their trading function, e.g.,

φ(R)=R1R2 \varphi(R) = \sqrt{R_1 R_2}

for Uniswap V2. However, for more complicated CFMMs, these functions may not have closed forms. (Recall that we only require the trading set to be convex.)

In this case, it is helpful to define the interface we use to interact with the CFMM. As long as the CFMM implements this interface, we do not care about the internal representation. Let Δ be the nonnegative input trade vector in ℝ². For two asset CFMMs, we only require three functions:

  1. get_price(Δ): Returns the price quoted by the CFMM after making a trade of size Δ

  2. swap!(cfmm, Δ): Returns the output trade vector Λ given input trade vector Δ and updates reserves accordingly

  3. update_liquidity!(cfmm, range): Updates the reserves of the CFMM with range, which specifies an amount of liquidity L to provide to an asset i uniformly over some interval p₁ to p₂. Note that the interval endpoints are restricted in practice (and are p₁ = 0, p₂ = ∞ for many CFMMs).

Finding arbitrage

Recall that our method requires the optimal arbitrage function arb(ν)\mathbf{arb}(\nu) to be defined for each CFMM, where ν\nu is a vector of prices. Without loss of generality, we will take asset 2 to be the numeraire, so that the input price ν=ν1/ν2\nu' = \nu_1 / \nu_2. In the case without fees, we simply want to find the trade Δ such that

get_price(Δ)=ν \mathrm{get\_price}(Δ^\star) = \nu'

which is a one-dimensional root finding problem (since we know which asset will be bought/sold based on ν\nu').

In the case with fees, we use the fact that for the price impact function gg, we have that the price impact function with fees gfg^f is[1]

gf(Δ)=γg(γΔ). g^f(\Delta) = \gamma g(\gamma \Delta).

Thus, to calculate the optimal arbitrage, we need to solve the problem

γget_price(γΔ)=ν. \gamma \cdot \mathrm{get\_price}(\gamma Δ^\star) = \nu'.

Concentrated liquidity

In the special case of concentrated liquidity (e.g., Uniswap V3), we can define additional functions:

  1. get_interval(mp): Returns the interval i containing the price mp.

  2. arb_interval(i, mp): Returns the trade Δ, which optimally arbitrages the CFMM in interval i with external market prices mp (assumes that mp is in interval i).

  3. trade_intervals(i): Returns the trade Δ which uses all the liquidity in the intervals from the current interval up to but not including interval i.

Finding arbitrage

Given the three functions above, the arbitrage problem becomes even simpler and can be solved in a non-iterative fashion with the following algorithm.

i ← get_interval(mp/γ)              # find post-trade interval
Δᵃ ← arb_interval(i, mp)            # find opt arb in interval
Δⁱ ← trade_intervals(i)             # get all liquidity up to interval
Δ ← Δⁱ + Δᵃ                         # total trade
swap!(cfmm, Δ)                      # execute swap


[1] G. Angeris, A. Evans, T. Chitra (2020). When does the tail wag the dog? Curvature and market making.