Shield Pool
A Shield Pool is where shielded users deposit yield-bearing assets to get protection, and protectors deposit collateral to back that protection and earn commission. This page describes how deposits, withdrawals, and fee flows work: who can do what, when, and what they receive. Whether you want to shield your assets or provide protection and earn, the sections below cover the mechanics.
Overview
A Shield Pool creates a protection relationship between two types of participants:
- Shielded Users: Deposit yield-bearing tokens to receive protection against value loss
- Protectors: Deposit collateral tokens to earn commission from the yield generated by shielded assets
The pool manages deposits, withdrawals, yield distribution, and ensures that protector collateral is always sufficient to cover shielded deposits. Collateralization is based on original deposit values (valueAtDeposit), not current token amounts, ensuring symmetry between shielded users and protectors.
Core Data Structures
ShieldedDeposit (ERC721 NFT Position)
Each shielded deposit is represented as an ERC721 NFT token. The position data is stored in the ShieldReceiptNFT contract:
tokenId: Unique identifier for the NFT positionamount: Current shielded token balance in the pool (reduces as fees are claimed)depositTime: Timestamp when the deposit was made (used for yield calculations and transfer locks)valueAtDeposit: USD value of the deposit at time of deposit (for cross-asset withdrawals)collateralAmount: Original collateral in backing token terms (for cross-asset withdrawal cap)lastFeeClaimTime: Last time fees were calculatedisWithdrawn: Flag indicating if the deposit has been withdrawn
ProtectorPosition (ProtectorReceiptNFT)
Each protector position is an ERC721 NFT with:
amount: Total backing tokens depositeddepositTime: Timestamp when the deposit was madeunlockRequestTime: When the unlock will complete (0 = not started). Set bystartUnlockProcess(tokenId)toblock.timestamp + unlockDuration. Withdrawal is allowed whenunlockRequestTime != 0andunlockRequestTime <= block.timestamp
Commission is tracked in the pool using a share-based (rewards-per-share) pattern. Protectors claim via the pool's claimCommission(tokenId); getClaimableCommission(tokenId) returns the claimable amount.
PoolConfig
Governance-controlled pool parameters:
minDepositAmount: Minimum deposit allowedmaxDepositAmount: Maximum single depositmaxTotalValueLocked: Maximum total value locked in the poolminimumPoolTime: Minimum time assets must stay before cross-asset (protector-token) withdrawal (default 1 day; max 90 days perMAX_MINIMUM_POOL_TIME)unlockDuration: Duration of the unlock period for protector withdrawals (default 28 days; bounds 1-365 days perConstantsLib)protocolFee: Protocol fee rate (basis points)protocolFeeRecipient: Address receiving protocol feespriceOracle: Oracle contract for token valuations
PoolState
Current pool balances:
insuredTokenBalance: Total shielded tokens held in the pooltotalUnderwriteTokenBalance: Sum of all backing token balances
Main Operations
Deposits
depositUnderwriteAsset(asset, depositAmount, minReceivedAmount): Protectors deposit collateral tokens
assetmust beUNDERWRITER_TOKENminReceivedAmount: Slippage protection for fee-on-transfer tokens (viaSlippageLib.enforceMinReceived)- Validates deposit amount and TVL limits
- Mints protector receipt NFT (ERC721) representing the collateral position
- Returns a unique
tokenIdfor the NFT
depositInsuredAsset(asset, depositAmount, minReceivedAmount): Shielded users deposit assets to be protected
assetmust beINSURED_TOKENminReceivedAmount: Slippage protection for fee-on-transfer tokens- Requires sufficient protector collateral (capacity check via oracle)
- Calculates and stores
valueAtDeposit(USD) andcollateralAmount(for cross-asset cap) - Mints shielded receipt NFT (ERC721) representing the shielded position
- Returns a unique
tokenIdfor the NFT - Collateralization is based on original deposit values, not current token amounts
Withdrawals
insuredWithdraw(tokenId, preferredAsset, minAmountOut): Shielded users withdraw their deposits
preferredAsset:INSURED_TOKEN(yield minus fees) orUNDERWRITER_TOKEN(principal only, i.e. cross-asset / "activate shield")minAmountOut: Slippage protection- Cross-asset (protector) requires
minimumPoolTimesincedepositTime - For cross-asset: uses
valueAtDepositandgetPriceWithCircuitBreaker(UNDERWRITER_TOKEN); payout is capped bypos.collateralAmount - Calculates and accumulates fees (commission, pool fee, protocol fee) then burns the receipt NFT
partialWithdrawInsured(tokenId, withdrawAmount, preferredAsset, minAmountOut) -> newTokenId: Partial withdrawal from a shielded position
preferredAssetmust beINSURED_TOKEN(partial does not support cross-asset)- Burns the old NFT and mints a new one with the remainder; preserves
depositTime - Runs
_calculateAndAccumulateFeeson the full position; enforcesremaining >= minDepositAmountafter fees
underwriterWithdraw(tokenId, amount, preferredAsset, minAmountOut): Protectors withdraw their collateral
preferredAssetmust beUNDERWRITER_TOKEN- Can withdraw only the available amount (subject to utilization lock). Use
getAvailableForWithdrawal(tokenId) - If unlock was requested and
unlockRequestTime <= block.timestamp, the full position can be withdrawn (subject to utilization) - On partial withdrawal, the pool auto-claims pending commission before updating the position
- Burns the receipt NFT on full withdrawal
startUnlockProcess(tokenId): Initiates the unlock period for backing tokens
- Sets
unlockRequestTime = block.timestamp + unlockDuration(time when unlock completes) - Withdrawal is allowed when
unlockRequestTime != 0andunlockRequestTime <= block.timestamp
cancelUnlockProcess(tokenId): Cancels an active unlock
- Resets
unlockRequestTimeto 0. Only the NFT owner can call.
Yield and Fees
claimRewards(tokenId): Calculates and accumulates fees for a position (does not transfer to recipients; those are paid via claimCommission, payPoolFee, payProtocolFee)
- 24-hour cooldown per
tokenId; earlier calls revert withClaimRewardsCooldownNotMet - Reduces position
amountby fees and updatesvalueAtDepositandlastFeeClaimTime - Can be called by anyone for any position
payPoolFee(): Pool creator or governance claims accumulated pool fees. See Fee Structure.
payProtocolFee(): Protocol fee recipient or governance claims accumulated protocol fees.
claimCommission(tokenId): Protector claims accumulated commission (in shielded tokens). Commission is tracked in the pool via the rewards-per-share pattern; use getClaimableCommission(tokenId) for the claimable amount.
For a complete breakdown of fee calculations, basis points, and distribution mechanics, see Fee Structure.
Access Control
Pools can optionally implement access control through the IPoolAccessControl interface:
- Pool creator can set an access control contract
- If set, all deposit/withdraw operations check permissions
- If
address(0), no restrictions (public pool) - Allows for private pools with custom access logic
Receipt NFTs (ERC721 NFTs)
Each pool uses two ERC721 NFT contracts to represent positions:
-
ShieldReceiptNFT: Represents shielded deposits as non-fungible tokens
- Each deposit mints a unique NFT with a
tokenId - NFT contains position data (amount, deposit time, value at deposit, etc.)
- NFTs can be transferred (subject to transfer lock period)
- Supports standard ERC721 operations (
balanceOf,tokenOfOwnerByIndex,ownerOf, etc.)
- Each deposit mints a unique NFT with a
-
ProtectorReceiptNFT: Represents protector collateral as non-fungible tokens
- Each deposit mints a unique NFT with a
tokenId - NFT contains position data (
amount,depositTime,unlockRequestTime). Commission is tracked in the pool, not in the NFT. - NFTs can be transferred (subject to transfer lock period)
- Supports standard ERC721 operations
- Each deposit mints a unique NFT with a
Receipt NFTs are ERC721 tokens, allowing for:
- Unique identification of each position via
tokenId - Transferability of positions (with optional transfer locks)
- Integration with NFT marketplaces and DeFi protocols
- On-chain position data storage within the NFT contract
For detailed information about NFT mechanics, transfer locks, and integration opportunities, see Receipt NFTs.
View Functions
getAvailableForWithdrawal(tokenId): Maximum protector amount withdrawable (accounts for utilization lock)getLockedAmount(tokenId): Amount locked for a protector positiongetUtilizationRatio()/getUtilizationRatioUsd(): Ratio of required collateral to protector value (token-based or USD-based)getReservedFees(): Total fees reserved (pool fee, protocol fee, commissions); not withdrawable by usersgetWithdrawableBalance(): Shielded token balance available for user withdrawals (excludes reserved fees)getClaimableCommission(tokenId): Claimable commission for a protector positiongetShieldDepositInfo(tokenId)/getProtectorDepositInfo(tokenId): Position data and metadatagetOracleInfo(): Oracle address and dual-feed status (if CompositeOracle)getUserNFTCounts(user): Shielded and protector NFT counts per usergetPoolBalances():insuredTokenBalanceandtotalUnderwriteTokenBalance
Advanced / Governance
migrateExistingPosition(tokenId): Governance-only. Migrates a protector position to the rewards-per-share system (MasterChef); sets rewardDebt to 0 so historical rewards can be claimed. One-time per position.
Supporting Components
Libraries
- SlippageLib: Enforces minimum received amounts for slippage protection
- ErrorsLib: Custom errors for gas-efficient error handling
- EventsLib: Standardized events for off-chain indexing
- ConstantsLib: Protocol-wide constants (basis point scale, limits, etc.)
Base Contract
- ProtocolAccessControlUpgradeable: Provides governance controls, pausable functionality, and reentrancy protection (see Security Model)
Interfaces
- ISplitRiskPool: Defines the public interface for pool interactions
- IShieldReceiptNFT: Interface for shielded receipt NFT operations (mint, burn, getPosition, updatePosition)
- IProtectorReceiptNFT: Interface for protector receipt NFT operations
- IPriceOracle: Interface for token price and valuation queries
Upgradeability
Pools are deployed as UUPS (Universal Upgradeable Proxy Standard) proxies, allowing for:
- Future improvements without losing state
- Governance-controlled upgrades
- Implementation contract updates
The pool implementation contract is immutable, while the proxy can be upgraded by governance.
Collateralization Model
The pool uses a symmetric collateralization model based on original deposit values:
- Original Deposit Values: Each shielded deposit stores its USD value at deposit time (
valueAtDeposit) - Protector Requirements: Collateral requirements are calculated from the sum of all original deposit values (
totalValueAtDeposit), not current token amounts - Symmetric Protection:
- Shielded users can only claim their original
valueAtDepositwhen activating their shield - Protectors only need to collateralize original deposit values, not yield appreciation
- Shielded users can only claim their original
- Benefits:
- Shielded token price appreciation does not lock additional protector collateral
- Predictable collateral requirements for protectors
- Fair risk distribution between shielded users and protectors
This design ensures that both parties work with the same baseline: the original principal deposit value, creating a balanced protection relationship.