Skip to main content
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.
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.

Fee Types

FeeFormulaWhen Collected
Trading Feenotional * snapshotTradingFeeBps / 10,000Open, increase, settlement, close, reduce
Liquidation Penaltynotional * snapshotLiquidationPenaltyBps / 10,000Liquidation only (added to trading fee)
Oracle FeeFlat snapshotOracleFee per price readEvery forward price lookup

Default Values

Trading Fee

5 bps (0.05%)Applied to the notional amount on every position open, increase, settlement, early termination, and reduction.

Liquidation Penalty

30 bps (0.3%)Applied to the notional amount only during liquidation, added on top of the trading fee. Combined liquidation fee = 35 bps.

Oracle Fee

0.10 USDCFlat fee per forward price lookup. Collected from the trader’s collateral each time a forward price is read.

Fee Distribution

All collected fees (trading fees and liquidation penalties) are split between two destinations:
DestinationDefault ShareDescription
Treasury30% (3,000 bps)Protocol revenue for operations and development
Pool70% (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.
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.

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.
availableMargin = marginAtRisk    // full margin is available
actualFee = min(fee, availableMargin)
When a position is profitable, the full calculated fee is typically collected since the margin is intact.
Fee capping means that in bad debt scenarios (where the trader’s loss exceeds their margin), the protocol may not collect the full fee. This is by design — the priority order is: (1) settle PnL, (2) collect fees from remainder. The protocol prioritizes correct PnL settlement over fee collection.

Fee Collection Methods

Fees are collected through different mechanisms depending on the context:
ContextCollection MethodSource
Position opencollectFeeFree collateral (available balance beyond locked margin)
Position increasecollectFeeFree collateral
Settlement (all types)Deducted in _settlePositionLocked margin (after PnL)
Oracle feecollectFeeFromCollateralTotal collateral (can draw from locked portion)
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.

Snapshotted Rates

All fee rates are captured at position open time and stored in the position struct:
Snapshotted FieldDefault Value
snapshotTradingFeeBps5 (0.05%)
snapshotLiquidationPenaltyBps30 (0.3%)
snapshotOracleFee100,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

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
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
Position:
  • Notional: 1,000 USDC
  • IM locked (marginAtRisk): 20 USDC
  • Realized loss: -18 USDC (capped at margin)
  • Trading fee calculated: 0.50 USDC
Available margin after loss:
marginAfterLoss = 20 - 18 = 2 USDC
actualFee = min(0.50, 2.00) = 0.50 USDC   // full fee collected
In this case, the margin after loss (2 USDC) is enough to cover the 0.50 USDC fee.But if the loss were 19.80 USDC:
marginAfterLoss = 20 - 19.80 = 0.20 USDC
actualFee = min(0.50, 0.20) = 0.20 USDC   // partial fee only
The protocol collects only 0.20 USDC instead of the full 0.50 USDC. The remaining 0.30 USDC is not collected and is effectively absorbed as reduced protocol revenue.

Liquidation Penalty Destination

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.

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.