Skip to main content
The CTFExchange implements a symmetric fee structure that ensures fairness across complementary outcome tokens. Fees are always levied on the proceeds (output asset) of a trade.

Why Symmetric Fees?

Fees for binary options with complementary relationships must be symmetric to preserve market integrity.
Symmetric means that someone selling 100 shares of A @ 0.99shouldpaythesamefeevalueassomeonebuying100sharesofA@0.99 should pay the same fee value as someone buying 100 shares of `A'` @ 0.01.This is necessary because minting/merging complementary token sets for collateral can happen at any time, and asymmetric fees would create arbitrage opportunities.

Fee Application Rules

Fees are applied differently based on the order side:

Buying Outcome Tokens

Fee is levied on the proceed tokens (the outcome tokens received)

Selling Outcome Tokens

Fee is levied on the proceed collateral (the USDC/collateral received)

Fee Parameters

Base Fee Rate

The feeRateBps field in the Order struct defines the base fee rate in basis points (BPS).
OrderStructs.sol
struct Order {
    // ... other fields
    /// @notice Fee rate, in basis points, charged to the order maker, charged on proceeds
    uint256 feeRateBps;
    // ... other fields
}
The base fee rate corresponds to 2x the fee rate (collateral per unit of outcome token) paid by traders when the price of the two tokens is equal (i.e., 0.50and0.50 and 0.50).

Maximum Fee Rate

The exchange enforces a maximum fee rate to protect users:
Fees.sol
/// @notice Maximum fee rate that can be signed into an Order
uint256 internal constant MAX_FEE_RATE_BIPS = 1000; // 1000 bips or 10%

Fee Calculation Formulas

The fee calculation adjusts based on price to maintain symmetry. The exchange uses min(price, 1-price) to ensure symmetric fees.

Case 1: Selling Outcome Tokens

When selling outcome tokens for collateral, the fee is charged on the collateral proceeds:feeQuote=baseRate×min(price,1price)×sizefeeQuote = baseRate × min(price, 1-price) × size Implementation:
CalculatorHelper.sol
// Fee charged on Collateral proceeds:
// baseRate * min(price, 1-price) * outcomeTokens
fee = feeRateBps * min(price, ONE - price) * outcomeTokens / (BPS_DIVISOR * ONE);
Where:
  • feeRateBps - Fee rate in basis points (1 bp = 0.01%)
  • outcomeTokens - Number of outcome tokens being sold
  • BPS_DIVISOR - 10,000 (converts basis points to decimal)
  • ONE - 10^18 (for fixed-point precision)

Fee Calculation Examples

The following examples demonstrate fee symmetry with baseFeeRate = 0.02 (200 BPS or 2%):
1

Buy 100 A @ $0.50

fee = 2 A tokens
value = $1.00
2

Sell 100 A' @ $0.50

fee = 1.0 C (USDC)
value = $1.00
Note: The USD value is identical, demonstrating symmetry.
1

Buy 100 A @ $0.10

fee = 2 A tokens
value = $0.20
2

Sell 100 A @ $0.90

fee = 0.20 C (USDC)
value = $0.20
Note: Lower price results in lower absolute fee value.
1

Buy 100 A @ $0.90

fee = 0.222 A tokens
value = $0.20
2

Sell 100 A @ $0.10

fee = 0.20 C (USDC)
value = $0.20
Note: The min(price, 1-price) function ensures the same fee value.

Implementation Details

The complete fee calculation is implemented in the CalculatorHelper library:
CalculatorHelper.sol
function calculateFee(
    uint256 feeRateBps,
    uint256 outcomeTokens,
    uint256 makerAmount,
    uint256 takerAmount,
    Side side
) internal pure returns (uint256 fee) {
    if (feeRateBps > 0) {
        uint256 price = _calculatePrice(makerAmount, takerAmount, side);
        if (price > 0 && price <= ONE) {
            if (side == Side.BUY) {
                // Fee charged on Token Proceeds:
                // baseRate * min(price, 1-price) * (outcomeTokens/price)
                fee = (feeRateBps * min(price, ONE - price) * outcomeTokens) / (price * BPS_DIVISOR);
            } else {
                // Fee charged on Collateral proceeds:
                // baseRate * min(price, 1-price) * outcomeTokens
                fee = feeRateBps * min(price, ONE - price) * outcomeTokens / (BPS_DIVISOR * ONE);
            }
        }
    }
}

Key Properties

Symmetry

Complementary trades at prices p and 1-p pay identical fee values

Price Adjustment

Fees adjust dynamically based on min(price, 1-price) to maintain fairness

Proceeds-Based

Fees are always charged on what you receive, not what you pay

Capped

Maximum fee rate of 10% (1000 BPS) protects users from excessive fees

Build docs developers (and LLMs) love