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 implementation
  • Ownable: Access control for owner-only functions
  • ReentrancyGuard: 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 be address(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 > minDepositAmount
  • depositAmount <= maxDepositAmount
  • totalPoolValue + depositAmount <= maxTotalValueLocked
  • asset == 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 underwriterAddresses array
  • Emits AssetDeposited event

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 deposit
  • underwriterAddress: Address of the underwriter providing collateral

Requirements:

  • depositAmount > minDepositAmount
  • depositAmount <= maxDepositAmount
  • totalPoolValue + depositAmount <= maxTotalValueLocked
  • asset == INSURED_TOKEN
  • underwriterAddress must 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 insuredDepositMapped array with:
    • Amount, pool time, underwriter address
    • Assets in vault (for yield calculation)
    • USD value of deposit (if oracle available)
  • Updates insuredTokenBalance
  • Emits AssetDeposited event

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 withdraw
  • preferredAsset: Asset to withdraw (INSURED_TOKEN or underwriteToken)

Requirements:

  • Valid deposit index
  • Deposit must not already be withdrawn
  • If preferredAsset == underwriteToken, must meet minimumPoolTime requirement
  • preferredAsset must be either INSURED_TOKEN or underwriteToken

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 insuredWithdrawal event

Fee Calculation: Fees are calculated based on yield earned since deposit:

  • yieldEarned = currentAssetsAmount - assetsInVault
  • commission = yieldEarned * commissionRate / 10000
  • poolFee = yieldEarned * poolFee / 10000
  • protocolFee = 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 withdraw
  • preferredAsset: Asset to withdraw (must be underwriter token)

Requirements:

  • totalTokensToWithdraw > 0
  • preferredAsset == underwriteToken
  • Must have sufficient unlocked tokens
  • If unlock process started, must wait for unlockDuration to pass

Effects:

  • Automatically unlocks tokens if unlock period has passed
  • Burns underwriter receipt tokens
  • Updates totalUnderwriteTokenBalance
  • Transfers tokens to underwriter
  • Emits ColleteralWithdraw event

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 UnlockProcessStarted event

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 insuredTokenBalance by fee amount
  • Resets accumulatedPoolFee to 0
  • Emits PoolFeePaid event

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 insuredTokenBalance by fee amount
  • Resets accumulatedProtocolFee to 0
  • Emits ProtocolFeePaid event

payCommission

Pays out accumulated commission to the caller (underwriter).

function payCommission() external nonReentrant

Effects:

  • Transfers accumulated commission to msg.sender
  • Reduces insuredTokenBalance by commission amount
  • Resets commissionAmount for the caller to 0
  • Emits CommissionPaid event

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 deposit
  • insuredAddress: 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 RewardsClaimed event

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 amount
  • lockedAmount: Amount currently locked as collateral
  • lockedUntil: 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

  1. Commission: Paid to underwriters from yield earned on insured deposits

    • Rate: Set per pool (immutable)
    • Calculated: yieldEarned * commissionRate / 10000
  2. Pool Fee: Paid to pool creator from yield earned

    • Rate: Set per pool (immutable)
    • Calculated: yieldEarned * poolFee / 10000
  3. 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:

  1. At Deposit: Records assetsInVault - the base assets in the vault
  2. Over Time: Calculates current assets using YieldBearingTokenAdapter.previewRedeem()
  3. Yield Earned: currentAssetsAmount - assetsInVault
  4. 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 deposited
  • insuredWithdrawal: When insured assets are withdrawn
  • ColleteralWithdraw: When underwriter assets are withdrawn
  • RewardsClaimed: When rewards are claimed
  • PoolFeePaid: When pool fees are paid
  • ProtocolFeePaid: When protocol fees are paid
  • CommissionPaid: When commissions are paid
  • UnlockProcessStarted: When unlock process begins
  • ParameterUpdated: 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

  1. Multiple Deposits: Users can make multiple deposits, each tracked separately with an index
  2. Receipt Tokens: Receipt tokens are ERC20 tokens that represent positions and can be traded
  3. Lock Mechanism: Underwriter tokens are locked when backing deposits and require an unlock process to withdraw
  4. Minimum Pool Time: Insured users must wait minimumPoolTime before withdrawing in underwriter tokens
  5. Fee-on-Transfer Tokens: Supported through balance-delta pattern
  6. Yield Calculation: Based on time elapsed and vault's yield mechanism
  7. Immutable Parameters: Commission rate, pool fee, and collateral ratio cannot be changed after deployment