Fee Structure

When your shielded assets earn yield, that yield is shared between you (the shielded user), the protectors (who provide protection), the pool creator, and the protocol. This page explains who gets what, in what order, and how the rules are set. If you're depositing, you can think of it as: What do I keep and what goes to others?

Overview

When shielded assets generate yield, the fees are calculated and distributed as follows:

Loading diagram...

Fee Types

Commission (Protector Fee)

Rate: 1% - 50% of yield

The commission compensates protectors for providing collateral protection:

  • Set by pool creator at pool creation time
  • Cannot be changed after pool creation
  • Paid from yield generated by shielded assets
  • Accumulates in the pool until claimed via claimCommission(tokenId)

Commission uses a rewards-per-share (MasterChef) pattern in the pool: rewardPerShareAccumulated, rewardDebt, and commissionsClaimed per tokenId. claimCommission computes the claimable amount via _calculateClaimableCommission and pays in shielded tokens. Commission is tracked in the pool, not in the Protector Receipt NFT. When totalProtectorTokens == 0, commission is redirected to the protocol fee in _calculateAndAccumulateFees.

Why it matters: Higher commission rates attract more protectors but reduce returns for shielded users. Pool creators must balance this tradeoff.

Pool Fee (Creator Fee)

Rate: 0% - 20% of yield

The pool fee rewards pool creators for curating and managing pools:

  • Set by pool creator at pool creation time
  • Cannot be changed after pool creation
  • Incentivizes creation of well-designed pools
  • Accumulates until claimed via payPoolFee()
  • Callable by pool creator or governance (timelock)

Use cases:

  • DAOs creating pools for their communities
  • Protocols offering integrated protection
  • Curators matching optimal token pairs

Protocol Fee

Rate: Governance-configurable, capped at 10% (ConstantsLib.MAX_PROTOCOL_FEE = 1000 bps). Default is 1% (100 bps).

The protocol fee supports YieldShield development and operations:

  • Set by governance through the YS Governor
  • Can be updated via governance proposal
  • Accumulates until claimed via payProtocolFee()
  • Callable by protocol fee recipient or governance (timelock)
  • Sent to the protocol fee recipient address

Basis Point System

All fees use basis points (bps) for precision:

Basis PointsPercentage
1001%
5005%
100010%
250025%
500050%
10000100%

The constant BPS_SCALE = 10000 represents 100%.

Fee Bounds

The protocol enforces strict bounds on fee parameters:

ParameterMinimumMaximum
Commission Rate1% (100 bps)50% (5000 bps)
Pool Fee0% (0 bps)20% (2000 bps)
Protocol Fee0%10% (1000 bps); default 1% (100 bps)
Collateral Ratio100% (10000 bps)500% (50000 bps)

These bounds are validated in PoolValidationLib during pool creation:

function validatePoolParams(uint256 commissionRate, uint256 poolFee, uint256 collateralRatio) {
    // Commission: 1% to 50%
    if (commissionRate < 100 || commissionRate > 5000) {
        revert InvalidCommissionRate();
    }
    // Pool fee: 0% to 20%
    if (poolFee > 2000) {
        revert InvalidPoolFee();
    }
    // Collateral ratio: 100% to 500%
    if (collateralRatio < 10000 || collateralRatio > 50000) {
        revert InvalidCollateralRatio();
    }
}

Yield Calculation

Yield is calculated as the increase in USD value of the shielded position since the last fee claim:

yield = currentValue - valueAtLastClaim

Where:

  • currentValue = current amount x current USD price
  • valueAtLastClaim = stored valueAtDeposit from last claim

Example Calculation

A shielded user deposits 100 tokens worth $1,000:

  1. Initial state: 100 tokens, valueAtDeposit = $1,000
  2. After yield: 100 tokens now worth $1,050 (5% yield)
  3. Yield earned: $1,050 - $1,000 = $50

Fee distribution (assuming 20% commission, 5% pool fee, 2% protocol fee):

Fee TypeRateAmount
Commission20%$10.00
Pool Fee5%$2.50
Protocol Fee2%$1.00
Net to User73%$36.50

Fee Operations

claimRewards

Calculates and accumulates fees for a position (it does not transfer funds to protector, pool creator, or protocol; those are paid via claimCommission, payPoolFee, and payProtocolFee):

function claimRewards(uint256 tokenId) external
  • Can be called by anyone for any position
  • 24-hour cooldown per tokenId (ConstantsLib.CLAIM_REWARDS_COOLDOWN = 1 day). Calls before cooldown revert with ClaimRewardsCooldownNotMet
  • Reduces position amount by the fee tokens (commission, pool fee, protocol fee)
  • Updates valueAtDeposit and lastFeeClaimTime to the new baseline for the next yield calculation
  • Adds fees to the pool's respective accumulators (commission, accumulatedPoolFee, accumulatedProtocolFee)

Note: This is typically called automatically before withdrawals, but can be called manually to realize gains.

claimCommission

Protectors claim their accumulated commission:

function claimCommission(uint256 tokenId) external
  • Only callable by position owner (NFT holder)
  • Transfers accumulated commission in shielded tokens to the caller
  • Uses the rewards-per-share pattern; when nothing is claimable, emits NoCommissionToClaim

payPoolFee

Pool creator or governance claims accumulated pool fees:

function payPoolFee() external
  • Callable by pool creator or governance (timelock)
  • Transfers accumulated pool fee tokens to the pool creator
  • Resets pool fee accumulator to zero

payProtocolFee

Protocol fee recipient or governance claims accumulated protocol fees:

function payProtocolFee() external
  • Callable by protocol fee recipient or governance (timelock)
  • Transfers accumulated protocol fee tokens to the protocol fee recipient
  • Resets protocol fee accumulator to zero

Fee Flow Diagram

Loading diagram...

Fee Accounting

Fees are tracked in the pool state. Commission uses a rewards-per-share (MasterChef) pattern; it is not stored in the Protector Receipt NFT:

// Commission (rewards-per-share pattern in the pool)
uint256 rewardPerShareAccumulated;   // Global accumulator
mapping(uint256 => uint256) rewardDebt;       // Per tokenId
mapping(uint256 => uint256) commissionsClaimed;  // Per tokenId

// Pool-level accumulators
uint256 accumulatedPoolFee;      // Total unclaimed pool fees
uint256 accumulatedProtocolFee;  // Total unclaimed protocol fees
uint256 accumulatedCommissions;  // Pending commissions for protectors

All fees are denominated in the shielded token, since that's where yield originates.

Strategic Considerations

For Pool Creators

When setting fee parameters, consider:

  • Higher commission -> More attractive to protectors, larger collateral pools
  • Lower commission -> More attractive to shielded users, higher net yields
  • Pool fee -> Your revenue, but reduces user returns

For Protectors

Evaluate pools based on:

  • Commission rate relative to risk
  • Shielded token yield expectations
  • Collateral ratio requirements

For Shielded Users

Compare pools on:

  • Net yield after all fees
  • Collateral ratio (protection strength)
  • Protector liquidity depth

Events

Fee-related events for off-chain tracking (align with EventsLib):

event RewardsClaimed(address indexed shieldedAddress, uint256 amount, address indexed asset);

event CommissionClaimed(address indexed recipient, uint256 indexed tokenId, uint256 amount);
event NoCommissionToClaim(address indexed recipient, uint256 indexed tokenId);

event PoolFeePaid(address indexed creator, uint256 amount);

event ProtocolFeePaid(address indexed recipient, uint256 amount);