Fee System
Fees are charged on every position open and close. They flow to three recipients: the vault (liquidity providers), the treasury (protocol), and keepers (execution incentive).
Fee Components
Base Fee
The base fee depends on which side of the market the position is on:
If the position's side has more or equal open interest, fee_dom applies. If it has less, fee_non_dom applies. Dominance is evaluated at the time of the action (open or close), not at position creation.
Price Impact Fee
Where impact is the per-market divisor from MarketConfig. Uses floor division.
Funding
Accumulated funding cost or credit since the position was filled:
Positive funding represents a cost to the position (position paid funding). Negative funding represents a credit (position received funding). Funding is purely peer-to-peer: 100% flows between longs and shorts with no protocol cut. See Funding Rate for how indices are computed.
Borrowing Fee
Accumulated borrowing cost since the position was filled:
Borrowing fees are always non-negative and accrue only to the dominant side of the market. The borrowing rate is an additive dual-utilization curve:
Where r_base and r_var are global parameters (TradingConfig), and r_var_market is per-market (MarketConfig). The vault term uses a quintic curve (gentle at low utilization, aggressive near capacity) and the market term uses a cubic curve (reacts faster to per-market congestion).
Total Fee
Protocol Fee
Protocol revenue excludes funding (which is peer-to-peer):
Treasury receives a cut of protocol_fee. Keepers receive a cut of trading_fee (base + impact only).
Fee Distribution
On Market Order Open (User)
| Recipient | Amount |
|---|---|
| Treasury | protocol_fee * treasury_rate / SCALAR_7 where protocol_fee = base_fee + impact_fee |
| Vault | (base_fee + impact_fee) - treasury_fee |
| Keeper | 0 (no keeper involved) |
Only base and impact fees apply at open (no borrowing or funding yet).
On Position Close (User)
| Recipient | Amount |
|---|---|
| User | max(equity, 0) where equity = col + pnl - total_fee |
| Treasury | protocol_fee * treasury_rate / SCALAR_7 |
| Vault | col - user_payout - treasury_fee |
| Keeper | 0 (no keeper involved) |
If the vault transfer is negative (user profited), the vault pays via strategy_withdraw.
On Keeper Execution (Fill, TP, SL)
| Recipient | Amount |
|---|---|
| User | max(equity, 0) |
| Treasury | protocol_fee * treasury_rate / SCALAR_7 |
| Keeper | trading_fee * caller_rate / SCALAR_7 |
| Vault | col - user_payout - treasury_fee - caller_fee |
The keeper earns a share of trading fees (base + impact), not of borrowing or funding.
On Liquidation (Keeper)
| Recipient | Amount |
|---|---|
| Treasury | revenue * treasury_rate where revenue = min(protocol_fee + liq_fee, col) |
| Keeper | min(trading_fee + liq_fee, col) * caller_rate / SCALAR_7 |
| Vault | col - treasury_fee - caller_fee |
| User | 0 (all collateral redistributed) |
liq_fee = max(equity, 0) is the remaining equity at liquidation time. Treasury receives a share of the total revenue (protocol fees + liquidation fee). No PnL settlement occurs for the user.
Limit Order Fee Handling
When a limit order is placed, the user's full collateral is transferred to the contract with no fee deduction. Fees are computed and deducted from collateral at fill time via ctx.open(), based on the position's dominance at that moment. This means limit orders have no fee cost until they are actually filled by a keeper.
Treasury Rate
The protocol fee rate is fetched from the treasury contract via a cross-contract call (TreasuryClient::get_rate()) on every trade. This allows the protocol to adjust fees dynamically without redeploying or reconfiguring the trading contract.