Dynamic Function Market Makers

Cover Image for Dynamic Function Market Makers
Colin Roberts
Colin Roberts

Dynamic Function Market Makers

Automated Market Makers (AMMs) have been extensively studied in DeFi and usually appear in the form of Constant Function Market Makers (CFMMs). I will discuss a more general class of AMMs called Dynamic Function Market Makers (DFMMs) for which CFMMs are a special case.

Key idea: CFMMs maintain a constant trading function while DFMMs can allow for the trading function to change.

What's the point?

Before I continue further, I don't want you to feel as if what you are about to read is abstract nonsense. The point of this model of DFMMs is to allow for more flexibility in the design of AMMs and to allow for more complex trading functions that can be tailored to an individual's desire. This particular design also allows for the tokenization of liquidity positions which can be used to create new financial instruments. None of what you are about to read is inherently unknown to the DeFi community, but I think that the DFMM framework presented here is a convenient formalism. Furthermore, making the mechanism design of AMMs rigorous allows for more principled analysis of their properties which in turn makes them easier to test and safer to operate.

With DFMMs, you get a derivative exposure on the tokens inside of the pool. The derivative comes in the form of a specially catered payoff curve or portfolio value function that can adjust over time and due to market state. However, you also get the benefit of being able to trade in and out of the position at any time and you do not need a counterparty. Furthermore, with tokenized LP positions, you can engage in borrowing and lending of liquidity positions which can be used to attain convex exposure. Last but not least, as now popular in DeFi, you can also use DFMMs to have a market-making-style derivative where you can also think about the location of your open two-sided orders (liquidity depth) at a given price.

With this in mind, we can consider the DFMM as a building block for decentralized portfolio management.

Background

There are many resources discussing CFMMs, but I recommend The Geometry of Constant Function Market Makers by Angeris et. al. In essence, a key result of the paper is that a CFMM's trading function is the boundary of a convex set SS of reachable states and there is a unique homogeneous trading function in an equivalence class of such functions.

Notation

We will use the following notation for a DFMM pool:

  • φ\varphi is the trading function.
  • R=(R1,,Rn)R+n\boldsymbol{R} = (R_1, \dots, R_n) \in \R^n_+ are the reserves.
  • xX\boldsymbol{x} \in \mathcal{X} are the pool parameters.
  • LL is the liquidity which has dimensions of tokens.
  • bB\boldsymbol{b} \in \mathcal{B} are the blockchain state. For instance, this could be block number and block timestamp or even functions applied to the previous state of the pool.
  • q=(R,L,x,b)Q=R+n×R+×X×B\boldsymbol{q} = (\boldsymbol{R}, L, \boldsymbol{x}, \boldsymbol{b}) \in \mathcal{Q} = \R^n_+ \times \R_+ \times \mathcal{X} \times \mathcal{B} is the pool state.
  • R+\ell \in \R^+ is the share balance of a pool or user.
  • ER+E \in \R^+ is the share redemption rate.
  • PP is the quoted price of the pool.
  • γ\gamma is the fee parameter, i.e., a 30bps fee yields γ=0.997\gamma=0.997.

Note that I will later use SS for price so I apologize for the mix of notation but the context should be clear.

Rework

Let's rework the CFMM case just slightly to prepare for the DFMM case. In the referenced work, we have φ ⁣:R+nR\varphi \colon \R^n_+ \to \R where the choice of image kRk \in \R defines the boundary of a reachable set. Instead, consider φ ⁣:R+n×R+R\varphi \colon \R^n_+ \times \R_+ \to \R where the second input is a liquidity parameter LL. To define the boundary of a reachable set, we pick LR+L \in \R_+ and consider kerφ(_,L)\ker \varphi(\_, L). That is, the set of reserves where φ(_,L)=0\varphi(\_,L) =0 (also called the kernel) where _\_ signifies an open input, i.e., φ(_,L) ⁣:R+nR\varphi(\_,L) \colon \R^n_+ \to \R.

Moving forward, we will consider the kernel of φ ⁣:QR\varphi \colon \mathcal{Q} \to \R for which the CFMM is a special case where pool parameters are fixed, there is no influence from the blockchain state. Prices of tokens in the pool can still be defined as a map P ⁣:QR+P \colon \mathcal{Q} \to \R^+.

DFMMs

Picture a DFMM as a parameterization by q\boldsymbol{q} of reachable sets in the set of all possible reachable sets S\mathcal{S}. Naturally, the oracle-free parameter choices are those listed above as Q\mathcal{Q}. For posterity, Q\mathcal{Q} can be extended to be any parameter space that is appropriate for your application at hand and could include external data feeds such as market prices or volatility.

Scale invariance

We will work with an equivalent condition of φ\varphi being homogeneous (as described in Angeris et. al.) in reserves as we have found this be more computationally viable. Namely, consider functions for which the reserves R\boldsymbol{R} such that φ(R,L,x,b)=0\varphi(\boldsymbol{R}, L, \boldsymbol{x}, \boldsymbol{b})=0 defines a hypersurface in Rn\R^n for which LL is solely a scaling factor. That is, if R\boldsymbol{R} is a solution to

φ(R,L,x,b)=0\varphi(\boldsymbol{R}, L, \boldsymbol{x}, \boldsymbol{b})=0

then if we scale R\boldsymbol{R} by λ\lambda, we have that

φ(λR,L,x,b)=0\varphi(\lambda\boldsymbol{R}, L', \boldsymbol{x}, \boldsymbol{b})=0

for some LL' so long as λ>0\lambda>0. This condition itself is weaker than homogeneity as we are not requiring that L=λLL'=\lambda L. In the case that L=λLL'=\lambda L, we say that φ\varphi is scale invariant in the reserves and we can see this is equivalent to homogeneity of degree 11 in the reserves. To prove this, consider φ~(R)L\tilde{\varphi}(\boldsymbol{R}) - L where φ~(_)=φ(_,L)\tilde{\varphi}(\_) = \varphi(\_,L). If φ~\tilde{\varphi} is homogeneous, then

Rkerφ~(λR)L    Rkerλφ~(R)(λL+ϵL)    Rkerλ(φ~(R)L)ϵL.\boldsymbol{R} \in \ker\tilde{\varphi}(\lambda \boldsymbol{R}) - L' \iff R \in \ker \lambda \tilde{\varphi}(\boldsymbol{R}) - (\lambda L+\epsilon_L) \iff R\in \ker \lambda \left(\tilde{\varphi}(\boldsymbol{R}) - L\right) - \epsilon_L.

This must only be true if ϵL=0\epsilon_L=0 so by definition of φ~\tilde{\varphi} we have the result.

Parameters

In a different scenario where the pool parameters and blockchain state changes: xx\boldsymbol{x} \mapsto \boldsymbol{x}' and bb\boldsymbol{b} \mapsto \boldsymbol{b}', we can determine a new value of LL' such that

φ(R,L,x,b)=0.\begin{equation} \varphi(\boldsymbol{R}, L', \boldsymbol{x}', \boldsymbol{b}')=0. \end{equation}

This is to say that changing parameters while leaving reserves invariant may cause a change in liquidity parameter LL. We have examples of this in Dollar cost averaging, Covered call below, and Liquidity tracker strategy.

DFMMs as state machines

Allow us now to extend the state of the pool to include a share balance \ell and a share redemption rate EE. Later on, this will let us tokenize liquidity positions. By abuse of notation, we will now think of

Q=R+nreserves×R+liquidity×Xpool parameters×Bblockchain state×R+liquidity balance×R+share redemption rate\mathcal{Q} = \underbrace{\R^n_+}_{\textrm{reserves}} \times \underbrace{\R_+}_{\textrm{liquidity}} \times \underbrace{\mathcal{X}}_{\textrm{pool parameters}} \times \underbrace{\mathcal{B}}_{\textrm{blockchain state}} \times \underbrace{\R^+}_{\textrm{liquidity balance}} \times \underbrace{\R^+}_{\textrm{share redemption rate}}

If you'd like, you can place \ell and EE into B\mathcal{B} as B\mathcal{B} is a bit of a catch-all for any and all blockchain state.

Let qmQ\boldsymbol{q}_m \in \mathcal{Q} be the current state of the DFMM at step mm (including our \ell and EE as above), then the DFMM can be thought of as a state machine that takes in current qm\boldsymbol{q}_m and the next state qm+1\boldsymbol{q}_{m+1} and produces \top if and only if the pair (qm,qm+1)(\boldsymbol{q}_m, \boldsymbol{q}_{m+1}) is valid and the machine continues the state transition and otherwise produces \bot (invalid) and rejects the state transition, halting progress. We use the machine's built in map ASSERT ⁣:{true,false}{,}\texttt{ASSERT} \colon \{\texttt{true}, \texttt{false}\} \to \{\top, \bot\} where true\texttt{true} \mapsto \top and false\texttt{false} \mapsto \bot.

The DFMM has two possible instructions each of which carry a payload of two states:

  • SWAP ⁣:Q×Q{,}\texttt{SWAP}\colon \mathcal{Q} \times \mathcal{Q} \to \{\top, \bot\}
  • CHANGE_ALLOCATION ⁣:Q×Q{,}\texttt{CHANGE\_ALLOCATION}\colon \mathcal{Q} \times \mathcal{Q} \to \{\top, \bot\}

Definitions.

  • (SWAP\texttt{SWAP}) We say that a SWAP\texttt{SWAP} instruction is valid if and only if the new state is in the kernel of the extended trading function φ\varphi and if the fee yields at least Lm+1L_{m+1}. Concretely, assuming we can determine LmL_m' a-la Equation (1) and the tokens we tender are in the list TT, we get the expression:
SWAP(qm,qm+1)=ASSERT((φ(qm+1)=0)(Lm+1Lm(1+(1γ)iTΔiRi))).\begin{equation} \texttt{SWAP}(\boldsymbol{q}_m, \boldsymbol{q}_{m+1}) = \texttt{ASSERT}\left((\varphi(\boldsymbol{q}_{m+1})=0) \wedge \left(L_{m+1} \geq L_m' \left(1 + (1-\gamma) \sum_{i \in T}\frac{\Delta_i}{R_i} \right)\right)\right). \end{equation}
  • (CHANGE_ALLOCATION\texttt{CHANGE\_ALLOCATION}) We say that a CHANGE_ALLOCATION\texttt{CHANGE\_ALLOCATION} instruction is valid if φ(λR,Lm+1,xm,bm)=0\varphi(\lambda \boldsymbol{R}, L_{m+1}, \boldsymbol{x}_m, \boldsymbol{b}_m) = 0 and if the LPTs are credited at the share redemption rate EE. If so, then the LP is credited \ell LPTs upon allocation, and debited \ell LPTs upon deallocation (hence R\ell \in \R and not R+\R^+). Specifically:
CHANGE_ALLOCATION(qm,qm+1)=ASSERT((Rm+1=λRm)(φ(qm+1)=0)(=Lm+1LmE)).\begin{equation} \texttt{CHANGE\_ALLOCATION}(\boldsymbol{q}_m, \boldsymbol{q}_{m+1}) = \texttt{ASSERT}\left( (\boldsymbol{R}_{m+1} = \lambda \boldsymbol{R}_m) \wedge (\varphi(\boldsymbol{q}_{m+1})=0) \wedge \left(\ell = \frac{L_{m+1} - L_m}{E} \right)\right). \end{equation}

For the SWAP\texttt{SWAP} definition, I should say that determining the LL' from the parameter updates is not always possible analytically and may require a numerical solution such as bisection search.

For the CHANGE_ALLOCATION\texttt{CHANGE\_ALLOCATION}, we are implicitly assuming pool parameter nor blockchain state values are changing within this instruction, though allowing for this is possible and would be a straightforward modification. Second, in the case the trading function is scale invariant, we get Lm+1=λLmL_{m+1} = \lambda L_m. Third, it follows that the quoted price Pm+1P_{m+1} is equivalent to the current quoted price PmP_m. The fact that price is invariant under CHANGE_ALLOCATION\texttt{CHANGE\_ALLOCATION} is under the assumption that only SWAP\texttt{SWAP} should lead to a change in quoted price from an AMM.

Let us illustrate these concepts with some examples.

Examples

We will provide two examples of CFMMs and their DFMM counterparts. First is the Geometric Mean Market Maker (GMMM) and the second is the Log Normal Market Maker (LNMM).

Geometric Mean Market Maker

The GMMM is a CFMM that maintains the following invariant:

φ(R,L,w)=i=1nRiLwi1\varphi(\boldsymbol{R},L,\boldsymbol{w}) = \prod_{i=1}^n \frac{R_i}{L}^{w_i} - 1

where wiw_i are the weights (i.e., wiw_i is the portfolio weight of token ii), and LL is the liquidity parameter which we can see has dimensions of tokens. In our notation before, x=w\boldsymbol{x} = \boldsymbol{w} and b=\boldsymbol{b} = \emptyset. We also require that wi[0,1]w_i \in [0,1] and i=1nwi=1\sum_{i=1}^n w_i = 1. Finally, the fee parameter γ\gamma is constant, and I dropped it from the notation of the pool state q\boldsymbol{q} for this example.

This trading function φ(R,L,w)\varphi(\boldsymbol{R},L,\boldsymbol{w}) is scale invariant. To see this, note

φ(λR,λL,w)=φ(R,L,w).\varphi(\lambda \boldsymbol{R}, \lambda L, \boldsymbol{w}) = \varphi(\boldsymbol{R}, L,\boldsymbol{w}).

Remark. You could consider φ~(R,L)=i=1nRiwiL\tilde{\varphi}(\boldsymbol{R},L) =\prod_{i=1}^n R_i^{w_i} - L to be the trading function of the GMMM as the kernel is the same set and the kernel is all we need to definee when an instruction is valid. However, computationally it is better to work with φ\varphi as it is scale invariant in the reserves and liquidity parameter to be near 1 as opposed to LL. This matters for stability of numerical approximations.

Since this CFMM is scale invariant, if we change the reserves to λR\lambda \boldsymbol{R} via CHANGE_ALLOCATION\texttt{CHANGE\_ALLOCATION}, then this is validated if Lm+1=λLmL_{m+1} = \lambda L_m.

As a DFMM

The DFMM counterpart allows for the weights be arbitrary functions of the additional parameters b\boldsymbol{b}. We will denote the varying weights by wi(b)w_i(\boldsymbol{b}) where b\boldsymbol{b} is a choice of blockchain state, i.e., we could let:

b=t,\boldsymbol{b} = t,

where tt is the block timestamp. Hence the state of our example DFMM is:

qt=(R1,,Rn,L,w1,,wn,t,,E).\boldsymbol{q}_t = \left(R_1, \dots, R_n, L, w_1, \dots, w_n,t, \ell, E\right).

Dollar Cost Averaging

For simplicity, consider a pool with two tokens XX and YY with weights solely dependent on time. That is, we have wX(t)=w(t)w_X(t) = w(t) and wY(t)=1w(t)w_Y(t) = 1-w(t) respectively. Then the DFMM trading function is:

φ(RX,RY,L,t)=RXL(t)w(t)RXL(t)1w(t)1.\varphi(R_X,R_Y,L,t) = \frac{R_X}{L(t)}^{w(t)} \frac{R_X}{L(t)}^{1-w(t)} - 1.

where L(t)L(t) is the liquidity over time. As a reminder, since w(t)w(t) is dynamic and reserves may not change from one step to the next, L(t)L(t) will change over time along with w(t)w(t) based on our discussion in Parameters.

For sake of concreteness, we can let t[0,1]t \in [0,1] and take w(t)=tw(t) = t so that the weights are linearly interpolated between XX and YY. The pool will start out with 00 weight on XX and 11 weight on YY and end with 11 weight on XX and 00 weight on YY. This can be thought of as a means of dollar cost averaging from YY into XX over time tt.

Liquidity Update

Suppose that we want to update the liquidity L(t)L(t) of the pool at time tt to L(t)L(t') where t>tt' > t. Then we can solve for L(t)L(t') such that φ(RX,RY,L(t),t)=0\varphi(R_X,R_Y,L(t'),t')=0. Quickly:

φ(RX,RY,L,t)=0    L(t)=RXw(t)RY1w(t).\varphi(R_X,R_Y,L',t') = 0 \implies L(t') = R_X^{w(t')} R_Y^{1-w(t')}.
Visualization

Let's imagine now that we want to maintain the reserves RXR_X and RYR_Y constant over time and we want to update the liquidity L(t)L(t) over time so that the reserves maintain on the graph of the trading function. For instance, if we have w=0.2w=0.2 and L=1L=1, then we can take RX=1.5R_X = 1.5, RY=0.9036R_Y \approx = 0.9036 to be a point on the graph of the trading function. Now, we can update ww to be 0.50.5 for which we would find L1.1642L\approx 1.1642 and if we updated again to w=0.8w=0.8, we would find L1.3554L\approx 1.3554. See the figure below for a visualization of this process. GMMM Trading Function Updates

Volatility Targeting

Suppose that we want to maintain a constant volatility of the pool over time. Then we can define the volatility of the pool as the standard deviation of the price of the pool over time and if we know the volatilities of each token in the pool, we can pick a set of weights w\boldsymbol{w} that will allow for the pool to attempt to maintain a constant volatility. This is certainly not a new idea as Aera provides this using Balancer and decentralized parameter submitters.

Log Normal Market Maker

The LNMM is a CFMM that maintains the following invariant:

φ(q)=Φ1(RXL)+Φ1(RXKL)+στ\varphi(\boldsymbol{q}) = \Phi^{-1}\left( \frac{R_X}{L} \right) + \Phi^{-1}\left( \frac{R_X}{K L} \right) + \sigma \sqrt{\tau}

where q=(RX,RX,L,K,σ,τ,γ)\boldsymbol{q} = (R_X, R_X, L, K, \sigma, \tau, \gamma), Φ1\Phi^{-1} is the inverse of the cumulative distribution function of the standard normal distribution, KK is the mean price of Token X with respect to Y, σ\sigma is the width parameter, τ\tau is the time parameter, and γ\gamma is the fee parameter. Note that this φ\varphi is also scale invariant in the reserves and liquidity parameter. Once again, we assume a constant γ\gamma and dropped it from this notation.

Why do we call this the Log Normal Market Maker? We call it this because if we look at the liquidity distribution of the pool, we see that it is log normal i.e., normally distributed in the log space: LNMM Liquidity Distribution

We can see how the trading function behaves as parameters change while reserves remain constant below. In essence, when parameters change, the resulting curve may not pass through our current reserves, but we are free to rescale the curve using LL as a scale parameter. It is necessarily the case that the curve will pass through the reserves for some value of LR+L'\in \R^+. The proof is in the picture by taking advantage of the scale invariance of the curve! LNMM Trading Function Updates

Covered Call

Let's consider the DFMM counterpart of the LNMM that adapts the parameters over time. Suppose that we let τ=Tt\tau = T-t where TT is the time to expiry of the pool. Then the DFMM invariant is:

φ(RX,RX,t)=Φ1(RXL(t))+Φ1(RXL(t))+σTt\varphi(R_X,R_X,t) = \Phi^{-1}\left( \frac{R_X}{L(t)} \right) + \Phi^{-1}\left( \frac{R_X}{L(t)} \right) + \sigma \sqrt{T-t}

Now, how does, or should, L(t)L(t) change over time?

First, let's plot the value of a position with respect to price SS with liquidity L(t)L(t) at time tt:

V(S,t)=L(t)(S(1Φ(lnSK+12σTt))+KΦ(lnSK12σTt))V(S,t) = L(t)\left( S\cdot\left(1-\Phi\left(\ln\frac{S}{K}+\frac{1}{2}\sigma\sqrt{T-t}\right)\right) + K\cdot \Phi\left(\ln\frac{S}{K}-\frac{1}{2}\sigma \sqrt{T-t}\right)\right)

Note that if L(t)=LL(t)=L then V(S,t)V(S,t) the value of the Black-Scholes Covered Call strategy.

In order for L(t)L(t) to be constant while TtT-t decreases, we actually have to effectively increase the reserves RXR_X and RYR_Y. This is only possible if we apply a fee upon the SWAP\texttt{SWAP} instruction. Suppose that a user wishes to call SWAP\texttt{SWAP} with a new state qt\boldsymbol{q}_{t'}, we can define that if we have LtL_t that

δL=L(1γ)ΔRXRY\begin{equation} \delta_L = L(1-\gamma)\frac{\Delta_{R_X}}{R_Y} \end{equation}

if the user was swapping in token XX. This is what we specified in Equation (2) for the SWAP\texttt{SWAP} instruction. Also, note that I now parameterized q\boldsymbol{q} using tt and tt' instead of mm and m+1m+1 for convenience due to the explicit dependence on time in this mechanism.

From this, we can define the trading rule for a valid SWAP\texttt{SWAP} of the Replicating Portfolio for the Covered Call strategy:

  1. Update the parameter τ=Tt\tau = T-t' for the new time tt', hold reserves constant, and solve to get a new liquidity LL' such that φ(RX,RY,L,t)=0\varphi(R_X,R_Y,L',t')=0.
  2. Assert that for qt=(RX+δX,RY+δY,L+δL,t)\boldsymbol{q}_{t'} = (R_X+\delta_X, R_Y+\delta_Y,L' + \delta_L, t') satisfies L+δLLL' + \delta_L \geq L Given (1) and (2), we can see that V(S,t)V(S, t) achieves the payoff of at least the Black-Scholes Covered Call strategy. Keep in mind this δL\delta_L comes from Equation (4) above and depends on the fee and trade size.

This extra need to accrue liquidity over time as it is diminished by tt approaching TT is equivalent to the positive theta of the Covered Call strategy. Supposing that L(T)>L(0)L(T) > L(0) and no allocations or deallocations were made, then this contributes extra value to the pool that can be thought of as akin to earning a portion of the options premium on the Covered Call over time.

Remark. It is likely not worth enforcing this strict trading rule above as it can lead to the pool being driven into a "dead" state. If it took too long for swaps to occur on the pool, then the pool is effectively behind in its liquidity accrual and thus you require a larger and larger fee revenue on a single swap to get past this gap the longer you wait. Instead, it's best to just ignore this condition and let the pool accrue liquidity as it can.

Liquidity Tracker Strategy

For a second example using LNMM, we can consider a strategy that tracks the liquidity distribution of the pool over time to the emergent token behavior. Current price is intrinsic to the LNMM (and so it falls into block state) and from this we can build a DFMM model that moves and scales the liquidity distribution of the LNMM to track the current price and volatility of the pool. Further, this strategy could employ a dynamic fee parameter γ\gamma to track the volatility of the pool, so we include this explicitly now in q\boldsymbol{q}.

Take the LNMM with constant τ=1\tau=1 and take K0K_0 and σ0\sigma_0 to be initially chosen parameters for the pool. Then, we can define the state of the DFMM via q=(RX,RY,L,K,σ,P,γ)\boldsymbol{q} = (R_X,R_Y,L,K,\sigma,P,\gamma). We assert additional conditions on SWAP\texttt{SWAP}:

  • Pm=KmP_m=K_m for all mm (including m=0m=0).
  • σm+1=Pm+1PmPm(tm+1tm)\sigma_{m+1} = \frac{|P_{m+1} - P_m|}{P_{m}(t_{m+1}-t_m)} where σm+1\sigma_{m+1} will have volatility in the same units of time as tmt_m which is the block timestamp at step mm. This must be picked for m=0m=0 by hand. Further, σ\sigma could be averaged out over a history of price changes if desired.
  • γm+1=1exp(12λPm+1Pm)\gamma_{m+1} = 1-\exp(\frac{1}{2}\lambda |P_{m+1}-P_m|) so that the fee increases for larger price impact trades and the rate of increase is based on a choice of λ\lambda. See exponential distribution.

Remark. This choice of fee parameter was chosen somewhat arbitrarily, but it has the effect of disuading swaps from occuring in periods of high volatility and compensating LPs more for the risk of high volatility. Perhaps it is somewhat like a soft circuit breaker.

We can see what an example of this would look like. If we had a market consistently increasing in volatility while the price also increased, our liquidity distribution would adjust like so: LNMM Liquidity Tracker This specific case increased both KK and σ\sigma as time moved on. If you wanted to, you could set a parameter range like this for shorter term liquidity management if you expect price to increase, but assume there is random motion in the market (i.e., this matches a drift-diffusion model).

Tokenization

I mentioned previously that this DFMM framework allows for the tokenization of liquidity positions in a pool and we materialized this a bit in the CHANGE_ALLOCATION\texttt{CHANGE\_ALLOCATION} instruction (see Equation (3)). With our framework, DFMM Liquidity Provider Tokens (LPTs) are fungible and can be traded on secondary markets, including placed into other DFMMs, or lent out as collateral, or used as a margin balance. When calling CHANGE_ALLOCATION\texttt{CHANGE\_ALLOCATION}, the user must specify the amount of liquidity they wish to add or remove δL\delta_L from the pool as the pool need to handle the reserves and pass the state transition. As worked out before, the amount of reserves added or removed will scale accordingly.

An LP, Alice, will maintain a balance of shares Alice\ell_{\textrm{Alice}} (credited to them on allocation) that represent their claim on the pool's liquidity LL. The pool itself also tracks an Pool\ell_{\textrm{Pool}} for how many shares (or LPTs) have been created for the pool. Therefore if LL changes there must be a change in the redemption rate EE that is used to convert the pools share balance Pool\ell_{\textrm{Pool}} to LL in order to properly credit the LP such as Alice with the correct amount of underlying tokens upon deallocation.

The process

Upon pool initialization, we can make the assumption that the initial liquidity L0L_0 is the same as the initial share balance of the pool Pool,0\ell_{\textrm{Pool},0} and therefore the initial liquidity redemption rate is E0=1E_0=1. If Alice is the LP who allocated this L0L_0 is given back

Alice=Pool,0=L0\ell_{\textrm{Alice}} = \ell_{\textrm{Pool},0} = L_0

LPTs.

  • Parameter change: If the parameters of the pool change, then we note that LL must change as well. Let's assume that the parameter change occurs in the first step, we have L0L1L_0 \mapsto L_1 and therefore the redemption rate E0E1=L1L0E_0 \mapsto E_1 = \frac{L_1}{L_0}. This means that Pool,0E1=L1\ell_{\textrm{Pool}, 0} \cdot E_1 = L_1. Of course, Alice can use her Alice=Pool,0\ell_{\textrm{Alice}} = \ell_{\textrm{Pool},0} LPTs to make claim on the whole pool, i.e., the new liquidity L1L_1.
  • Swap: If a swap instruction occurs, then if a fee is applied on the swap, then LL will change again. Assume that this happens at the first step, then we again just have L0L1L_0 \mapsto L_1 and E0E1=L1L0E_0 \mapsto E_1 = \frac{L_1}{L_0}.

The two cases above resolve to the same problem in tracking change of liquidity that isn't due to an allocation. This is done by keeping an redemption rate EE between LPTs \ell and pool liquidity LL.

  • Liquidity (de)allocation: In the case that liquidity is allocated at step mm to m+1m+1, we will know that EmE_m is the redemption rate. Suppose a new user, Bob, creates a CHANGE_ALLOCATION\texttt{CHANGE\_ALLOCATION} instruction with a δL=Lm+1Lm\delta_L = L_{m+1} - L_{m}, then there is a credit/debit of Bob=δLEm\ell_{\textrm{Bob}} = \frac{\delta_L}{E_m} LPTs to the user's account. The total share balance of the pool is Pool,m+1=Alice+Bob\ell_{\textrm{Pool}, m+1} = \ell_{\textrm{Alice}} + \ell_{\textrm{Bob}}.

Tokenization of DFMM positions provides a new way to compose DeFi tools together and create new products. This design space is quite open and I am excited to see what people come up with.

Wrapping up

This post was meant to be a formal overview of DFMMs and I hope that it has been helpful. If you would like to see implementation details of DFMMs, please see the DFMM-based contracts in the Excalibur repository. Throughout the repo, you can find more details on these two examples as well a Arbiter simulations that are being completed. We plan to rigorously test our implementations and once we are confident in their safety, we will have them deployed. Stay tuned to see some specific instruments we build using DFMMs as primitives. Please get in touch by joining our Discord server if you have any questions or would like to build on top of the DFMM framework.