Skip to main content

Fees

Symmetry has a multi-tier fee system. Fees are measured in basis points (bps), where 10,000 bps = 100%.

Fee Categories

CategoryDescription
Deposit FeeCharged when users deposit tokens into the vault. Computed as a percentage of the vault tokens being minted, and deducted before the remainder is sent to the depositor.
Withdrawal FeeCharged when users withdraw tokens from the vault. Computed as a percentage of the vault tokens being burned, and deducted before the proportional underlying tokens are released.
Management FeeOngoing fee charged over time, specified as an annualized rate. For example, 100 bps = 1% per year. The fee is accrued continuously and deducted from the vault’s value proportionally over time.
Performance FeeCharged on profits above the vault’s high watermark — the all-time high vault token price. Only the gain above the high watermark is subject to this fee. For example, if the high watermark is 1.00andthevaulttokenpricerisesto1.00 and the vault token price rises to 1.10, the performance fee applies to the 0.10gain.Ifthepricelaterdropsto0.10 gain. If the price later drops to 0.90 and recovers to 1.05,noperformancefeeischargedbecausethepricehasntexceededtheprevious1.05, no performance fee is charged because the price hasn't exceeded the previous 1.10 high watermark. This prevents double-charging on recovery from drawdowns.
Management fees and performance fees are currently disabled at the protocol level (global config). Setting non-zero values for these fees in vault configuration will have no effect until they are re-enabled. Only deposit and withdrawal fees are active. See Global Config for current status.
When enabled, performance fees are collected via supply dilution: the protocol mints new vault tokens to fee recipients (host, creator, managers, protocol) rather than removing underlying tokens from the vault. This increases the total vault token supply, which proportionally dilutes existing holders by the fee amount. The high watermark is updated after minting to prevent double-charging.

Fee Tiers

Each fee category is split across 4 vault-level configurable tiers plus a protocol fee layer (global config), collected independently:
TierSet ByModifiable
HostSet at vault creationNever (immutable)
CreatorCreator or authorized managerVia editFeesTx (subject to modification delay)
ManagersAuthorized managerVia editFeesTx (subject to modification delay)
VaultAuthorized manager (deposit & withdraw only)Via editFeesTx (subject to modification delay)
Symmetry ProtocolProtocol adminVia global config

Host Fees (Immutable)

Set at vault creation and cannot be changed:
host_platform_params: {
  host_pubkey: "<HOST_PUBKEY>",
  host_deposit_fee_bps: 10,
  host_withdraw_fee_bps: 10,
  host_management_fee_bps: 0,
  host_performance_fee_bps: 0,
}

Creator/Manager Fees

const tx = await sdk.editFeesTx(
  { vault: "<VAULT>", manager: "<MANAGER>" },
  {
    creator_deposit_fee_bps: 50,
    creator_withdraw_fee_bps: 50,
    creator_management_fee_bps: 100,   // currently disabled in global config
    creator_performance_fee_bps: 500,  // currently disabled in global config
    managers_deposit_fee_bps: 0,
    managers_withdraw_fee_bps: 0,
    managers_management_fee_bps: 0,    // currently disabled in global config
    managers_performance_fee_bps: 0,   // currently disabled in global config
    vault_deposit_fee_bps: 0,
    vault_withdraw_fee_bps: 0,
    modification_delay: 86400,
  }
);

Vault Fees

vault_deposit_fee_bps and vault_withdraw_fee_bps stay inside the vault rather than being distributed. This fee benefits all existing vault token holders.

Protocol Fees

Set in the global config by the protocol admin:
SettingDescription
symmetry_deposit_fee_bpsFlat deposit fee
symmetry_deposit_fee_share_bpsShare of total deposit fees going to protocol
symmetry_withdraw_fee_bpsFlat withdrawal fee
symmetry_withdraw_fee_share_bpsShare of total withdrawal fees going to protocol
symmetry_management_fee_bpsFlat management fee (currently 0)
symmetry_management_fee_share_bpsShare of total management fees going to protocol (currently 0)
symmetry_performance_fee_bpsFlat performance fee (currently 0)
symmetry_performance_fee_share_bpsShare of total performance fees going to protocol (currently 0)
symmetry_trade_fee_bpsTrade fee
symmetry_limit_order_fee_bpsLimit order fee

Fee Limits

The global config enforces maximum fee limits: max_deposit_fee_bps, max_withdraw_fee_bps, max_management_fee_bps, and max_performance_fee_bps.

Fee Accumulation & Claiming

Fees accumulate in the vault’s accumulatedFees field, tracked separately for each tier:
interface FormattedAccumulatedFees {
  symmetry_fees: number;
  creator_fees: number;
  host_fees: number;
  managers_fees: number;
}
Claiming fees is a two-step process: Step 1: Withdraw fees from vault
const tx = await sdk.withdrawVaultFeesTx({
  claimer: wallet.publicKey.toBase58(),
  vault: "<VAULT_PUBKEY>",
});
This automatically determines which fee types the claimer can collect based on their role, creates WithdrawVaultFees accounts, and transfers the fee tokens. Step 2: Claim remaining tokens (if needed)
const tx = await sdk.claimTokenFeesFromVaultTx({
  claimer: wallet.publicKey.toBase58(),
  withdrawVaultFees: "<WITHDRAW_VAULT_FEES_PUBKEY>",
});

Manager Fee Splitting

Manager fees are split based on fee_split_weight_bps. All manager weights must sum to 10,000. Example: if manager A has weight 6,000 and manager B has weight 4,000, A gets 60% and B gets 40%.

WithdrawVaultFees Account

There are 4 WithdrawVaultFees accounts per vault (one per fee type: symmetry=0, creator=1, host=2, managers=3). Each stores:
  • The vault it belongs to
  • Owners and their weight splits
  • Accumulated fee tokens and amounts

Fetching Fee Accounts

const fees = await sdk.fetchAllWithdrawVaultFees({ type: "vault", pubkey: "<VAULT>" });
const managerFees = await sdk.fetchManagerWithdrawVaultFees("<MANAGER_PUBKEY>");
const creatorFees = await sdk.fetchCreatorWithdrawVaultFees("<CREATOR_PUBKEY>");
const hostFees = await sdk.fetchHostWithdrawVaultFees("<HOST_PUBKEY>");
const symmetryFees = await sdk.fetchSymmetryWithdrawVaultFees("<SYMMETRY_PUBKEY>");

Oracles

Each token in a vault has an oracle aggregator that computes its price from up to 4 oracle sources.

Oracle Types

TypeEnumStringDescription
Pyth0pythPyth Network price feeds via Hermes (Price Feed IDs)
Raydium CLMM1raydium_clmmRaydium Concentrated Liquidity AMM TWAP (currently disabled on-chain)
Raydium CPMM2raydium_cpmmRaydium Constant Product AMM TWAP
Switchboard3switchboardSwitchboard oracle feeds (currently disabled on-chain)
Example255examplePlaceholder
Raydium CLMM and Switchboard oracles are currently disabled on-chain. Only Pyth and Raydium CPMM oracle types can be used. Attempting to configure a token with a disabled oracle type will fail.

Quote Tokens

Oracle prices can be denominated in:
QuoteEnumString
USDC0usdc
WSOL1wsol
USD2usd
The quote_token field tells the protocol what denomination the oracle feed reports prices in. For Pyth, most price feeds are denominated in USD, so use "usd". If a feed reports prices in USDC or WSOL, use the corresponding quote token — the protocol will automatically convert to a common base using the on-chain WSOL/USD and USDC/USD Pyth feeds.

Oracle Configuration

Each oracle source has these settings:
interface OracleInput {
  oracle_type: "pyth" | "raydium_clmm" | "raydium_cpmm" | "switchboard" | "example";
  account_lut_id: number;
  account_lut_index: number;
  account: string;
  weight_bps: number;
  is_required: boolean;
  conf_thresh_bps: number;
  volatility_thresh_bps: number;
  max_slippage_bps: number;
  min_liquidity: number;
  staleness_thresh: number;
  staleness_conf_rate_bps: number;
  token_decimals: number;
  twap_seconds_ago: number;
  twap_secondary_seconds_ago: number;
  quote_token: "usdc" | "wsol" | "usd";
}
account_lut_id (0 or 1) selects which of the vault’s two Address Lookup Tables contains the oracle account. account_lut_index is the position within that LUT. When adding a new token, use account_lut_id: 0 and account_lut_index: 0 — the SDK and rewriteLookupTablesTx will handle LUT management. After adding multiple tokens, call rewriteLookupTablesTx to rebuild the LUTs with all oracle accounts.

Oracle Aggregator

The aggregator combines prices from multiple oracle sources using weighted percentile calculations:
interface FormattedOracleAggregator {
  num_oracles: number;
  min_oracles_thresh: number;
  oracles: FormattedOracle[];
  min_conf_bps: number;
  conf_thresh_bps: number;
  conf_multiplier: number;
}
The weight_bps values across all oracles for a single token must sum to exactly 10,000 (100%). Additionally, min_conf_bps must be strictly less than conf_thresh_bps.

Default Oracle Settings (Pyth)

{
  oracle_type: "pyth",
  weight_bps: 10000,
  is_required: true,
  conf_thresh_bps: 200,
  volatility_thresh_bps: 200,
  max_slippage_bps: 1000,
  min_liquidity: 0,
  staleness_thresh: 120,
  staleness_conf_rate_bps: 50,
  twap_seconds_ago: 0,
  twap_secondary_seconds_ago: 0,
  quote_token: "usd",
}

Pyth Integration

For Pyth oracles, the account field is the Pyth price account pubkey. You can look up price feed IDs for all supported assets at Pyth Price Feed IDs. During price updates, the SDK:
  1. Fetches feed IDs from the on-chain price accounts.
  2. Requests VAAs from the Pyth Hermes API.
  3. Creates, initializes, writes, and verifies VAAs on-chain.
  4. Updates the individual price feeds.
  5. Runs the vault’s updateTokenPrices instruction.
  6. Closes the temporary VAA accounts.
This is handled automatically by updateTokenPricesTx.

Raydium CLMM Oracle

Raydium CLMM oracles are currently disabled on-chain. The information below is for reference only.
Uses on-chain TWAP observations and tick arrays to compute prices. Requires twap_seconds_ago to be set (e.g., 300 for a 5-minute TWAP).

Raydium CPMM Oracle

Uses on-chain TWAP observations and vault reserves to compute prices. Requires twap_seconds_ago to be set.

Multi-Oracle Example

Configure a token with both Pyth and Raydium CPMM oracles (note: Raydium CLMM is currently disabled):
await sdk.addOrEditTokenTx(
  { vault: "<VAULT>", manager: "<MANAGER>" },
  {
    token_mint: "<TOKEN_MINT>",
    active: true,
    min_oracles_thresh: 1,
    min_conf_bps: 10,
    conf_thresh_bps: 300,
    conf_multiplier: 1.5,
    oracles: [
      {
        oracle_type: "pyth",
        account_lut_id: 0,
        account_lut_index: 0,
        account: "<PYTH_PRICE_ACCOUNT>",
        weight_bps: 7000,
        is_required: false,
        conf_thresh_bps: 200,
        volatility_thresh_bps: 200,
        max_slippage_bps: 1000,
        min_liquidity: 0,
        staleness_thresh: 120,
        staleness_conf_rate_bps: 50,
        token_decimals: 9,
        twap_seconds_ago: 0,
        twap_secondary_seconds_ago: 0,
        quote_token: "usd",
      },
      {
        oracle_type: "raydium_cpmm",
        account_lut_id: 0,
        account_lut_index: 1,
        account: "<RAYDIUM_CPMM_POOL>",
        weight_bps: 3000,
        is_required: false,
        conf_thresh_bps: 300,
        volatility_thresh_bps: 300,
        max_slippage_bps: 1500,
        min_liquidity: 1000000,
        staleness_thresh: 300,
        staleness_conf_rate_bps: 100,
        token_decimals: 9,
        twap_seconds_ago: 300,
        twap_secondary_seconds_ago: 60,
        quote_token: "usdc",
      },
    ],
  }
);