# Weighted AMM

In previous sections, we introduced the fundamental concepts of pools and the vault for pool interaction. This section delves into a unique type of pool, known as a weighted pool, housed within the amm module.

## Weighted Math

Weighted pools utilize a constant product equation:

$\Pi_i B_i^{w_i} = C,$Where $B_i$ and $w_i$ denote the balance and weight of the $i$th token in the pool, respectively.

Our implementations incorporate a notion termed virtual balances. This involves augmenting a token's actual balance with a virtual number for specific use-cases. A typical instance is when a new token is introduced into a pool, we generate a virtual balance for the token to regulate its price. The specifics of each use-case will be detailed in the relevant documentation. For now, we'll include virtual balances in the equation as follows:

$\Pi_i V_i^{w_i} = C,$Here, $V_i$ represents the virtualized balance of token $i$, encompassing both the token's actual balance and virtual amounts.

## Pool Interactions

This section outlines the equations used in pool interactions, such as trades, spot prices, liquidity deposit, and withdrawal.

### Swaps

Let $\phi$ denote the trading fee. Our pool trading formulas, similar to Balancer's, account for virtual balances. Specifically, if the real balances in the pool prior to a trade are $B_1,\ldots,B_n$, the virtual balances are $V_1,\ldots,V_n$, and the weights are $w_1,\ldots,w_n$, the quantity $A_o$ of token $o$ received by the trader when depositing an amount $A_i$ of token $i$ is

$A_o = V_o\left(1-\left(\frac{V_i}{V_i+(1-\phi)A_i}\right)^{\frac{w_i}{w_o}}\right).$The amount $A_i$ of token $i$ required to deposit to obtain an amount $A_o$ of token $o$ is

$A_i = \frac{V_i}{1-\phi}\left(\left(\frac{V_o}{V_o-A_o}\right)^{\frac{w_o}{w_i}}-1\right).$If a trader deposits an amount $A_i$ of token $i$ and receives an amount $A_o$ of token $o$, and $B'_1,B'_2,\ldots,B'_n$ denote the real balances in the pool post-trade, then

$\begin{alignedat}{1} B'_i&=B_i+A_i \\ B'_o&=B_o-A_o \\ \forall_{j\in\{1, 2, ...., n\}-\{i,o\}} B'_j&=B_j \end{alignedat}$as expected.

Note that we must ensure $B'_o>0$ to execute the trade, unless the token is being removed from the pool. In this case, we also accept $B'_o=0$ and subsequently remove the token from the pool.

### Spot Prices

As alluded to earlier, virtual balances are used in lieu of real balances when trading pool assets. Therefore, for all $i,j\in\{1, 2, ..., n\}$, the spot price of $\text{token}_j$ in terms of $\text{token}_i$ is

$\frac{\ \frac{V_i}{w_i}\ }{\frac{V_j}{w_j}}.$**LP token spot price:**
To calculate the LP token's price of the pool in terms of $token_i$, we first determine the pool's total value in this token as follows:

Considering the quantity of LP tokens in circulation as $L$, the LP token's spot price in terms of the token becomes

$\frac{V_i}{Lw_i} \sum_{j} \frac{w_j}{V_j} B_j\ .$### Liquidity Deposit

**Proportional all-asset deposits:** These deposits follow the standard procedure, where a user deposits amounts of each asset in the pool, proportional to the pool's current balances. Specifically, if $B_1,\ldots,B_n$ are the balances in the pool, a user must deposit amounts $A_1,\ldots,A_n$ of each asset that satisfy

Thus, the user will receive $qL$ LP tokens, where $L$ is the number of LP tokens in circulation. If we consider $L$ as a scalar in the virtual balance designs, all virtual balances scale by $q$. It's straightforward to see that the spot prices remain unaltered after this operation.

**Single-asset deposits given LP amount:** Here, the user aims to deposit a single asset (token $i$) and receive a certain LP token amount. We need to determine the token $i$ amount the user must deposit.

Suppose $c$ is the LP token amount the user wishes to receive. Let $B_1,\ldots,B_n$ be the real balances, and $V_1,\ldots,V_n$ are the virtual balances of the pool. Let $\phi$ be the pool fee. To facilitate the single-asset deposit of token $i$, we contemplate several trades of token $i$ against other tokens, followed by a proportional all-asset deposit.

Let $q=\frac{c}{L}$. Note, $q$ is the pool share corresponding to the $c$ LP tokens the user wishes to obtain. For $j\in \{1,...,n\}-\{i\}$, let $A_j$ be the amount the user acquires from the trades and will be used in the proportional all-asset deposit. Since, for each $j\in \{1,...,n\}-\{i\}$, the pool's real balance of token $j$ after trades is $B_j-A_j$, and the user wishes to receive $c$ LP tokens which correspond to a $q$ pool fraction, we deduce that

$A_j = q(B_j-A_j) \ .$Hence,

$A_j = \frac{qB_j}{1+q}$for each $j\in \{1,...,n\}-\{i\}$. Let $A$ denote the quantity of token $i$ a user must trade in the pool to acquire quantities $A_j$ of token $j$, for each $j \in \{1,...,n\}-\{i\}$, excluding transaction fees. This relationship is represented by the following equation:

$(V_i+A)^{w_i} \prod_{\substack{j=1 \\ j\neq i}}^{n} (V_j-A_j)^{w_j} = \prod_{j=1}^{n} V_j^{w_j} \ .$From this, we can derive:

$A = \left(\frac{\prod\limits_{j=1}^{n} V_j^{w_j}}{\prod\limits_{\substack{j=1 \\ j\neq i}}^{n} (V_j-A_j)^{w_j}}\right)^{\frac{1}{w_i}} - V_i \ .$When fees are accounted for, the user must trade an amount $A'$ of token $i$, where $A' = \frac{1}{1-\phi}\cdot A$. The post-trade balance of token $i$ becomes $B_i + A'$. If a proportional all-asset deposit equal to a pool share $q$ is performed after the trades, the user must provide $q(B_i + A')$ of token $i$. Consequently, the total deposit required from the user is $A' + q(B_i + A')$ to obtain $c$ LP tokens through a single-asset deposit of token $i$.

**Non-proportional multi-asset deposits:** In this scenario, the user intends to deposit varying amounts of one or more pool assets, and we must calculate the amount of LP tokens to be issued.

Suppose the real balances of the pool are $B_1,\ldots,B_n$ and the virtual balances are $V_1,\ldots,V_n$. The user's deposit amounts are $A_1,\ldots,A_n$. We will assume a feeless pool for this discussion.

To undertake a proportional all-asset deposit, the user would need to exchange appropriate amounts of some assets to acquire more of others so that the user's post-trade asset amounts are proportional to the pool's real balances. Subsequently, the user can make a proportional all-asset deposit with their assets. Let $q$ represent the user's LP token share for the deposit; hence, the user will receive $qL$ LP tokens.

We will determine the value of $q$, allowing us to perform a single transaction instead of several trades followed by a proportional all-asset deposit. Note that the pool's real balances post-trade and following the all-asset deposit will be $B_1+A_1,\ldots,B_n+A_n$, as the user takes some assets from the pool only to deposit them back shortly after. Consider $V'_1,\ldots,V'_n$ as the virtual balances of the pool post-deposit. Given that the outcome of Balancer's weighted geometric mean formula remains constant with trades and increases in proportion to all-asset deposits, we get:

$\prod_{j=1}^{n} (V'_j)^{w_j} = (1+q)\prod_{j=1}^{n} V_j^{w_j} \ .$Let's denote the value of the parameter $L$ after the deposit as $L'$. For all $j\in \{1,...,n\}-\{i\}$, there is a $z_j$ such that:

$V'_j-B'_j = z_j \cdot L' = z_j \cdot L \cdot (1+q) = (1+q)(V_j-B_j) \ .$This gives us:

$V'_j = B'_j + (1+q)(V_j-B_j) = B_j + A_j + (1+q)(V_j-B_j) \ .$Our goal is to find $q$ that satisfies:

$\prod_{j=1}^{n} (B_j + A_j + (1+q)(V_j-B_j))^{w_j} = (1+q)\prod_{j=1}^{n} V_j^{w_j} \ ,$or equivalently:

$\prod_{j=1}^{n} (B_j + A_j + (1+q)(V_j-B_j))^{w_j} - (1+q)\prod_{j=1}^{n} V_j^{w_j} = 0\ ,$To find the value of $q$, we apply Newton's method. Define $g(x)$ as:

$g(x) = \prod_{j=1}^{n} (B_j + A_j + (1+x)(V_j-B_j))^{w_j} - (1+x)\prod_{j=1}^{n} V_j^{w_j} \ .$For each $j\in\{1, 2, ...., n\}$, let

$h_j(x) = B_j + A_j + (1+x)(V_j-B_j).$Hence,

$g(x) = \prod_{j=1}^{n} (h_j(x))^{w_j} - (1+x)\prod_{j=1}^{n} V_j^{w_j} \ .$Observe:

$\begin{alignedat}{1} g'(x) & = \sum_{l=1}^{n} \left( w_l (h_l(x))^{w_l-1}(V_l-B_l)\prod_{\substack{j=1 \\ j\neq l}}^{n} (h_j(x))^{w_j} \right) - \prod_{j=1}^{n} V_j^{w_j} \\ & = \sum_{l=1}^{n} \left( \frac{w_l (V_l-B_l)}{h_l(x)}\prod_{j=1}^{n} (h_j(x))^{w_j} \right) - \prod_{j=1}^{n} V_j^{w_j} \\ & = \prod_{j=1}^{n} (h_j(x))^{w_j}\sum_{l=1}^{n} \left( \frac{w_l (V_l-B_l)}{h_l(x)} \right) - \prod_{j=1}^{n} V_j^{w_j} \ . \end{alignedat}$We apply Newton's method with:

$x_0 = \prod_{j=1}^{n}\left(\frac{B_i+A_i}{B_i}\right)^w - 1$and define:

$x_{j+1} = x_j - \frac{g(x_j)}{g'(x_j)} \ .$Simulations indicate that 10 iterations of Newton's method suffice in most cases. We've established that this operation entails a swap step, which necessitates a swap fee. To streamline swap fee calculations, we apply the fee to the LP amount using the pool's swap fee ratio. As these estimations hinge on the ratio of assets used in swaps versus those used in proportional join, we first execute the largest possible proportional join. The remaining amounts are then calculated using the equations, and the swap fee is only applied to this portion.

### Liquidity Withdrawals

**Proportional all-asset withdrawals:** These are conducted in the usual manner, where users redeeming a certain quantity of LP tokens receive proportional amounts of all pool assets, corresponding to their pool share. Specifically, if a user redeems an amount $c$ of LP tokens and the pool's real balances are $B_1,\ldots,B_n$, the user will receive, for each $j\in \{1, ..., n\}$, an amount equal to $\frac{c}{L}B_j$ of asset $j$ of the pool. Here, $L$ represents the number of LP tokens in circulation.

As with the all-asset deposit case, we'll now demonstrate that spot prices remain unchanged after an all-asset liquidity withdrawal. Let's assume $B'_1,\ldots,B'_n$ are the pool's real balances post-withdrawal, $V'_1,\ldots,V'_n$ are the virtual balances post-withdrawal, and $L'$ represents the value of parameter $L$ post-withdrawal. Let $q=\frac{c}{L}$. Note that for all $j\in \{1, ..., n\}$,

$B'_j = B_j - A_j = B_j - qB_j = (1-q)B_j$and

$L' = L - qL = (1-q)L \ .$If we use parameter $L$ as a scaler in the virtual balances, they will change proportionally to the real balances and parameter $L$, implying that spot prices remain unchanged after an all-asset liquidity withdrawal.

**Single-asset withdrawals given LP amount:** Here, the user redeems a specific quantity of LP tokens and wishes to receive only token $i$, instead of a proportional amount of all pool assets. We aim to calculate the quantity of token $i$ the user will receive.

Let $c$ represent the quantity of LP tokens the user wishes to redeem. Let $B_1,\ldots,B_n$ be the real balances and $V_1,\ldots,V_n$ the virtual balances of the pool. Let $\phi$ represent the pool fee. To provide the liquidity provider with only token $i$, we'll consider a proportional all-asset withdrawal followed by several trades. For simplicity, we'll apply the trading fee to token $i$. Define $q$ as the pool's share corresponding to $c$ LP tokens to be redeemed, i.e., $q=\frac{c}{L}$. Let $B'_1,\ldots,B'_n$ and $V'_1,\ldots,V'_n$ denote the real and virtual balances of the pool post-liquidity withdrawal, respectively. As per the proportional all-asset withdrawal case, for all $j \in \{1,...,n\}$, we have $V'_j = (1-q)V_j$. Therefore,

$\prod_{j=1}^{n} (V'_j)^{w_j} = (1-q)\prod_{j=1}^{n} V_j^{w_j} \ .$Let $A$ represent the total amount of token $i$ the user will obtain after the corresponding trades, if there were no fees. Let $B''_1,\ldots,B''_n$ and $V''_1,\ldots,V''_n$ denote the real and virtual balances of the pool after the trades. We have

$\prod_{j=1}^{n} (V''_j)^{w_j} = \prod_{j=1}^{n} (V'_j)^{w_j} \ ,$and

$B''_j = \left\{ \begin{array}{cl} B_j & \textnormal{for }j\neq i \\ B_i - A & \textnormal{for } j = i \ .\end{array} \right.$We observe that

$V''_j - B''_j = V'_j - B'_j = (1-q)(V_j - B_j) \ \textnormal{for all } j \in \{0,1,...,n\}.$Hence,

$\begin{alignedat}{1} (1-q)\prod_{j=1}^{n} V_j^{w_j} &= \prod_{j=1}^{n} (V'_j)^{w_j} = \prod_{j=1}^{n} (V''_j)^{w_j} = \prod_{j=1}^{n} (B''_j + V''_j - B''_j )^{w_j} \\ & = \prod_{j=1}^{n} (B''_j + (1-q)(V_j - B_j) )^{w_j} \\ & = (B_i-A+(1-q)(V_i-B_i))^{w_i}\prod_{\substack{j=1 \\ j\neq i}}^{n} (B_j + (1-q)(V_j - B_j) )^{w_j} \ . \end{alignedat}$Therefore,

$A = B_i + (1-q)(V_i-B_i) - \left(\frac{(1-q)\prod\limits_{j=1}^{n} V_j^{w_j}}{\prod\limits_{\substack{j=1 \\ j\neq i}}^{n} (B_j + (1-q)(V_j - B_j) )^{w_j}} \right) ^{\frac{1}{w_i}} \ .$This formula provides the amount a liquidity provider should receive without fees. However, as we charge a fee on token $i$, the user receives

$A' = qB_i + (1-\phi) (A - q B_i) \ .$Here, the trading fee is charged on the amount $A - q B_i$ because the user first receives an amount $qB_i$ of token $i$ with the proportional withdrawal, so this amount should be fee-exempt.

**Non-proportional multi-asset withdrawal:** This operation can be executed using the deposit equations with negative amounts for the given amounts.

### Pool Initialization

To initialize a pool, we receive the balance for each token and mint the appropriate number of liquidity provider (LP) tokens. The number of LP tokens required for initialization is computed as follows:

$LP = invariant \times len(tokens) \\ invariant = \Pi b_i^{w_i}$In the above formula, $tokens$ represents the list of all tokens in the pool, and $b_i, w_i$ denote the initial balance and normalized weight of the $i$'th token, respectively.

## Token Weights

As outlined in the weighted pool equations, each token in the pool should have an assigned weight. Consequently, we store the following data for each token of a weighted pool:

`message WeightedToken {`

uint64 pool_id = 1;

string denom = 2;

string normalized_start_weight = 3 [

(cosmos_proto.scalar) = "cosmos.Dec",

(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",

(gogoproto.nullable) = false

];

string normalized_end_weight = 4 [

(cosmos_proto.scalar) = "cosmos.Dec",

(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",

(gogoproto.nullable) = false

];

}

The `pool_id`

and `denom`

serve as keys to reference a specific token in a particular pool, while the remaining parameters are used to determine the token weights. These weights can change linearly over time, shifting from a `normalized_start_weight`

to a `normalized_end_weight`

within a specified time range per pool. This time range is stored in the following structure:

`message WeightUpdateTiming {`

uint64 pool_id = 1;

int64 start_unix_millis = 2;

int64 end_unix_millis = 3;

}

A pool's owner can request a weight update process based on the timing and target token weights. Weights are then programmed to transition linearly within the given time range from their current value to the target value.

With this model, token weights are always computed on demand using the following methods:

`func (m *WeightUpdateTiming) CalculateProgress(blockTime time.Time) sdk.Dec {`

currentTime := blockTime.UTC().UnixMilli()

// if ended -> progress is one

if m.EndUnixMillis <= currentTime {

return sdk.OneDec()

}

// if not started yet -> progress is zero

if currentTime <= m.StartUnixMillis {

return sdk.ZeroDec()

}

rangeLength := sdk.NewDec(m.EndUnixMillis - m.StartUnixMillis)

elapsed := sdk.NewDec(currentTime - m.StartUnixMillis)

// We don't need to consider zero division here as this is covered above.

return QuoDown(elapsed, rangeLength)

}

func (m *WeightedToken) CalculateNormalizedWeight(changeProgress sdk.Dec) sdk.Dec {

start := m.NormalizedStartWeight

end := m.NormalizedEndWeight

if changeProgress.GTE(sdk.OneDec()) || start.Equal(end) {

return end

}

if changeProgress.IsZero() {

return start

}

if start.GT(end) {

delta := MulDown(changeProgress, start.Sub(end))

return start.Sub(delta)

} else {

delta := MulDown(changeProgress, end.Sub(start))

return start.Add(delta)

}

}

## Introducing Tokens

You cannot add a new token with a zero balance to an existing and initialized weighted pool (a type of balancer pool). To include a new token $U$, we'll use a virtual adjustment balance, which will gradually decrease for the new token. This will stimulate arbitrage opportunities, increasing the real balance of the new token.

Let $w>0$ be the desired pool weight of the new token. Let $w_S$ and $B_S$ denote the weight and balance of token $S$ in the pool, respectively. Let $q_0$ be the lower price bound for the new token $U$ in terms of token $S$. Let $\mathcal{L}(t)$ be the LP supply of the pool at time $t$. We define the initial balance $A$ at introduction time $t_0$ as:

$A=\frac{2 \cdot B_S \cdot w}{w_S \cdot q_0 \cdot(1-w)\cdot \mathcal{L}(t_0)} .$$A$ is only computed at introduction time and remains unchanged thereafter.

The virtual adjustment balance of the new token is given by

$V(t)=\mathcal{L}(t)\left(A-\frac{A}{T} \cdot \min \left\{t-t_0, T\right\}\right), \quad \text { for } t \geq t_0,$where $t_0$ is when the new token is added to the pool, and $T$ is the time over which the virtual adjustment balance decreases to $0$. The time $T$ can be any suitable duration (a week, fifteen days, a month, etc.) that allows a slow decay of the virtual adjustment balance. Note that $V\left(t_0\right)=A\times \mathcal{L}(t)$ and $V(t)=0$ for all $t \geq t_0+T$. Upon introducing a new token $U$, we assign it a weight $w$ and adjust the weights $w_j$ of the existing tokens in the Balancer-type pool as follows:

$w_j \mapsto(1-w) \cdot w_j .$This ensures that the sum of the weights for all tokens in the pool remains equal to 1. Let's denote the weight of token $S$ in the pool after adding token $U$ as $w_S^{\prime}$. Immediately after the introduction of token $U$, the spot price of token $U$ in terms of token $S$ is

$p_U=\frac{\frac{B_S}{w_S^{\prime}}}{\frac{B_U}{w_U}}=\frac{B_S \cdot w_U}{B_U \cdot w_S^{\prime}}=\frac{B_S \cdot w}{\mathcal{L}(t)\cdot A \cdot(1-w) \cdot w_S}=\frac{w_S \cdot q_0 \cdot(1-w) \cdot B_S \cdot w}{2 \cdot B_S \cdot w \cdot(1-w) \cdot w_S}=\frac{q_0}{2},$This price is lower than the actual price of token $U$ in terms of token $S$. As the virtual balance of token $U$ drops to $0$, the spot price of token $U$ will gradually increase. At some point, arbitrageurs will find it profitable to sell token $U$ to the pool, thus enhancing its real balance.

Please note that the sum of the real balance and the virtual balance is used as the token's balance in the pool when executing trades.

For newly introduced tokens, the following record structure is created:

`message VirtualBalancePoolToken {`

uint64 pool_id = 1;

string denom = 2;

string target_virtual_balance = 3 [

(cosmos_proto.scalar) = "cosmos.Dec",

(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",

(gogoproto.nullable) = false

];

int64 start_unix_millis = 4;

int64 end_unix_millis = 5;

}

### Adding YAMM LPT

As explained in the Yield AMM section, our unique pool comprises cASSETs and pASSETs associated with a specific asset. In PRYZM, we endeavor to facilitate the creation of weighted pools that include the LP token from these pools, offering users an easy way to navigate multiple Yield AMMs. While creating such a pool is simple, introducing a new asset to the system and adding its LP to an existing pool requires a token introduction process. This process is explained in detail below.

We've already covered token introduction for weighted pools. However, to introduce new tokens to a weighted pool, we need its price in terms of the tokens already present in the pool. The amm module leverages PRYZM Oracle and price feeders to fulfill this requirement.

The steps to introduce new LP tokens to a nested pool are as follows:

- Governance creates an oracle price pair. This structure informs the feeders on how to compute the price of the underlying asset in terms of a specific quote token in the target weighted pool.

`message Pair {`

string base = 1;

string quote = 2;

string pool_id = 3; //refers to the data source pool (e.g. osmosis gamm pool)

string data_source = 4;

}

// TwapAlgorithm enumerates the valid algorithms for twap_algorithm.

enum TwapAlgorithm {

option (gogoproto.goproto_enum_prefix) = false;

TWAP_ALGORITHM_ARITHMETIC = 0 [(gogoproto.enumvalue_customname) = "ArithmeticTwapAlgorithm"];

TWAP_ALGORITHM_GEOMETRIC = 1 [(gogoproto.enumvalue_customname) = "GeometricTwapAlgorithm"];

}

message OraclePricePair {

string asset_id = 1;

// this is the token denom which should exist in the target weighted pool in pryzm chain

// the reason for adding this property and not using the pairs, is that the token denom in various chains might be different

// for example usdc token might have contract or ibc denom on different chains with different channel and ids

string quote_token = 2;

uint64 twap_duration_millis = 3;

TwapAlgorithm twap_algorithm = 4;

bool disabled = 5;

repeated Pair pairs = 6 [

(gogoproto.castrepeated) = "Pairs",

(gogoproto.nullable) = false

];

// this is the denom of the base token on this chain

// should be ibc denom for most cases

string base_denom = 7;

}

The `OraclePricePair`

message comprises five fields and one repeated field:

`asset_id`

: A string denoting the asset identifier. For instance, to introduce the LP token of Luna YAMM pool, assetId of the Luna asset would be used.`quote_token`

: A string denoting the quote token identifier, which should already exist in the target weighted pool.`twap_duration_millis`

: An integer representing the TWAP calculation duration in milliseconds.`twap_algorithm`

: An enum indicating the TWAP calculation algorithm used by the price feeders.`disabled`

: A boolean indicating whether the oracle price pair is disabled. Disabled price pairs do not get price data from feeders.`pairs`

: A route of pairs, including the pairs of base and quote tokens used to calculate the asset price in terms of the quote token. Each`Pair`

message has three fields:`base`

,`quote`

, and`pool_id`

.`pool_id`

is a string denoting the data source pool identifier (e.g., an Osmosis Gamm pool), and`base`

and`quote`

are strings denoting the base and quote tokens of the pair.

Upon having a price-pair, pool creator (or someone from administration list of the pool) can send a message to introduce the LPT to the target weighted pool. The message should include:

- Target weighted pool ID.
- Yamm pool ID.
- New token weight in the pool. Post-introduction, the LP token will have this weight in the weighted pool.
- Virtual balance interval, which is the time window discussed in the token introduction section. It starts from a computed value and gradually decreases to zero during this window.

We then first check the pool. If it's not initialized, we simply add the token to the pool with zero balance. Otherwise, we initiate the introduction by creating a pending token introduction with the following structure:

`message PendingTokenIntroduction {`

string asset_id = 1;

uint64 target_pool_id = 2;

string token_denom = 3;

string token_normalized_weight = 4 [

(cosmos_proto.scalar) = "cosmos.Dec",

(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",

(gogoproto.nullable) = false

];

int64 virtual_balance_interval_millis = 5;

}

In addition to establishing the pending token introduction, we also need governance to activate the oracle price pair, enabling price feeders to report the asset's price in terms of the specified quote.

- The oracle feeders report the price in the following payload:

`message OraclePayloadDataSourceBlockHeight {`

string data_source = 1;

ibc.core.client.v1.Height block_height = 2 [(gogoproto.nullable) = false];

}

// OraclePayload defines the structure of oracle vote payload

message OraclePayload {

repeated OraclePayloadDataSourceBlockHeight data_source_block_heights = 1 [(gogoproto.nullable) = false];

string price = 2 [

(cosmos_proto.scalar) = "cosmos.Dec",

(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",

(gogoproto.nullable) = false

];

repeated Pair pairs = 3 [

(gogoproto.castrepeated) = "Pairs",

(gogoproto.nullable) = false

];

string quote_token = 4;

}

We calculate the LP token's price lower bound in terms of the specified quote. This is done by first determining a lower bound on the LP price in terms of the asset, then multiplying it by the given asset price.

Suppose we have the asset price in terms of a token in the weighted pool, denoted $S$. To obtain a lower limit for the LP token price ($q$) minted by a YAMM pool in terms of the chosen token $S$, we proceed as follows. Let $B_0$ be the cAsset balance in the YAMM pool, $c$ be the cAsset price in terms of the corresponding Asset, and $p$ be the Asset price (or a suitable lower limit for it) in terms of token $S$. Let $L$ be the existing LP tokens amount of the YAMM pool, and $B$ be the YAMM pool's total value in terms of $S$. Then

$q=\frac{B}{L} \geq \frac{B_0 \cdot c \cdot p}{L},$and we take

$q_0=\frac{B_0 \cdot c \cdot p}{L}$as the lower limit for the equations in the token introduction section. Following the equations in that section, we can add the new token with a virtual balance.

### Adding Base Tokens

We not only plan to have a weighted pool of YAMM LP tokens, but also of the pools' actual underlying assets. This pool facilitates instant batch swaps from the base assets to any other asset. Like the nested pool discussed earlier, we want the capability to add new refractable assets over time, necessitating the base tokens' addition to this weighted pool.

The process of introducing base tokens to a weighted pool is quite similar to that for the LP tokens, except for the price computation segment. As we have oracle feeders reporting the base asset's price, we don't need to contemplate the equations to compute the LP token's price. We can simply use the reported price for token introduction and virtual balance calculations.

## Token Removal

Just as we may need to introduce new tokens into the pool, we may also need to remove tokens. To extract a specific token $U$ from the pool, we will again utilize virtual balances. The token removal process involves two steps.
**First Step:** Let's select an appropriate time period $T$ (such as one week) and gradually escalate the virtual adjustment balance for token $U$. Specifically, if $t_1$ is the initiation time of this step, and $B_U$ represents the balance of token $U$ at time $t_1$, we establish the virtual adjustment balance as follows:

Consequently, $V\left(t_1+T\right)=B_U$, meaning that after the time period $T$, the virtual adjustment balance is equivalent to the balance of token $U$ at the commencement of the first step.

Undoubtedly, this strategy will cause the price of token $U$ to decrease gradually, incentivizing arbitrageurs to purchase token $U$ from the pool. It's crucial that we prohibit traders from selling token $U$ to the pool during this phase, as it could hinder the token's removal.

**Second Step:** As soon as the actual balance of token $U$ in the pool reduces to $0$, we activate a special function to remove this token from the pool. Note that the weights of the remaining pool tokens must be proportionally adjusted to maintain a sum of $1$. Specifically, if $w_U$ is the weight of token $U$, we adjust the weights $w_j$ of the other pool tokens in the following manner when the token removal function is invoked:

This ensures that the sum of the weights of the remaining pool tokens remains $1$.

Similar to the process of introducing tokens, we form an expiring virtual token for these tokens with the following structure:

`message VirtualBalancePoolToken {`

uint64 pool_id = 1;

string denom = 2;

string target_virtual_balance = 3 [

(cosmos_proto.scalar) = "cosmos.Dec",

(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",

(gogoproto.nullable) = false

];

int64 start_unix_millis = 4;

int64 end_unix_millis = 5;

}