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

# Fee Structure

> Four fee types: trading (0.05% of notional), liquidation penalty (0.3%), early termination (0.05%), and oracle fee (flat per read) — split 70% to LP pool, 30% to treasury, snapshotted at position open.

The Open Nile Protocol collects three types of fees: trading fees on every position event, liquidation penalties on distressed positions, and oracle fees for price reads. All fees use snapshotted rates from position open time, ensuring traders know their costs upfront. Fees are distributed between the protocol treasury and the liquidity pool according to a configurable split.

<Info>
  Nile Markets uses flat fee rates in M2 — there are no volume-based tiers or maker/taker distinctions. Every trader pays the same rates regardless of volume. Volume-tiered pricing is planned for M3.
</Info>

## Fee Types

| Fee                     | Formula                                             | When Collected                            |
| ----------------------- | --------------------------------------------------- | ----------------------------------------- |
| **Trading Fee**         | `notional * snapshotTradingFeeBps / 10,000`         | Open, increase, settlement, close, reduce |
| **Liquidation Penalty** | `notional * snapshotLiquidationPenaltyBps / 10,000` | Liquidation only (added to trading fee)   |
| **Oracle Fee**          | Flat `snapshotOracleFee` per price read             | Every forward price lookup                |

## Default Values

<Columns cols={3}>
  <Card title="Trading Fee" icon="receipt">
    **5 bps (0.05%)**

    Applied to the notional amount on every position open, increase, settlement, early termination, and reduction.
  </Card>

  <Card title="Liquidation Penalty" icon="triangle-exclamation">
    **30 bps (0.3%)**

    Applied to the notional amount only during liquidation, added on top of the trading fee. Combined liquidation fee = 35 bps.
  </Card>

  <Card title="Oracle Fee" icon="satellite-dish">
    **0.10 USDC**

    Flat fee per forward price lookup. Collected from the trader's collateral each time a forward price is read.
  </Card>
</Columns>

## Fee Distribution

All collected fees (trading fees and liquidation penalties) are split between two destinations:

| Destination | Default Share   | Description                                                      |
| ----------- | --------------- | ---------------------------------------------------------------- |
| Treasury    | 30% (3,000 bps) | Protocol revenue for operations and development                  |
| Pool        | 70% (7,000 bps) | LP compensation, directly increasing pool equity and share price |

The distribution is executed by `FeeLib.distributeFee()`, which iterates through all configured fee destinations and transfers proportional shares.

<Warning>
  Fee destination shares must sum to exactly 10,000 basis points (100%). This is enforced by `Config.setFeeDestinations()` and is one of the protocol's core invariants. If shares do not sum correctly, the configuration transaction reverts.
</Warning>

## Fee Capping

Fees are always capped at available margin. The protocol never reverts on insufficient fee funds -- it collects whatever is available. This ensures that settlement and liquidation cannot be blocked by a position that has been drained by losses.

<Tabs>
  <Tab title="After Profit">
    ```
    actualFee = min(fee, marginAtRisk)
    marginAfterFee = marginAtRisk - actualFee
    payout = marginAfterFee + pnl
    ```

    When a position is profitable, the full calculated fee is typically collected since the margin is intact, and the trader receives `marginAfterFee + pnl`.
  </Tab>

  <Tab title="After Loss">
    ```
    actualFee = min(fee, marginAtRisk)        // fee taken first
    marginAfterFee = marginAtRisk - actualFee
    cappedLoss = min(|pnl|, marginAfterFee)
    ```

    When a position has losses, the fee is taken first from the locked margin, then PnL is applied to the remainder. Fee collection is deterministic regardless of PnL sign — fees are a function of the trade, not its outcome. In extreme bad-debt scenarios the trader's loss is capped at `marginAtRisk - actualFee`, and any uncollectable loss is absorbed by LPs as bad debt.
  </Tab>

  <Tab title="Zero PnL">
    ```
    actualFee = min(fee, marginAtRisk)
    ```

    With no PnL, the full margin is available and the full fee is typically collected.
  </Tab>
</Tabs>

<Info>
  The fee waterfall is fee-first: `actualFee` is computed against the full `marginAtRisk`, then PnL is applied from the remainder. Under bad debt, LPs absorb the difference rather than the fee being trimmed. This is a deliberate design choice that makes fee revenue independent of PnL outcomes.
</Info>

## Fee Collection Methods

Fees are collected through different mechanisms depending on the context:

| Context                | Collection Method             | Source                                                        |
| ---------------------- | ----------------------------- | ------------------------------------------------------------- |
| Position open          | `collectFee`                  | Free collateral (available balance beyond locked margin)      |
| Position increase      | `collectFee`                  | Free collateral                                               |
| Settlement (all types) | Deducted in `_settlePosition` | Locked margin — **fee taken first**, PnL applied to remainder |
| Oracle fee             | `collectFeeFromCollateral`    | Total collateral (can draw from locked portion)               |

<Note>
  The oracle fee uses `collectFeeFromCollateral`, which can draw from the locked portion of collateral, not just the free balance. This is because oracle fees are small (0.10 USDC) and must be collected reliably for every price read. Allowing it to draw from locked collateral prevents oracle fee collection from failing when a trader has minimal free collateral.
</Note>

## Snapshotted Rates

All fee rates are captured at position open time and stored in the position struct:

| Snapshotted Field               | Default Value       |
| ------------------------------- | ------------------- |
| `snapshotTradingFeeBps`         | 5 (0.05%)           |
| `snapshotLiquidationPenaltyBps` | 30 (0.3%)           |
| `snapshotOracleFee`             | 100,000 (0.10 USDC) |

If the protocol admin changes fee rates after a position is opened, the position continues to use its snapshotted rates. Only newly opened positions use the updated rates. This protects traders from retroactive fee changes.

## Worked Examples

<AccordionGroup>
  <Accordion title="Standard trading fee on position open">
    **Position:**

    * Notional: 1,000 USDC (1,000,000,000 raw)
    * Trading fee: 5 bps

    **Calculation:**

    ```
    fee = 1,000,000,000 * 5 / 10,000 = 500,000 = 0.50 USDC
    ```

    **Distribution:**

    * Treasury (30%): 0.50 \* 3,000 / 10,000 = 0.15 USDC
    * Pool (70%): 0.50 \* 7,000 / 10,000 = 0.35 USDC
  </Accordion>

  <Accordion title="Liquidation fee (trading fee + penalty)">
    **Position:**

    * Notional: 1,000 USDC
    * Trading fee: 5 bps
    * Liquidation penalty: 30 bps

    **Calculation:**

    ```
    tradingFee = 1,000 * 5 / 10,000 = 0.50 USDC
    liquidationPenalty = 1,000 * 30 / 10,000 = 3.00 USDC
    totalFee = 0.50 + 3.00 = 3.50 USDC
    ```

    **Distribution:**

    * Treasury (30%): 3.50 \* 3,000 / 10,000 = 1.05 USDC
    * Pool (70%): 3.50 \* 7,000 / 10,000 = 2.45 USDC
  </Accordion>

  <Accordion title="Fee-first waterfall under bad debt">
    **Position:**

    * Notional: 1,000 USDC
    * IM locked (marginAtRisk): 20 USDC
    * Calculated loss: -19 USDC
    * Trading fee calculated: 1 USDC (combined trading + liquidation penalty in this example)

    **Fee-first waterfall:**

    ```
    actualFee       = min(1, 20)        = 1 USDC          // fee taken first
    marginAfterFee  = 20 - 1            = 19 USDC
    cappedLoss      = min(19, 19)       = 19 USDC         // entire margin-after-fee absorbs the loss
    badDebt         = max(0, 19 - 19)   = 0 USDC          // no bad debt this time
    traderNet       = -(actualFee + cappedLoss) = -20 USDC
    ```

    **What if the loss had been 25 USDC instead?**

    ```
    actualFee       = min(1, 20)        = 1 USDC          // fee paid in full
    marginAfterFee  = 20 - 1            = 19 USDC
    cappedLoss      = min(25, 19)       = 19 USDC         // capped at remaining margin
    badDebt         = 25 - 19           = 6 USDC          // pool absorbs 6 USDC
    traderNet       = -(actualFee + cappedLoss) = -20 USDC // trader still loses full 20
    ```

    Under fee-first, the fee is always collected as long as `marginAtRisk > 0`. The pool absorbs any uncollectable loss as bad debt, but fee revenue stays deterministic.
  </Accordion>
</AccordionGroup>

## Liquidation Penalty Destination

<Warning>
  The liquidation penalty goes entirely to fee destinations (30% treasury, 70% pool), **not** to the address that triggers the liquidation. The liquidator receives no reward -- they only pay gas. This design choice means that in M2, the keeper service performs liquidations as a protocol service rather than as an economically motivated external actor.
</Warning>

## Fee on Position Increase

When a position is increased via `increasePosition`, the trading fee is calculated on the **additional notional only**, not the full new notional:

```
fee = additionalNotional * snapshotTradingFeeBps / 10,000
```

The trader has already paid the trading fee on the original notional at position open. Only the new exposure incurs a fee.

## Fee on Position Reduction

Similarly, when a position is reduced via `reducePosition`, the fee is on the **reduction amount only**:

```
fee = reductionNotional * snapshotTradingFeeBps / 10,000
```

This ensures fees are proportional to the notional being transacted, not the full position size.
