SplitRiskPool
Splits the risk into two different tranches
Overview
The SplitRiskPool contract is the core insurance pool contract in the YieldShield protocol. It manages deposits, withdrawals, fee calculations, and yield distribution between insured users and underwriters. Each pool instance represents a specific insurance market for a pair of tokens.
Purpose
The SplitRiskPool contract enables:
- Insurance Coverage: Users can deposit yield-bearing tokens to get insurance against depegging
- Underwriting: Users can provide collateral tokens to back insured deposits and earn commissions
- Yield Sharing: Automatic calculation and distribution of yield between parties
- Flexible Withdrawals: Insured users can withdraw in either insured or underwriter tokens
- Fee Management: Separate fees for pool creators, protocol, and underwriter commissions
Key Concepts
- Insured Tokens: Yield-bearing tokens that users want to insure (e.g., ERC4626 vault tokens, Aave aTokens)
- Underwriter Tokens: Collateral tokens provided by underwriters to back insured deposits
- Receipt Tokens: ERC20 tokens minted to represent deposits (separate tokens for insured and underwriter positions)
- Collateral Ratio: The ratio of underwriter tokens required to back insured deposits (e.g., 150% = 1.5x)
- Commission: Yield earned by underwriters for providing insurance coverage
- Pool Fees: Fees collected by pool creators and the protocol
- Pool Time: Timestamp tracking for yield calculations
- Lock Mechanism: Underwriter tokens are locked when backing deposits
Contract Details
Inheritance
ISplitRiskPool: Interface implementationOwnable: Access control for owner-only functionsReentrancyGuard: Protection against reentrancy attacks
Immutable Parameters
Set once during construction and cannot be changed:
address public immutable INSURED_RECEIPT_TOKEN; // Receipt token for insured positions
address public immutable UNDERWRITER_RECEIPT_TOKEN; // Receipt token for underwriter positions
address public immutable INSURED_TOKEN; // Insured yield-bearing token
uint256 public immutable COMMISSION_RATE; // Commission rate (basis points)
uint256 public immutable POOL_FEE; // Pool creator fee (basis points)
address public immutable POOL_CREATOR; // Pool creator address
uint256 public immutable COLLATERAL_RATIO; // Collateral ratio (basis points)
uint16 public immutable INSURED_TOKEN_TYPE; // Token type (0: ERC4626, 1: AToken, 2: Aave)
address public immutable INSURED_VAULT_ADDRESS; // Vault address for insured token
uint16 public immutable UNDERWRITER_TOKEN_TYPE; // Underwriter token type
address public immutable UNDERWRITER_VAULT_ADDRESS; // Vault address for underwriter token
address public immutable PRICE_ORACLE; // Price oracle address (optional)
Configurable Parameters
Managed through structs and can be updated by governance:
struct PoolConfig {
uint256 minDepositAmount; // Minimum deposit amount
uint256 maxDepositAmount; // Maximum single deposit
uint256 maxTotalValueLocked; // Maximum TVL
uint256 minDepositDuration; // Minimum deposit duration
uint256 minimumPoolTime; // Minimum time before withdrawal
uint256 unlockDuration; // Unlock period for underwriter tokens
uint256 protocolFee; // Protocol fee rate (basis points)
address protocolFeeRecipient; // Protocol fee recipient
address governanceTimelock; // Governance timelock address
}
struct PoolState {
uint256 insuredTokenBalance; // Total insured tokens in pool
uint256 totalUnderwriteTokenBalance; // Total underwriter tokens in pool
}
struct PoolMetadata {
string poolName; // Pool name
string poolSymbol; // Pool symbol
address underwriteToken; // Underwriter token address
}
Constructor
constructor(
TokenWhitelistLib.TokenInfo memory _insuredTokenInfo,
TokenWhitelistLib.TokenInfo memory _underwriteTokenInfo,
uint256 _commissionRate,
uint256 _poolFee,
address _poolCreator,
uint256 _collateralRatio,
address _governanceTimelock,
address _priceOracle
) Ownable(msg.sender)
Parameters:
_insuredTokenInfo: TokenInfo struct for insured token (address, symbol, vault, type)_underwriteTokenInfo: TokenInfo struct for underwriter token_commissionRate: Commission rate in basis points (e.g., 100 = 1%)_poolFee: Pool creator fee in basis points_poolCreator: Address of the pool creator (receives pool fees)_collateralRatio: Collateral ratio in basis points (e.g., 15000 = 150%)_governanceTimelock: Governance timelock address_priceOracle: Price oracle address (can beaddress(0))
Effects:
- Deploys receipt token contracts (insured and underwriter)
- Transfers receipt token ownership to this pool
- Initializes pool configuration with default values
- Sets immutable parameters
Core Functions
Deposit Functions
depositUnderwriteAsset
Deposits underwriter tokens to provide collateral for the pool.
function depositUnderwriteAsset(
address asset,
uint256 depositAmount
) external nonReentrant
Parameters:
asset: Address of the underwriter token (must match pool's underwriter token)depositAmount: Amount to deposit
Requirements:
depositAmount > minDepositAmountdepositAmount <= maxDepositAmounttotalPoolValue + depositAmount <= maxTotalValueLockedasset == underwriteToken
Effects:
- Transfers tokens from caller to pool (handles fee-on-transfer tokens)
- Mints underwriter receipt tokens to caller (1:1 ratio)
- Updates
totalUnderwriteTokenBalance - Tracks underwriter address in
underwriterAddressesarray - Emits
AssetDepositedevent
Example:
// Approve first
IERC20(underwriterToken).approve(poolAddress, amount);
// Deposit
pool.depositUnderwriteAsset(underwriterToken, amount);
depositInsuredAsset
Deposits insured tokens to get insurance coverage.
function depositInsuredAsset(
address asset,
uint256 depositAmount,
address underwriterAddress
) external nonReentrant
Parameters:
asset: Address of the insured token (must match pool's insured token)depositAmount: Amount to depositunderwriterAddress: Address of the underwriter providing collateral
Requirements:
depositAmount > minDepositAmountdepositAmount <= maxDepositAmounttotalPoolValue + depositAmount <= maxTotalValueLockedasset == INSURED_TOKENunderwriterAddressmust have sufficient unlocked tokens- Required collateral =
equivalentUnderwriterAmount * collateralRatio / 10000
Effects:
- Transfers tokens from caller to pool
- Locks required amount of underwriter tokens
- Mints insured receipt tokens to caller (1:1 ratio)
- Records deposit in
insuredDepositMappedarray with:- Amount, pool time, underwriter address
- Assets in vault (for yield calculation)
- USD value of deposit (if oracle available)
- Updates
insuredTokenBalance - Emits
AssetDepositedevent
Example:
// Approve first
IERC20(insuredToken).approve(poolAddress, amount);
// Deposit with specific underwriter
pool.depositInsuredAsset(insuredToken, amount, underwriterAddress);
Withdrawal Functions
insuredWithdraw
Withdraws insured deposits. Users can choose to receive either insured tokens or underwriter tokens.
function insuredWithdraw(
uint256 withdrawIndex,
address preferredAsset
) external nonReentrant
Parameters:
withdrawIndex: Index of the deposit to withdrawpreferredAsset: Asset to withdraw (INSURED_TOKENorunderwriteToken)
Requirements:
- Valid deposit index
- Deposit must not already be withdrawn
- If
preferredAsset == underwriteToken, must meetminimumPoolTimerequirement preferredAssetmust be eitherINSURED_TOKENorunderwriteToken
Effects:
- Calculates and stores fees (commission, pool fee, protocol fee)
- Burns insured receipt tokens
- Unlocks corresponding underwriter tokens
- Transfers preferred asset to user
- If withdrawing underwriter tokens:
- Uses USD value at deposit time for conversion
- Converts to current underwriter token price
- Sets
isWithdrawn = true - Emits
insuredWithdrawalevent
Fee Calculation: Fees are calculated based on yield earned since deposit:
yieldEarned = currentAssetsAmount - assetsInVaultcommission = yieldEarned * commissionRate / 10000poolFee = yieldEarned * poolFee / 10000protocolFee = yieldEarned * protocolFee / 10000
Example:
// Withdraw in insured token
pool.insuredWithdraw(0, insuredToken);
// Or withdraw in underwriter token (after minimum pool time)
pool.insuredWithdraw(0, underwriterToken);
underwriterWithdraw
Withdraws underwriter tokens from the pool.
function underwriterWithdraw(
uint256 totalTokensToWithdraw,
address preferredAsset
) external nonReentrant
Parameters:
totalTokensToWithdraw: Amount of tokens to withdrawpreferredAsset: Asset to withdraw (must be underwriter token)
Requirements:
totalTokensToWithdraw > 0preferredAsset == underwriteToken- Must have sufficient unlocked tokens
- If unlock process started, must wait for
unlockDurationto pass
Effects:
- Automatically unlocks tokens if unlock period has passed
- Burns underwriter receipt tokens
- Updates
totalUnderwriteTokenBalance - Transfers tokens to underwriter
- Emits
ColleteralWithdrawevent
Example:
// Start unlock process first
pool.startUnlockProcess();
// Wait for unlock duration...
// Then withdraw
pool.underwriterWithdraw(amount, underwriterToken);
startUnlockProcess
Initiates the unlock process for underwriter tokens. Must be called before withdrawing locked tokens.
function startUnlockProcess() external nonReentrant
Requirements:
- Caller must have underwriter tokens deposited
- Unlock process must not already be started
Effects:
- Sets
lockedUntil = block.timestamp + unlockDuration - Emits
UnlockProcessStartedevent
Note: After the unlock period, tokens are automatically unlocked when underwriterWithdraw is called.
Fee Management Functions
payPoolFee
Pays out accumulated pool fees to the pool creator. Can be called by anyone.
function payPoolFee() external nonReentrant
Effects:
- Transfers accumulated pool fees to
POOL_CREATOR - Reduces
insuredTokenBalanceby fee amount - Resets
accumulatedPoolFeeto 0 - Emits
PoolFeePaidevent
payProtocolFee
Pays out accumulated protocol fees to the protocol fee recipient. Can be called by anyone.
function payProtocolFee() external nonReentrant
Effects:
- Transfers accumulated protocol fees to
protocolFeeRecipient - Reduces
insuredTokenBalanceby fee amount - Resets
accumulatedProtocolFeeto 0 - Emits
ProtocolFeePaidevent
payCommission
Pays out accumulated commission to the caller (underwriter).
function payCommission() external nonReentrant
Effects:
- Transfers accumulated commission to
msg.sender - Reduces
insuredTokenBalanceby commission amount - Resets
commissionAmountfor the caller to 0 - Emits
CommissionPaidevent
Example:
// Underwriter claims their commission
pool.payCommission();
claimRewards
Claims rewards for an insured deposit. Can be called by anyone.
function claimRewards(
uint256 index,
address insuredAddress
) external nonReentrant
Parameters:
index: Index of the insured depositinsuredAddress: Address of the insured user
Effects:
- Calculates and stores fees (commission, pool fee, protocol fee)
- Reduces insured deposit amount by total fees
- Unlocks corresponding underwriter tokens proportionally
- Emits
RewardsClaimedevent
Use Case:
- Allows anyone to claim rewards on behalf of insured users
- Useful for automated reward claiming services
View Functions
getUserTokenBalances
Returns a user's receipt token balances.
function getUserTokenBalances(address user)
external
view
returns (uint256 insuredBalance, uint256 underwriterBalance)
getPoolBalances
Returns the current pool balances.
function getPoolBalances()
external
view
returns (uint256 insuredTokenPoolBalance, uint256 totalUnderwriteTokenPoolBalance)
getUnderwriterDepositInfo
Returns detailed information about an underwriter's deposit.
function getUnderwriterDepositInfo(address underwriterAddress)
external
view
returns (
uint256 amount,
uint256 lockedAmount,
uint64 lockedUntil,
uint256 commissionAmount
)
Returns:
amount: Total deposited amountlockedAmount: Amount currently locked as collaterallockedUntil: Timestamp when tokens will be unlocked (0 = unlocked, 1 = not started)commissionAmount: Accumulated commission available for payout
getInsuredDepositInfo
Returns information about a specific insured deposit.
function getInsuredDepositInfo(address insuredAddress, uint256 depositIndex)
external
view
returns (
uint256 amount,
uint64 poolTime,
address underwriterAddress,
bool isWithdrawn
)
getInsuredDepositCount
Returns the number of deposits for an insured user.
function getInsuredDepositCount(address insuredAddress)
external
view
returns (uint256 count)
getAllUnderwriters
Returns an array of all underwriter addresses.
function getAllUnderwriters() external view returns (address[] memory underwriters)
getPoolConfig
Returns the pool configuration struct.
function getPoolConfig() external view returns (
uint256 minDepositAmount,
uint256 maxDepositAmount,
uint256 maxTotalValueLocked,
uint256 minDepositDuration,
uint256 minimumPoolTime,
uint256 unlockDuration,
uint256 protocolFee,
address protocolFeeRecipient,
address governanceTimelock
)
getPoolState
Returns the pool state struct.
function getPoolState() external view returns (
uint256 insuredTokenBalance,
uint256 totalUnderwriteTokenBalance
)
getPoolMetadata
Returns the pool metadata struct.
function getPoolMetadata() external view returns (
string memory poolName,
string memory poolSymbol,
address underwriteToken
)
Preview Functions
Preview functions allow users to simulate operations without executing them.
previewDepositUnderwriteAsset
Preview the outcome of depositing underwriter assets.
function previewDepositUnderwriteAsset(address asset, uint256 depositAmount)
external
view
returns (
uint256 receiptTokens,
uint256 newUnderwriterBalance,
uint256 newTotalTvl
)
previewDepositInsuredAsset
Preview the outcome of depositing insured assets.
function previewDepositInsuredAsset(
address asset,
uint256 depositAmount,
address underwriterAddress
)
external
view
returns (
uint256 receiptTokens,
uint256 assetsInVault,
uint256 newInsuredBalance,
uint256 newTotalTvl,
uint256 lockedUnderwriterAmount
)
previewInsuredWithdraw
Preview the outcome of insured withdrawal.
function previewInsuredWithdraw(
uint256 withdrawIndex,
address preferredAsset,
address userAddress
)
external
view
returns (
uint256 withdrawAmount,
uint256 commissionAmount,
uint256 poolFeeAmount,
uint256 protocolFeeAmount,
uint256 newInsuredBalance,
uint256 newTotalTvl
)
previewUnderwriterWithdraw
Preview the outcome of underwriter withdrawal.
function previewUnderwriterWithdraw(
uint256 totalTokensToWithdraw,
address preferredAsset,
address userAddress
)
external
view
returns (
uint256 withdrawAmount,
uint256 newUnderwriterBalance,
uint256 newTotalTvl
)
Governance Functions
All governance functions are only callable by the governance timelock contract.
setProtocolFee
Updates the protocol fee rate.
function setProtocolFee(uint256 newProtocolFee) external
Requirements:
- Caller must be governance timelock
newProtocolFee <= 1000(10%)
setProtocolFeeRecipient
Updates the protocol fee recipient address.
function setProtocolFeeRecipient(address newProtocolFeeRecipient) external
Requirements:
- Caller must be governance timelock
- New recipient must not be zero address
setMinDepositAmount
Updates the minimum deposit amount.
function setMinDepositAmount(uint256 newMinDepositAmount) external
Requirements:
- Caller must be governance timelock
newMinDepositAmount > 0
setMaxDepositAmount
Updates the maximum deposit amount.
function setMaxDepositAmount(uint256 newMaxDepositAmount) external
Requirements:
- Caller must be governance timelock
newMaxDepositAmount > minDepositAmount
setMaxTotalValueLocked
Updates the maximum total value locked.
function setMaxTotalValueLocked(uint256 newMaxTotalValueLocked) external
Requirements:
- Caller must be governance timelock
newMaxTotalValueLocked > 0
setMinDepositDuration
Updates the minimum deposit duration.
function setMinDepositDuration(uint256 newMinDepositDuration) external
Requirements:
- Caller must be governance timelock
newMinDepositDuration <= 365 days
setUnlockDuration
Updates the unlock duration for underwriter tokens.
function setUnlockDuration(uint256 newUnlockDuration) external
Requirements:
- Caller must be governance timelock
1 days <= newUnlockDuration <= 365 days
setMinimumPoolTime
Updates the minimum time assets must stay in pool before withdrawal.
function setMinimumPoolTime(uint256 newMinimumPoolTime) external
Requirements:
- Caller must be governance timelock
1 hours <= newMinimumPoolTime <= 30 days
setGovernanceTimelock (Owner Only)
Updates the governance timelock address.
function setGovernanceTimelock(address newGovernanceTimelock) external onlyOwner
Requirements:
- Caller must be contract owner
- New address must not be zero
Security Features
Reentrancy Protection
All state-changing functions are protected with nonReentrant modifier from OpenZeppelin's ReentrancyGuard.
Access Control
- Owner: Can update governance timelock address
- Governance Timelock: Can update pool parameters and protocol settings
- Pool Creator: Receives pool fees
- Protocol Fee Recipient: Receives protocol fees
- Anyone: Can deposit, withdraw, and claim fees
Input Validation
- All addresses are validated to be non-zero where required
- Amounts are validated against min/max limits
- Deposit indices are validated before array access
- Asset addresses are validated against supported tokens
Safe Token Transfers
All token transfers use OpenZeppelin's SafeERC20 library to handle tokens that don't return boolean values.
Balance-Delta Pattern
Deposit functions use balance-delta pattern to support fee-on-transfer tokens:
uint256 beforeBal = IERC20(asset).balanceOf(address(this));
SafeERC20.safeTransferFrom(IERC20(asset), msg.sender, address(this), depositAmount);
uint256 afterBal = IERC20(asset).balanceOf(address(this));
uint256 received = afterBal - beforeBal;
Fee Structure
Fee Types
-
Commission: Paid to underwriters from yield earned on insured deposits
- Rate: Set per pool (immutable)
- Calculated:
yieldEarned * commissionRate / 10000
-
Pool Fee: Paid to pool creator from yield earned
- Rate: Set per pool (immutable)
- Calculated:
yieldEarned * poolFee / 10000
-
Protocol Fee: Paid to protocol from yield earned
- Rate: Configurable by governance (default: 100 basis points = 1%)
- Calculated:
yieldEarned * protocolFee / 10000
Fee Payment
Fees are accumulated over time and can be claimed:
- Pool Fees: Anyone can call
payPoolFee()to pay pool creator - Protocol Fees: Anyone can call
payProtocolFee()to pay protocol - Commissions: Underwriters call
payCommission()to claim their earnings
Yield Calculation
The protocol calculates yield based on the underlying vault's yield mechanism:
- At Deposit: Records
assetsInVault- the base assets in the vault - Over Time: Calculates current assets using
YieldBearingTokenAdapter.previewRedeem() - Yield Earned:
currentAssetsAmount - assetsInVault - Fee Distribution: Fees are calculated from yield earned
Supported Token Types
- ERC4626: Standard vault tokens
- Aave aTokens: Aave lending protocol tokens
- Custom: Extensible through adapter pattern
Events
The contract emits comprehensive events for all major operations:
AssetDeposited: When assets are depositedinsuredWithdrawal: When insured assets are withdrawnColleteralWithdraw: When underwriter assets are withdrawnRewardsClaimed: When rewards are claimedPoolFeePaid: When pool fees are paidProtocolFeePaid: When protocol fees are paidCommissionPaid: When commissions are paidUnlockProcessStarted: When unlock process beginsParameterUpdated: When governance parameters are updated
Usage Examples
Depositing as Underwriter
// Approve tokens first
IERC20(underwriterToken).approve(poolAddress, amount);
// Deposit
pool.depositUnderwriteAsset(underwriterToken, amount);
Depositing as Insured User
// Approve tokens first
IERC20(insuredToken).approve(poolAddress, amount);
// Deposit with specific underwriter
pool.depositInsuredAsset(insuredToken, amount, underwriterAddress);
Withdrawing Insured Deposit
// Withdraw in insured token
pool.insuredWithdraw(0, insuredToken);
// Or withdraw in underwriter token (after minimum pool time)
pool.insuredWithdraw(0, underwriterToken);
Claiming Rewards
// Claim rewards for a deposit (can be called by anyone)
pool.claimRewards(0, insuredUserAddress);
Withdrawing as Underwriter
// Start unlock process first
pool.startUnlockProcess();
// Wait for unlock duration...
// Then withdraw
pool.underwriterWithdraw(amount, underwriterToken);
Claiming Fees
// Pool creator claims pool fees
pool.payPoolFee();
// Protocol claims protocol fees
pool.payProtocolFee();
// Underwriter claims commission
pool.payCommission();
Important Notes
- Multiple Deposits: Users can make multiple deposits, each tracked separately with an index
- Receipt Tokens: Receipt tokens are ERC20 tokens that represent positions and can be traded
- Lock Mechanism: Underwriter tokens are locked when backing deposits and require an unlock process to withdraw
- Minimum Pool Time: Insured users must wait
minimumPoolTimebefore withdrawing in underwriter tokens - Fee-on-Transfer Tokens: Supported through balance-delta pattern
- Yield Calculation: Based on time elapsed and vault's yield mechanism
- Immutable Parameters: Commission rate, pool fee, and collateral ratio cannot be changed after deployment