Eta X DEX Expansion: Understanding DODO’s Proactive Market Maker

Jack Lodge
Deeplink Labs
Published in
9 min readMay 5, 2023

· AMM Function: Proactive Market Maker
PMM States and Regression Targets
· Calculating Price and Price Impact
Price Curve Integral
Base Token Shortage
Quote Token Shortage
· Quadratic Equations
Token Balance After Transaction
Regression Targets
· Solving For Returns and Price Impact
· External Data
Price Oracle Proxy
· DODOEX Subgraph
· Code
· Alternate Contract Query Approach
· References

AMM Function: Proactive Market Maker

The Proactive Market Maker (PMM) model aims to provide a more efficient and dynamic market-making mechanism within the DeFi context. The PMM model is designed to react to changes in available information and prioritize liquidity as the most important metric in the market.

PMM is built on the concept of base tokens and quote tokens, as well as four parameters that describe the funding pool of PMM:

  • B₀: base token regression target
  • Q₀: quote token regression target
  • B: base token balance
  • Q: quote token balance

The PMM pricing formula is given by:

Where Pmargin is the actual price at which the PMM offers trades, R is the pricing range factor determining the maximum deviation of the asset price from the base price, and i is the market price provided by an oracle.

R is defined by:

in base token shortages,

in quote token shortages,

in equilibrium.

Where k is the “liquidity parameter” in the range of (0,1). When k=0 the PMM sells or buys at market price, as k increases, DODOEX’s price curve becomes more “curved”, jeopardizing liquidity as funds are placed at prices further away from market price, leaving them under-or-unutilized. As k approaches 1 PMM essentially acts as a standard AMM such as Uniswap’s XYK.

DODOEX Liquidity Parameter and Price Curve

PMM States and Regression Targets

The state of a pool can be classified as one of three possibilities:

  1. Equilibrium
  • Prior to a transaction the pool’s capital is in equilibrium, the regression targets are:
  • B=B₀
  • Q=Q₀
  • R=1
  1. Base Token Shortage
  • When a user buys the base token the pool’s capital is in a shortage of the base token, and the regression targets become:
  • B<B₀
  • Q>Q₀
  • R>1
  • The PMM then aims to sell the quote token to replenish the base token.
  1. Quote Token Shortage
  • When a user buys the quote token the pool’s capital is in a shortage of the quote token, and the regression targets become:
  • B>B₀
  • Q<Q₀
  • R<1
  • The PMM then aims to sell the base token to replenish the quote token.
PMM States

The system is designed to aim for equilibrium, but is constantly taken out of balance by traders swapping tokens in the pool, this is what triggers the PMM to sell the token in excess, bringing it closer in line with the token in deficit.

Calculating Price and Price Impact

“The core of PMM is essentially calculating one integral and solving two quadratic equations.”

Price Curve Integral

“For traders, the most important thing is the average transaction price. The average transaction price is the integral of the marginal price.”

Price Curve Integral

Base Token Shortage

The number of quote tokens to be paid for ΔB = B1 — B2 amount of base tokens is given by:

Where B1 is the balance of base tokens before the transaction, and B2 is the balance of base tokens after the transaction

The average price of a trade that puts the PMM into a shortage of base tokens (i.e. the trader is buying base tokens) is given by:

Quote Token Shortage

The number of base tokens to be paid for ΔQ = Q2 — Q1 base tokens is given by:

Where Q1 is the reserve of quote tokens before the transaction, and Q2 is the amount of quote tokens after the transaction.

The average price of a trade that puts the PMM into a shortage of quote tokens (i.e. the trader is buying quote tokens) is given by:

Quadratic Equations

Token Balance After Transaction

In the case of a quote token shortage, ΔB, Q0, and Q1 (the number of base tokens being bought, the regression target, and the number of quote tokens before the transaction) are known, we must find Q2 (the number of quote tokens after the transaction). We can do this by solving the roots of:

— by rearranging to standard form and identifying them via the quadratic equation (ignoring the negative root):

Similarly, the value for B2 can be obtained by finding the root (ignoring the negative root) of its respective equation:

Regression Targets

When the PMM is not in equilibrium, changes to the oracle price can cause profit or loss for liquidity providers. The regression target represents a balance of base and quote tokens and is influenced by the oracle price. A formula involving the initial reserve values, the oracle price, and other parameters is derived to calculate the regression target at a certain oracle price. Understanding how regression targets are calculated is essential to understand the dynamics of AMMs and their impact on market prices.

We can use a similar quadratic equation approach to identify the roots for the regression targets B0 and Q0:

Solving For Returns and Price Impact

Now that we have equations for calculating every term required for price calculation (and thus price impact calculation), we can generalize our equations to be used in an algorithm.

Fetch the price before a transaction:

Solve the root of Y0 (the regression target of the token being purchased):

Solve the root of Y2 (the balance of the token being purchased after the transaction):

Solve the average transaction price:

Calculate the price impact as:

Calculate the minimum return as:

Where ΔX is the number of tokens being sold, Y1 and Y2 are the reserves of the token being purchased before and after the transaction respectively, k is the liquidity factor, and z is an introduced variable stand-in for i where z=i and z=1/i under base token shortage and quote token shortage respectively.

External Data

Price Oracle Proxy

DODO uses a Chainlink price oracle to fetch the “fair price” i of given assets used in their calculations. The exact price oracle used by DODO is not specified in the documents, however, it is likely they use feeds such as the ETH/USD price oracle in conjunction with a price oracle routing algorithm that uses ETH/USD as a point of reference and calculates routes from token A to ETH to deduce a value.

For this prototype, we can extract i from external APIs. The accuracy of our calculations may depend on how close this data source is to the one used by DODO. We can do this by averaging a few sources such as CoinMarketCap and CoinGecko’s price endpoint. However, too many API queries may slow the system down and there will be a point of diminishing returns in accuracy regarding the number of sources to average.

Alternatively, we can leverage Eta X’ existing order routing techniques to find routes between the given asset and a common asset such as ETH.

DODOEX Subgraph

The following query is sent to the DODOEX subgraph:

{
"pairs": {
"first": "{X}",
"orderBy": "volumeUSD",
"orderDirection": "desc",
"skip": "{skip}",
"where": {
"volumeUSD_lt": "{max_metric}",
"type_not": "VIRTUAL"
}
},
"fields": [
"baseReserve",
"baseToken": {
"decimals",
"id",
"name",
"usdPrice",
"symbol"
},
"i",
"id",
"feeBase",
"feeQuote",
"k",
"lastTradePrice",
"quoteReserve",
"quoteToken": {
"decimals",
"id",
"name",
"usdPrice",
"symbol"
},
"volumeUSD",
"type"
]
}

Some important notes on data points:

isTradeAllowed

  • Trading is sometimes suspended for a given pool, if the pool is in this state, we should ignore it for SOR purposes
  • This can be done in the pool filter stage of SOR

type

  • The DODOEX subgraph provides data on several types of pools including:
  • Virtual
    Virtual pools are not real liquidity pools. They are used to provide a user interface for traders to perform token swaps without directly interacting with the underlying liquidity pool. They simulate the pool’s behavior and serve as a reference for token pricing.
    These can be ignored for SOR purposes.
  • DPP
    DODO Private Pools allow for customizable token swap curves and are designed for professional market makers. In DPP pools, liquidity providers can adjust the price curve by modifying the k value and the range of prices where the curve is flat, allowing for more control over slippage and impermanent loss.
  • DVM
    DODO Vending Machines are a type of liquidity pool designed for new token projects and initial token distributions. DVM pools provide a fixed token swap price with an unlimited token supply, allowing project teams to issue and distribute tokens at a fixed price before transitioning to a more traditional liquidity pool.
  • DSP
    DODO Stake Pool are designed for staking DODO tokens and earning rewards. In a DSP pool, users can stake their DODO tokens to earn rewards from trading fees, mining rewards, and other sources.
  • Classical
    Classical pools use the traditional XYK constant product formula, similar to Uniswap. These pools provide a simple and straightforward way for users to provide liquidity and facilitate token swaps.

volumeUSD

  • This is not a great indicator of a pool’s resistance to slippage (these metrics are used for sorting pools before use in routing), as such, a reserveUSD value is derived and appended to each pool as follows:
    pool[‘reserveUSD’] = float(pool[‘quoteReserve’]) * float(pool[‘quoteToken’][‘usdPrice’]) + float(pool[‘baseReserve’]) * float(pool[‘baseToken’][‘usdPrice’])

Code

from math import sqrt

def regression_target_root(Y_1, k, z, delta_x):
'''
returns the root of the regression target,
i.e. the target balance of the token being put into deficit
$$Y_0=Y_1+Y_1\frac{\sqrt{1+\frac{4kz\Delta X}{Y_1}}-1}{2k}$$
'''
return Y_1 + Y_1 * (sqrt(1 + 4 * k * z * delta_x / Y_1) - 1) / (2 * k)

def balance_root(Y_0, Y_1, k, z, delta_x):
'''
returns the root of the balance,
i.e. the balance of the token being put into deficit
$$Y_2 = \frac{-(\frac{kY_0^2}{Y_1}-Y_1+kY_1-z\Delta X)+\sqrt{(\frac{kY_0^2}{Y_1}-Y_1+kY_1-z\Delta X)^2-4(1-k)(-kY_0^2)}}{2(1-k)}$$
'''
return (-(k * Y_0 ** 2 / Y_1 - Y_1 + k * Y_1 - z * delta_x) + sqrt((k * Y_0 ** 2 / Y_1 - Y_1 + k * Y_1 - z * delta_x) ** 2 - 4 * (1 - k) * (-k * Y_0 ** 2))) / (2 * (1 - k))

def price_after(Y_0, Y_1, k, z, delta_x):
'''
returns the price after the trade is initiated
$$P=\frac{\Delta X}{Y_2 - Y_1}$$
'''
return delta_x / (balance_root(Y_0, Y_1, k, z, delta_x) - Y_1)

Alternate Contract Query Approach

To avoid errors due to subgraph data inaccuracies and to leverage the built-in math of the actual contracts of DODO pools, we can call the contract in question and read the functions:

Classical pools:

  • querySellBaseToken
  • queryBuyBaseToken

Other pools:

  • querySellBase
  • querySellQuote

These return the minimum number of tokens received for a given transaction, which can then be used to calculate price impact as a percentage. This is faster to implement, however making many calls to the contracts will be slower than running our own replica algorithms

References

--

--