> ## Documentation Index
> Fetch the complete documentation index at: https://docs.multiliquid.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Ladder Pricing Model

> How an LP can implement a rolling 24-hour laddered pricing model on Multiliquid SVM using pair fee updates and vault liquidity management

## Overview

The Multiliquid SVM program does not implement a native laddered pricing model on chain. Instead, each pair stores one live discount rate and one live redemption fee.
An LP can still build a laddered pricing model on top of those fields by using an off-chain worker to monitor rolling volume and update fees as thresholds are crossed.

* Each `Pair` has one `redemption_fee_bps` and one `discount_rate_bps`, which can be updated by the LP admin every block if needed
* Each `Pair` sources liquidity from two vaults: one vault for the stablecoin and one vault for the RWA
* Swaps consume whatever fee values are currently stored in the pair

Given that design, a laddered pricing model has to be enforced operationally.

<Info>
  This page uses `discount_rate_bps` for the primary example, but the same approach can be mirrored onto `redemption_fee_bps`.
</Info>

**Example scenario:** An LP admin wants to keep up to \$2,500,000 of live stablecoin inventory available for `RWA1` while applying a tiered discount schedule across rolling 24-hour `AssetToStable` volume.
The first \~\$2.5M should clear at a 1% discount, giving the user 99% of the `RWA1` NAV value in stablecoin.
From \$2.5M to \$5M, the discount is set at 5%. From \$5M to \$10M, the discount is set at 15%.

| Rolling 24-hour cumulative volume | Active `discount_rate_bps` | Discount |
| :-------------------------------- | :------------------------- | :------- |
| `$0` to `< $2.5M`                 | `100`                      | `1.00%`  |
| `$2.5M` to `< $5M`                | `500`                      | `5.00%`  |
| `$5M` to `< $10M`                 | `1500`                     | `15.00%` |

This pricing model is enforced by an off-chain worker that tracks cumulative rolling 24-hour volume for swaps on `RWA1`.

1. The LP keeps between `$1,000,000` and `$2,500,000` of live stablecoin inventory in the vault at any given time
2. An off-chain worker measures rolling 24-hour volume
3. The worker decides which tier is currently active
4. When a threshold is crossed, the worker updates `discount_rate_bps` via `update_pair`
5. The LP uses `add_liquidity` to top up the stablecoin vault when balances fall below the chosen threshold, for example `$1,000,000`, potentially sourcing capital from a yield-generating product such as Liquid Treasury

## What The Program Gives You

### Pair Fee Fields

The pair stores only the **currently active** fee values:

```rust theme={null}
pub struct Pair {
    pub redemption_fee_bps: u16,
    pub discount_rate_bps: u16,
    pub stable_coin_mint_address: Pubkey,
    pub asset_token_mint_address: Pubkey,
    pub liquidity_provider: Pubkey,
    pub paused: bool,
    pub bump: u8,
}
```

Operationally, this means:

* `redemption_fee_bps` is the live fee for `StableToAsset`
* `discount_rate_bps` is the live fee for `AssetToStable`
* the current ladder rung is represented by the current value stored in the pair

### Live Fee Updates

The LP can rotate the active rung with [`update_pair`](/svm/instructions/pair#update_pair):

```rust theme={null}
pub fn update_pair(
    ctx: Context<UpdatePair>,
    redemption_fee_bps: u16,
    discount_rate_bps: u16,
    paused: bool,
) -> Result<()>
```

This is the key mechanism that makes the ladder possible despite only having one fee field per direction.

### Shared Vault Liquidity

Liquidity is not stored per pair. It is stored per **LP + mint**:

* one vault per LP and stable mint
* one vault per LP and RWA mint

On chain, the LP component is represented by the `vault_authority` PDA derived from `["vault_authority", liquidity_provider]`. Each vault is the associated token account for `(mint, vault_authority)`, not a token account owned by the global program authority.

So:

* `add_liquidity` deposits into the LP's vault for a mint
* `remove_liquidity` withdraws from that same vault
* if the LP has multiple pairs using the same mint, those pairs share the same vault balance

This shared-vault model is what lets the LP keep only a bounded amount of capital on chain while still supporting more cumulative daily turnover.

## Directional Fee And Inventory Mechanics

The two fee fields are directional:

| Swap direction  | Pair field used      | Inventory that leaves LP vault | Inventory that enters LP vault |
| :-------------- | :------------------- | :----------------------------- | :----------------------------- |
| `StableToAsset` | `redemption_fee_bps` | RWA                            | Stablecoin                     |
| `AssetToStable` | `discount_rate_bps`  | Stablecoin                     | RWA                            |

For the scenario in this page, the ladder is a **discount ladder**, so the relevant direction is:

* user sells RWA to the LP
* LP pays out stablecoin
* the live price lever is `discount_rate_bps`
* the constrained inventory is the LP's **stablecoin vault**

That is why this model can be run with:

* a rolling 24-hour counter off chain
* periodic `update_pair` calls that change only `discount_rate_bps`
* periodic stablecoin `add_liquidity` and `remove_liquidity` calls

In many deployments, `redemption_fee_bps` can stay fixed while only `discount_rate_bps` changes with the ladder.

If the LP also wants a ladder on the opposite direction, the same pattern applies:

* maintain a separate rolling notional book for `StableToAsset`
* choose the active `redemption_fee_bps` from that book
* pass both the current `redemption_fee_bps` and current `discount_rate_bps` into `update_pair`

In other words, the pair does not store an entire schedule. It stores the **currently active rung for each direction**.

## Reference Ladder

For the example strategy, define the active `discount_rate_bps` from rolling 24-hour notional:

| Rolling 24-hour cumulative volume | Active `discount_rate_bps` | Discount |
| :-------------------------------- | :------------------------- | :------- |
| `$0` to `< $2,500,000`            | `100`                      | `1.00%`  |
| `$2,500,000` to `< $5,000,000`    | `500`                      | `5.00%`  |
| `$5,000,000` to `< $10,000,000`   | `1500`                     | `15.00%` |

This should be read as:

* there is one active rung at any point in time
* the worker recomputes the rolling 24-hour total
* the worker updates the pair to the fee corresponding to the current band

The program does not transition bands automatically. The LP's worker does.

## How To Measure The Rolling 24-Hour Volume

For a discount ladder on `AssetToStable`, the cleanest operational metric is:

```text theme={null}
stable_outflow_per_fill = amount_out + protocol_fee_amount
```

Why this works:

* on `AssetToStable`, the user receives stablecoin from the LP stable vault
* protocol fees are also paid out of that same stable vault
* so `amount_out + protocol_fee_amount` is the total stable inventory consumed by the fill

That makes the rolling ladder straightforward to compute:

```text theme={null}
rolling_24h_stable_outflow =
  Σ(amount_out + protocol_fee_amount)
  for all AssetToStable swaps in the last 24 hours
```

For a `$1.00` stablecoin such as USDC, that rolling stable outflow is already a practical dollar proxy.

<Note>
  For `AssetToStable`, do not rely on the raw `amount_in` field from the `SwapExecuted` event as your primary volume metric. In this direction, the useful inventory measure is stablecoin consumed from the LP vault, which is `amount_out + protocol_fee_amount`.
</Note>

## How The Live Inventory Band Works

Assume the LP wants to keep up to `$2,500,000` of stablecoin live in the vault, and refill when the balance drops below `$1,000,000`.

Define:

* target stable vault balance: `$2,500,000`
* refill floor: `$1,000,000`
* refill amount: `target - current_vault_balance`

Then the worker applies the following policy:

* if the stable vault balance is below `$1,000,000`, call `add_liquidity` with the delta back to `$2,500,000`
* if the stable vault balance is above `$2,500,000`, call `remove_liquidity` for the excess

This keeps the live capital band between `$1,000,000` and `$2,500,000` while allowing the LP to manage on-chain inventory independently from the rolling fee ladder.

That is the core distinction:

* **live liquidity** is what is currently sitting in the vault
* **rolling 24-hour volume** is what has traded through the vault over time

The LP can therefore run a rolling ladder that tops out at `$10,000,000` while keeping between `$1,000,000` and `$2,500,000` of stablecoin inventory on chain, replenishing inventory as needed during the day.

## Why `remove_liquidity` Matters

`remove_liquidity` is just as important as `add_liquidity`.

In this model:

* `AssetToStable` flow drains stablecoin from the stable vault and accumulates RWA in the RWA vault
* `StableToAsset` flow does the opposite: it adds stablecoin to the stable vault and drains RWA from the RWA vault

That means the worker can use `remove_liquidity` in two practical ways:

* sweep excess stablecoin out of the stable vault when reverse flow pushes it above the `$2,500,000` cap
* sweep accumulated RWA out of the RWA vault after users sell RWA into the LP

This lets the LP keep only the intended amount of on-chain capital exposed to the pair while moving the rest back to custody, treasury, or an execution wallet.

## Single-Pair And Multi-Pair Variants

### Single Pair

For one `RWA / stablecoin` pair, the model is simple:

* track rolling 24-hour volume for that pair
* update that pair's `discount_rate_bps`
* manage the pair's shared stable vault balance

### Multiple Pairs Sharing The Same Stablecoin

If the LP wants one ladder across several pairs that all share the same stablecoin:

* the stable vault is already shared across those pairs because the vault is keyed by `LP + stable mint`
* the worker should aggregate rolling notional across the full set of managed pairs
* the worker should push the same active `discount_rate_bps` to every pair in that set
* stablecoin rebalancing is done once at the shared stable vault

This is the cleanest version of a cross-pair ladder on the current program.

### Multiple Pairs Across Different Stablecoins

If the LP spans multiple stablecoins:

* each stablecoin still has its own vault
* the worker must normalize fills into a common USD measure off chain
* `update_pair` must still be called per pair
* liquidity rebalancing must still be done per stable vault

So the ladder can still be global across the LP's book, but the accounting is entirely off chain.

Contact the Multiliquid team for guidance on building an off-chain worker that tracks rolling 24-hour volume, rotates pair fees with `update_pair`, and rebalances vault inventory with `add_liquidity` and `remove_liquidity`.

## What To Do At 10M Rolling Volume

Because this page assumes no pause controls, the `$10,000,000` level should be treated as a **soft operating budget**, not as a hard protocol-enforced stop.

In practice, the worker usually does one or both of the following once rolling 24-hour volume reaches the final band:

* keep the pair on the highest configured discount tier
* stop topping the stable vault back up after it drains below the LP's desired exposure

That is enough to express the pricing model operationally, but it is important to be precise about the limit:

* the fee schedule is controllable
* the live inventory band is controllable
* the exact rolling-volume ceiling is **not** enforced atomically by the program

<Warning>
  Without an on-chain rolling-volume counter, fee-tier transitions are not atomic with swap execution. A trade can cross a threshold before the worker's next `update_pair` transaction lands. The model works well as an LP-admin policy, but it is not a hard on-chain invariant.
</Warning>

## Practical Summary

The current SVM swap program is sufficient to run this laddered model today:

* use `discount_rate_bps` as the current active rung for `AssetToStable`
* keep `redemption_fee_bps` fixed unless the LP also wants a ladder on `StableToAsset`
* measure rolling 24-hour stable outflow off chain
* update the pair fee when the rolling total enters a new band
* keep only `$1,000,000` to `$2,500,000` of live stablecoin inventory in the vault with `add_liquidity` and `remove_liquidity`
* if the ladder spans multiple pairs, aggregate volume off chain and push the selected rung to each managed pair
* treat the `$10,000,000` threshold as an operational target rather than a hard on-chain invariant

That is the correct way to implement a laddered LP pricing model on the current protocol without adding any new on-chain state.

***

<CardGroup cols={2}>
  <Card title="Pair Management" icon="file-code" href="/svm/instructions/pair">
    Full reference for pair instructions, account structures, and liquidity management
  </Card>

  <Card title="Integration Guide" icon="book" href="/svm/guides/integration">
    SDK installation, quoting, and swap execution
  </Card>
</CardGroup>
