# MIP 9: Feeder Pools

Author | Alex Scott, Onur Solmaz |
---|---|

Discussions-To | https://forum.mstable.org/t/mip-9-feeder-pools/410 |

Status | Implemented |

Created | 2021-03-15 |

## Simple Summary

Following the deployment of MIP-7, it’s possible to extend the mAssets by creating “Feeder pools” composed of 50% fAsset and 50% mAsset. These Feeder Pools (fPools) provide important benefits to mAssets by:

- Free
`fAsset`

->`mAsset`

swaps (effectively allowing users to ‘mint’ mAssets with any fAsset) - Leveraging the
`mAsset`

SAVE rate by providing a source of demand for mAssets from within mStable (thus increasing mAsset SAVE APY) - Feed swap fees back into the
`mAsset`

by supporting trades between fAsset <-> mAsset bAssets

This MIP outlines the implementation details for fPools and proposes that mStable actively deploy these to stimulate growth and mAsset utility.

## Abstract

fPools maintain the bulk of functionality from mAssets:

- Same interface for Mint/Swap/Redeem
- Protected through max weights
- Ability to deploy fAsset/mAsset onto third party lending platforms to generate yield
- Governed by vMTA holders
- Ability to enable a governance fee, which extracts a % of total pool revenue

There are a number of core differences between fPools and mAsset pools

- fPools support mint/swap/redeem using assets within the mAsset basket in addition to it’s own basket
- fPools produce LP tokens that increase in value, rather than inflate supply
- Swaps into mAsset do not apply a fee
- fPools are always composed of 50/50 fAsset/mAsset, and use an invariant derived specifically for 2 assets
- fPools are not protected by MTA in the event of an underlying asset losing it’s peg

Incentivising liquidity on these pools will provide on/off ramps, generate swap volume and leverage mAssets SAVE utilisation rate - thus making them ideal options for MTA rewards. Additionally, it provides another place for projects to incentivise their own asset liquidity and benefit from MTA token emission.

## Specification

### Overview

For feeder pools, we introduce a new invariant for 2-asset stablecoin AMMs that shows similar properties as Stableswap, but can be solved with less number of operations. The implicit invariant equation follows as

\[ \frac{Ak}{x + y} + \frac{k^2}{4xy} = A + 1\,. \]

Here, \(x\) and \(y\) are asset reserves, \(k\) is the invariant and \(A\) is an amplification coefficient. The equation is quadratic in terms of \(k\), making it easier to compute the invariant value. This invariant results in a bonding curve that is very similar to Stableswap, when the amplification coefficient is 4 times that of Stableswap’s.

Note that the `A`

parameter used in existing Stableswap contracts actually corresponds to \(An^{n-1}\) —we consider the original definition of \(A\) from the Stableswap invariant.

By contributing liquidity to a Feeder Pool, the user will receive:

- Swap fees into the bAsset specific to that feeder basket
- Interest from lending out Feeder basket bAssets, including the mAsset trading pair
- Possible liquidity rewards from project’s that wish to incentivise their stablecoin on mStable
- MTA rewards

### Rationale

fPools are not protected by MTA in the event of an underlying asset losing it’s peg

Feeder pools “feed” value into mAssets and the wider mStable system, thus they should not receive protection from MTA. In fact, in the future it may be proposed that all feeder pools can be converted into insurance pools, from which mAssets can be protected. This would involve using the feeder pool assets as a backup of last resort in the event of catastrophic failure in the mAsset pool.

Value accruing LP token vs inflation

In order to reduce the steps needed to become an active LP, it is decided that the feeder pools will use the standard value accruing LP token.

Router for cross pool swaps vs localised

Swapping from the fPool to the mAsset pool (e.g. fAsset -> mpAsset) requires 2 steps. The complexity increases massively for multiMint and multiRedeem,
and so it was decided that mint/swap/redeem would be able to communicate cross pool, and a seperate `FeederRouter`

would be developed in order to
facilitate more complex trade paths (e.g. mintMulti and cross-feeder pool swaps).

Swaps into mAsset do not apply a fee

In order to maximise utility for mAssets, it is decided that trades from fAsset to mAsset will have 0 fee. This trade path can be presented to the end
user as `minting`

an mAsset.

fPools are always composed of 50/50 fAsset/mAsset, and use an invariant derived specifically for 2 assets

It makes sense to optimise the invariant for 2 dimensions rather than use an iterative solution for an `n`

dimensional surface.

## Technical Specification

### Computing the invariant for given reserve values

The invariant equation is quadratic in terms of \(k\), and is solved as

\[ k(x, y) = 2 \left(\sqrt{c_1^2 + (A+1)xy} - c_1\right) \]

where \(c_1 = Axy/(x+y)\).

Below is a pseudocode for computing \(k\):

```
def compute_k(x: int, y: int, A: int):
prod = x * y
c1 = A * prod // (x + y)
result = 2 * (sqrt(c1**2 + (A + 1) * prod) - c1)
return result
```

where `sqrt()`

is an optimized integer square root function.

### Computing a reserve given other reserve and invariant

Reserve value \(y\) is computed similarly, by solving the invariant equation for \(y\):

\[ y(x, k) = \frac{1}{2}\left(\sqrt{c_3^2+c_2} + c_3\right) \]

where

\[c_2=\frac{k^2}{A+1} \quad\text{and}\quad c_3=\frac{c_2}{4x} + \frac{Ak}{A+1} - x.\]

Below is a pseudocode for computing \(y\):

```
def compute_y(x: int, k: int, A: int):
Aplus1 = A + 1
c2 = k**2 // Aplus1
# Account for negative c_3
dummy1 = c2 // (4 * x) + k * A // Aplus1
if dummy1 >= x:
dummy2 = dummy1 - x
else:
dummy2 = x - dummy1
result = (sqrt(dummy2**2 + c2) + dummy1 - x) // 2
return result
```

### Checking whether the reserve change is allowed

User actions will modify reserves in a certain way, and we need to check after each action whether the final values are within the allowed range.

```
def in_bounds(x: List[int]):
sum_ = sum(x)
for i in range(n_basset):
w = FULL_SCALE * x[i] // sum_
if not (penalty[i].hard_min <= w and w <= penalty[i].hard_max):
return False
return True
```

### Computing mint output

The amount of LP tokens received for adding a certain amount of pool asset is computed as

```
def compute_mint(i: int, quantity: int):
"""Compute the amount LP token received for minting
with `quantity` amount of asset index `i`."""
x = get_current_reserves()
lp_supply = get_supply() # LP token supply
A = get_A()
k_init = compute_k(x[0], x[1], A)
x[i] += quantity
k_final = compute_k(x[0], x[1], A)
if lp_supply == 0:
total_minted = k_final - k_init
else:
total_minted = lp_supply * (k_final - k_init) // k_init
if not in_bounds(x):
raise Exception("Mint not allowed")
return total_minted
```

### Compute redeem output

The amount of assets received for redeeming a certain amount of LP tokens is computed as

```
def compute_redeem(i: int, quantity: int):
"""Compute the amount of asset index `i` received for
redeeming `quantity` amount of LP token."""
if i == 0:
j = 1
elif i == 1:
j = 0
redemption_fee = quantity * swap_fee_rate // FULL_SCALE
deducted_quantity = quantity - redemption_fee
x = get_current_reserves()
lp_supply = get_supply() # LP token supply
A = get_A()
k_init = compute_k(x[0], x[1], A)
k_final = k_init * (lp_supply - deducted_quantity) // lp_supply
new_reserve = compute_y(x[j], k_final, A)
total_received = x[i] - new_reserve
x[i] = new_reserve
if not in_bounds(x):
raise Exception("Redeem not allowed")
return total_received, redemption_fee
```

### Compute swap output

The output of swapping a certain amount of an asset for another one is computed as

```
def compute_swap(input_idx: int, output_idx: int, quantity: int):
"""Compute the amount of asset received for swapping
`quantity` amount of index `input_idx` to index `output_idx`."""
x = get_current_reserves()
lp_supply = get_supply() # LP token supply
A = get_A()
k1 = compute_k(x[0], x[1], A)
x[input_idx] += quantity
k2 = compute_k(x[0], x[1], A)
total_minted = k2 - k1
swap_fee = total_minted * swap_fee_rate // FULL_SCALE
deducted_quantity = total_minted - swap_fee
swap_fee = swap_fee * lp_supply // k1
k3 = k2 - deducted_quantity
new_reserve = compute_y(x[input_idx], k3, A)
total_received = x[output_idx] - new_reserve
x[output_idx] = new_reserve
if not in_bounds(x):
raise Exception("Swap not allowed")
return total_received, swap_fee
```

### Configurable Values (Via MCCP)

Each bAsset has the following configurable values:

`hard_min`

`hard_max`

`A`

`governance fee`

`swap fee`

`redemption fee`

`cache size`

## Copyright

Copyright and related rights waived via CC0.