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

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

$\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:

`get_price(Δ)`

: Returns the price quoted by the CFMM after making a trade of size`Δ`

`swap!(cfmm, Δ)`

: Returns the output trade vector`Λ`

given input trade vector`Δ`

and updates reserves accordingly`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).

Recall that our method requires the optimal arbitrage function $\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 $\nu' = \nu_1 / \nu_2$. In the case without fees, we simply want to find the trade `Δ`

such that

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 $g$, we have that the price impact function with fees $g^f$ is^{[1]}

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

$\gamma \cdot \mathrm{get\_price}(\gamma Δ^\star) = \nu'.$In the special case of concentrated liquidity (e.g., Uniswap V3), we can define additional functions:

`get_interval(mp)`

: Returns the interval`i`

containing the price`mp`

.`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`

).`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`

.

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. |

© Theo Diamandis. Last modified: September 19, 2023. Website built with Franklin.jl and the Julia programming language.