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 Points | Percentage |
|---|---|
| 100 | 1% |
| 500 | 5% |
| 1000 | 10% |
| 2500 | 25% |
| 5000 | 50% |
| 10000 | 100% |
The constant BPS_SCALE = 10000 represents 100%.
Fee Bounds
The protocol enforces strict bounds on fee parameters:
| Parameter | Minimum | Maximum |
|---|---|---|
| Commission Rate | 1% (100 bps) | 50% (5000 bps) |
| Pool Fee | 0% (0 bps) | 20% (2000 bps) |
| Protocol Fee | 0% | 10% (1000 bps); default 1% (100 bps) |
| Collateral Ratio | 100% (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 pricevalueAtLastClaim= storedvalueAtDepositfrom last claim
Example Calculation
A shielded user deposits 100 tokens worth $1,000:
- Initial state: 100 tokens, valueAtDeposit = $1,000
- After yield: 100 tokens now worth $1,050 (5% yield)
- Yield earned: $1,050 - $1,000 = $50
Fee distribution (assuming 20% commission, 5% pool fee, 2% protocol fee):
| Fee Type | Rate | Amount |
|---|---|---|
| Commission | 20% | $10.00 |
| Pool Fee | 5% | $2.50 |
| Protocol Fee | 2% | $1.00 |
| Net to User | 73% | $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 withClaimRewardsCooldownNotMet - Reduces position
amountby the fee tokens (commission, pool fee, protocol fee) - Updates
valueAtDepositandlastFeeClaimTimeto 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);