###### tags: `zip` `zip-3` `amm` # ZIP-3: Initial Predictions for CPMM :::info Author: Dr. Malte Kliemann Proposal version: 1.0 Protocol version: 0.3.6 ::: ## Introduction When a liquidity pool is deployed in the current iteration of the Zeitgeist app, the prices of all outcome assets are even. For example, when creating a market for a sports event with the typical four outcomes A (A wins), B (B wins), TIE, NONE (no clear result, game/fight cancelled, unforeseen events), all outcomes start at a value of 0.25 ZTG (representing a prediction of 25%). But clearly, 25% of all football games are _not_ cancelled, which means that NONE is heavily overpriced. By buying full sets of outcome tokens, then selling the NONE tokens and keeping the other three, traders can short NONE to correct the mispricing (in terms of predictions, this means they bet that NONE will not occur). A similar scenario is an A/B market with a clear favorite A and underdog B. The favorite and underdog will be priced at 0.50 ZTG. If the typical prediction is that the favorite has a 90% chance of winning, this will allow traders to acquire A tokens at a very cheap price to correct the value of A to 0.90 ZTG. This "price discovery" comes at the cost of the liquidity providers, who bet against the market but are forced to initially sell their tokens at these sometimes completely inappropriate prices. This makes providing liquidity unnecessarily unappealing. The solution is to allow the users who deploy a pool to set the initial prices of the outcome tokens. There are two methods which would allow the user to control at what prices the outcome assets are initially sold. Both methods will be discussed in the next chapter. We will go with the first option (_using weights_) and demonstrate why the second option (_using balances_) is not a good choice. ## Details Recall that the _value function_ of a Balancer pool is $$ V = \prod_{i=0}^n b_i^{\tilde w_i}, $$ where $b_i$ are the current balances and $\tilde w_i$ are the normalized weights of the pool. The product ranges over all assets in the pool. The prices in Balancer pool are set by requiring that $V$ remain constant under trades. Denote by $b_0$ and $w_0$ the balance and (unnormalized) weight of the base asset (which is currently only ZTG) and by $b_i$ and $w_i$ the balance and weight of the outcome tokens. Then the spot price (ignoring swap fees) of the $i^\mathrm{th}$ outcome token in ZTG is $$ s_i = \frac{w_i}{w_0} \frac{b_0}{b_i}. $$ In the current version of the protocol, when a pool is deployed, the following holds: \begin{align*} b_0 &= b_1 = \ldots = b_n, \\ w_1 &= w_2 = \ldots = w_n, \\ w_0 &= \sum_{i=1}^n w_i. \end{align*} It's easy to conclude that $s_i = 1 / n$ for $i = 1, \ldots, n$. The condition that the weight of the base asset be equal to the sum of the weights of the outcome tokens is very important to ensure that the spot prices add up to $1$ when the pool is created. This means we have two ways of controlling the spot price: Let the user determine the weights or the balances. ### Using weights This solution is straightforward: Let the user set the prices by setting weights $w_1, \ldots, w_n$ and then ensure $w_0 = \sum_{i=1}^n w_i$. Lower weights result in lower prices, higher weights result in higher prices. The user still puts the same amount of each outcome tokens in the pool. In detail, we proceed as follows: Let $w_0 > 0$ (in the implementation, $w_0$ will be equal to the `swaps` pallet's `MaxWeight` constant) and let $\tilde s_1, \ldots, \tilde s_n$ be the initial spot prices the liquidity providers wants to offer to the traders. We require that $\sum_{i=1}^n \tilde s_i = 1$ (which is perfectly natural, as predictions should always add up to 100%). Then define $$ w_i = w_0 \cdot \tilde s_i. $$ :::info **Proposition.** A pool with these weights has the prescribed initial spot prices. ::: _Proof._ Note that $\tilde s_i = w_i / w_0$. Using $b_0 = b_1 = \ldots = b_n$, we get $$ s_i = \frac{w_i}{w_0} \frac{b_0}{b_i} = \frac{w_i}{w_0} = \tilde s_i, $$ QED. ### Using balances This solution is a bit more involved. We keep the weights the same as before and let the user control the initial spot prices by placing varying amounts of outcome tokens into the pool upon creation. In detail, we start of with the same weight configuration as before: \begin{align*} w_1 &= w_2 = \ldots = w_n, \\ w_0 &= \sum_{i=1}^n w_i. \end{align*} Let $\tilde s_1, \ldots, \tilde s_n$ be the initial spot prices the liquidity providers wants to offer to the traders, and $B$ the maximum amount of full sets the user is willing to buy (deploying the pool is going to cost at most $2B$ ZTG in total). Let $k$ be the index of the smallest spot price and define $$ b_0 = \tilde s_k \cdot B \cdot \frac{w_0}{w_i}. $$ The define the rest of the balances as $$ b_i = \frac{w_i}{w_0} \frac{b_0}{\tilde s_i}. $$ :::info **Proposition.** - (a) $b_i \leq B$ for all $i$ and $b_k = B$. - (b) A pool with these balances has the prescribed initial spot prices. ::: _Proof._ (a) Recall that $\tilde s_k \leq \tilde s_i$. Thus, we have $$ b_i = \frac{w_i}{w_0} \frac{b_0}{\tilde s_i} = \frac{w_i}{w_0} \frac{w_0}{w_i} \frac{\tilde s_k}{\tilde s_i} B = \frac{\tilde s_k}{\tilde s_i} B \leq B, $$ with equality iff $\tilde s_k = \tilde s_i$. (b) $$ s_i = \frac{w_i}{w_0} \frac{b_0}{b_i} = \frac{w_i}{w_0} \frac{b_0}{\frac{w_i}{w_0} \frac{b_0}{\tilde s_i}} = \tilde s_i, $$ QED. When the user creates the pool, they must own $b_i$ of the $i^\mathrm{th}$ outcome token and $b_0$ ZTG, all of which goes into the pool. In particular, they must own $B$ units of the $k^\mathrm{th}$ outcome token, which means they have to buy $B$ complete sets and retain all outcome tokens which they didn't place in the pool. ### Discussion We study the effects of both options on the typical favorite-underdog scenario. Suppose we have a market with two outcomes, FAV and DOG. The market creator wishes to set the initial prices of 0.9 ZTG and 0.1 ZTG, resp. When using weights, assuming that $w_0 = 128$, we get the following pool: ``` Value: 10000 Total spot price: 1.0 Asset Weight Balance Spot Price -------- -------- --------- ------------ FAV 115.2 10000 0.9 DOG 12.8 10000 0.1 ZTG 128 10000 1 ``` If someone would buy DOG worth 100 ZTG, they would receive 947.13 DOG, bringing the price of DOG to 0.109 (post automatic arbitrage). When using balances instead, we would get the following pool: ``` Value: 2581.9888974716114 Total spot price: 1.0 Asset Weight Balance Spot Price -------- -------- --------- ------------ FAV 1 1111.11 0.9 DOG 1 10000 0.1 ZTG 2 2000 1 ``` Immediately, we notice something very unfortunate: The user spent 12,000 ZTG to get about 1000 ZTG worth of liquidity for FAV. The low price of DOG results in most of the FAV tokens sitting in the liquidity providers wallet without any use to anyone. While in both cases (using weights or balances) the liquidity provider takes a massive risk in case the underdog wins, they can't even hope to sell many of the FAV tokens when using the second method. The fact that using the second most cost 12,000 ZTG (more than half of the price of the first pool) but only have a value of about 2,500 (a quarter of the value of the first pool) is quite telling. Furthermore, the calculation is rather unintuitive and possibly confusing to new users, who might wonder why they are still holding 8889 FAV tokens after the pool is deployed. If someone would buy DOG worth of 100 ZTG, they would receive 929.70 DOG, which is roughly the same as when using weights to set the prices, and drive the price of DOG to 0.113 (post automatic arbitrage). So in both cases the DOG/ZTG market is just about equally thin. That may look concerning, but we have to remember that by setting a spot price configuration as lopsided as 0.9/0.1, the liquidity provider is making something like a 9:1 bet that the favorite wins. ### Notes We ignore swap fees when making all of these calculations for the same reason we ignore swap fees when performing on-chain arbitrage, see ZIP-1. ## Implementation On the chain side, there is no additional work required. The `deploy_swap_pool_for_market` dispatchable and the related functions have a parameter `weights`, which can be used to set the initial weights (within certain limits, see below). ### Limitations and Pain Points - The sum of all weights must be smaller than the `MaxTotalWeights` constant of `swaps`, but each weight must be at least `MinWeight`. In particular, the larger the number of assets, the smaller the possible differences between the weights of different outcome tokens. For example, if `MaxTotalWeight = 128`, `MaxWeight = 64` and `MinWeight = 1`, then the only choice for a pool with $n = 64$ assets is $w_0 = 64$, $w_1 = \ldots = w_n$.