# Stableswap-NG: Overview

> import DocCard, { DocCardGrid } from '@site/src/components/DocCard'

# Stableswap-NG: Overview

:::deploy[Contract Source & Deployment]

Source code is available on [ GitHub](https://github.com/curvefi/stableswap-ng). The following documentation covers source code up until commit number [`5f582a6`](https://github.com/curvefi/stableswap-ng/commit/5f582a6b8f709d863825c5fbe026cd3b4fa2d840).

All stableswap-ng deployments can be found in the "Deployment Addresses" section. [↗](../../deployments.md)

:::

For an in-depth understanding of the Stableswap invariant design, please refer to the official [Stableswap whitepaper](/pdf/whitepapers/whitepaper_stableswap.pdf).

A Curve pool is essentially a smart contract that implements the Stableswap invariant, housing the logic for exchanging stable tokens. While all Curve pools share this core implementation, they come in various pool flavors.

In its simplest form, a Curve pool is an implementation of the Stableswap invariant involving two or more tokens, often referred to as a 'plain pool.' Alternatively, Curve offers more complex pool variants, including pools with rebasing tokens and metapools. Metapools facilitate the exchange of one or more tokens with those from one or more underlying tokens.

**New features:**

- price and D oracles
- dynamic fees
- [**`exchange_received`**](../stableswap-ng/pools/plainpool.md#exchange_received)
- [**`get_dx`**](../stableswap-ng/pools/plainpool.md#get_dx)

---

## Supported Assets

Stableswap-NG pools support the following asset types:

| Asset Type  | Description            |
| :---------: | ---------------------- |
| `0`         | **Standard ERC20** token with no additional features |
| `1`         | **Oracle** - token with rate oracle (e.g. wstETH) |
| `2`         | **Rebasing** - token with rebase (e.g. stETH) |
| `3`         | **ERC4626** - token with *`convertToAssets`* method (e.g. sDAI) |

*Consequently, supported tokens include:*

- ERC20 support for return True/revert, return True/False, return None
- ERC20 tokens can have arbitrary decimals (&lt;=18)
- ERC20 tokens that rebase (either positive or fee on transfer)
- ERC20 tokens that have a rate oracle (e.g. wstETH, cbETH, sDAI, etc.) Oracle precision *must* be 10^18
- ERC4626 tokens with arbitrary precision (&lt;=18) of Vault token and underlying asset

:::warning[Rebasing Tokens]

Pools including rebasing tokens work a bit differently compared to others.
The internal **`_balance()`** function - which is used to calculate the coin balances within the pool - makes sure that **LP's keep all rebases**.

:::

<Dropdown title="`_balances()`">

```vyper
@view
@internal
def _balances() -> DynArray[uint256, MAX_COINS]:
    """
    @notice Calculates the pool's balances _excluding_ the admin's balances.
    @dev If the pool contains rebasing tokens, this method ensures LPs keep all
            rebases and admin only claims swap fees. This also means that, since
            admin's balances are stored in an array and not inferred from read balances,
            the fees in the rebasing token that the admin collects is immune to
            slashing events.
    """
    result: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    balances_i: uint256 = 0

    for i in range(MAX_COINS_128):

        if i == N_COINS_128:
            break

        if POOL_IS_REBASING_IMPLEMENTATION:
            balances_i = ERC20(coins[i]).balanceOf(self) - self.admin_balances[i]
        else:
            balances_i = self.stored_balances[i] - self.admin_balances[i]

        result.append(balances_i)

    return result
```

</Dropdown>

---

## Dynamic Fees

Stableswap-NG introduces dynamic fees. The use of the **`offpeg_fee_multiplier`** allows the system to dynamically adjust fees based on the pool's state.

The internal **`_dynamic_fee()`** function calculates the fee **based on the balances and rates** of the tokens being exchanged. If the balances of the tokens being exchanged are highly imbalanced or significantly differ from its peg, the fee is adjusted using the **`offpeg_fee_multiplier`**.

:::bug

If the formulas below do not render, please make sure to refresh the site. A solution is being worked on.

:::

*Let's define some terms and variables for clarity:*

- Let $fee$ represent the fee, as retrieved by the method **`StableSwap.fee()`**
- Let $fee_m$ denote the off-peg fee multiplier, sourced from **`StableSwap.offpeg_fee_multiplier()`**
- **`FEE_DENOMINATOR`** is a constant with a value of $10^{10}$, representing the precision of the fee
- The terms $rate_{i}$ and $balance{i}$ refer to the specific rate and balance for coin $i$, respectively, and similarly, $rate_j$ and $balance_j$ for coin $j$
- $PRECISION_{i}$ and $PRECISION_{j}$ are the precision constants for the respective coins

*Given these, we define:*

$xp_{i} = \frac{{rate_{i} \times balance_{i}}}{{PRECISION_{i}}}$

$xp_{j} = \frac{{rate_{j} \times balance_{j}}}{{PRECISION_{j}}}$

$xp_{i}$ and $xp_{j}$ are the token balances of the pool adjusted for decimals and the pool's internal rates (stored in `stored_rates`).

*And we also have:*

$xps2 = (xp_{i} + xp_{j})^2$

**The dynamic fee is calculated by the following formula:**$$\text{dynamic fee} = \frac{{fee_{m} \times fee}}{\frac{(fee_{m} - 10^{10}) \times 4 \times xp_{i} \times xp_{j}}{xps2}+ 10^{10}}$$

<Dropdown title="`dynamic_fee` method">

```vyper
A_PRECISION: constant(uint256) = 100
MAX_COINS: constant(uint256) = 8
PRECISION: constant(uint256) = 10 **18
FEE_DENOMINATOR: constant(uint256) = 10 **10

@view
@external
def dynamic_fee(i: int128, j: int128, pool:address) -> uint256:
    """
    @notice Return the fee for swapping between `i` and `j`
    @param i Index value for the coin to send
    @param j Index value of the coin to recieve
    @return Swap fee expressed as an integer with 1e10 precision
    """
    N_COINS: uint256 = StableSwapNG(pool).N_COINS()
    fee: uint256 = StableSwapNG(pool).fee()
    fee_multiplier: uint256 = StableSwapNG(pool).offpeg_fee_multiplier()

    rates: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    balances: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    xp: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    rates, balances, xp = self._get_rates_balances_xp(pool, N_COINS)

    return self._dynamic_fee(xp[i], xp[j], fee, fee_multiplier)

@view
@internal
def _dynamic_fee(xpi: uint256, xpj: uint256, _fee: uint256) -> uint256:

    _offpeg_fee_multiplier: uint256 = self.offpeg_fee_multiplier
    if _offpeg_fee_multiplier <= FEE_DENOMINATOR:
        return _fee

    xps2: uint256 = (xpi + xpj) **2
    return (
        (_offpeg_fee_multiplier * _fee) /
        ((_offpeg_fee_multiplier - FEE_DENOMINATOR) * 4 * xpi * xpj / xps2 + FEE_DENOMINATOR)
    )

@view
@internal
def _get_rates_balances_xp(pool: address, N_COINS: uint256) -> (
    DynArray[uint256, MAX_COINS],
    DynArray[uint256, MAX_COINS],
    DynArray[uint256, MAX_COINS],
):

    rates: DynArray[uint256, MAX_COINS] = StableSwapNG(pool).stored_rates()
    balances: DynArray[uint256, MAX_COINS] = StableSwapNG(pool).get_balances()
    xp: DynArray[uint256, MAX_COINS] = empty(DynArray[uint256, MAX_COINS])
    for idx in range(MAX_COINS):
        if idx == N_COINS:
            break
        xp.append(rates[idx] * balances[idx] / PRECISION)

    return rates, balances, xp
```

</Dropdown>

### Interactive Graph

The embedded graph has limited features, such as the inability to modify the axis. However, by clicking the *"edit graph on desmos"* button at the bottom right, one is redirected to the main Desmos site. There, a wider range of functionalities is available, allowing for further adjustments and detailed exploration of the graph.

---

## Oracles

The new generation (NG) of stableswap introduces two new pool-built-in oracles:

- **price oracle** (spot and moving-average price)
- moving average **D oracle**

More on oracles [here](./pools/oracles.md).

---

## `exchange_received`

This new function **allows the exchange of tokens without actually transferring tokens in**, as the exchange is based on the change of the coins balances within the pool.

Users of this method are dex aggregators, arbitrageurs, or other users who **do not wish to grant approvals to the contract**. They can instead send tokens directly to the contract and call **`exchange_received()`**.

:::abstract[Article]

Explore the `exchange_received` function's role in streamlining swaps without approvals, its efficiency benefits, and security considerations in a succinct article. Learn more about this innovative feature for cost-effective, secure trading through Curve pools: [How to Do Cheaper, Approval-Free Swaps](https://blog.curvemonitor.com/posts/exchange-received/).

:::

---

**The Stableswap-NG AMM infrastructure represents a technically enhanced iteration of the previous stableswap implementation. It comprises the following key components:**

<DocCardGrid>
  <DocCard title="AMM Blueprint Contracts" link="./pools/plainpool" linkText="Plain Pools">

Stableswap-NG has two main implementations: [**Plain Pools**](./pools/plainpool.md) and [**Metapools**](./pools/metapool.md).

  </DocCard>
  <DocCard title="CurveStableSwapFactoryNG.vy" icon="vyper" link="../../factory/stableswap-ng/overview" linkText="CurveStableSwapFactoryNG.vy">

The Pool Factory is used to **permissionlessly deploy new plain and metapools**, as well as **liquidity gauges**. It also acts as a registry for finding the deployed pools and querying information about them.

  </DocCard>
  <DocCard title="CurveStableSwapNGMath.vy" icon="vyper" link="./utility-contracts/math" linkText="CurveStableSwapNGMath.vy">

Contract which provides **mathematical utilities** for the AMM blueprint contracts.

  </DocCard>
  <DocCard title="CurveStableSwapNGViews.vy" icon="vyper" link="./utility-contracts/views" linkText="CurveStableSwapNGViews.vy">

Contract targeted at **integrators**. Contains **view-only external methods** for the AMMs.

  </DocCard>
  <DocCard title="Liquidity Gauge Blueprint">

A liquidity gauge blueprint implementation which deploys a liquidity gauge of a pool on Ethereum. Gauges on sidechains must be deployed via the `RootChainGaugeFactory`.

  </DocCard>
  <DocCard title="Oracles" link="./pools/oracles" linkText="Oracles">

Exponential moving-average oracles for the `D` invariant and for the prices of coins within the AMM.

  </DocCard>
</DocCardGrid>
