Skip to content

06 premiums claims

Actions edited this page Apr 8, 2026 · 54 revisions

Premiums and Claims

Overview

Coverage economics are split into two operational paths:

  • premium path: continuous accrual in CoveredVaultWrapper against depositor principal → collectPremium() flush → fee split in PremiumManager → restaker reward routing,
  • claim path: withdrawal-time shortfall detection → fileClaim evaluation → token-native committee slashing → collateral conversion → beneficiary payout.

Premiums and claims both use the same policy/committee identity (policyId).

Policy actor note:

  • In this architecture, the policy buyer is expected to be the CoveredVaultWrapper or its operator contract.

CoveredVaultWrapper Integration Context

The covered integration uses a single CoveredVaultWrapper:

  • CoveredVaultWrapper: UUPS-upgradeable ERC-4626 vault that wraps a Morpho V2 vault; user-facing and premium-bearing.
  • Morpho Vault (underlying): yield source; wrapper is its sole depositor.

Users opt into insurance by depositing into the CoveredVaultWrapper rather than directly into the Morpho vault.

Deposit Flow (Covered Path)

sequenceDiagram
    participant User
    participant CVW as CoveredVaultWrapper
    participant MV as Morpho Vault

    User->>CVW: approve + deposit(assets, receiver)
    CVW->>CVW: _accruePremium() — checkpoint premium accumulator
    CVW->>CVW: record userPrincipal[receiver] += assets
    CVW->>MV: deposit(assets)
    MV-->>CVW: Morpho shares
    CVW-->>User: mint CoveredVaultWrapper shares
Loading

Accounting details:

  • userPrincipal[receiver] tracks cost-basis (not yield); premium accrues on principal only,
  • a per-user userPremiumDebt offset prevents the depositor from inheriting liability that accrued before they arrived.

Premium Flow

Components

  • CoveredVaultWrapper - accrues premiums internally; exposes collectPremium() as a permissionless flush.
  • PremiumManager - fee splitting and reward routing.
  • CoverPool - source of pool fee settings and fee recipient.
  • Core RewardsManager - downstream restaker reward distribution.

Premium Distribution Sequence

sequenceDiagram
    participant Anyone
    participant CVW as CoveredVaultWrapper
    participant PRM as PremiumManager
    participant Pool as CoverPool
    participant Treasury as Platform Treasury
    participant PoolRecipient as Pool Fee Recipient
    participant RM as Core RewardsManager
    participant SSP as SSPRouter

    Note over CVW: premium accrues continuously<br/>against totalPrincipal × premiumRatePerSecond
    Anyone->>CVW: collectPremium()
    CVW->>CVW: _accruePremium() — checkpoint accumulator
    CVW->>PRM: approve(pending premium)
    CVW->>PRM: distributePremium(pool, policyId, token, pending)
    PRM->>PRM: safeTransferFrom(CVW, PRM, amount)
    PRM->>Pool: read poolFeeBps + feeRecipient + owner
    PRM->>PRM: compute fee splits (retain restaker share in PRM)
    PRM->>Treasury: platform fee
    PRM->>PoolRecipient: pool fee
    PRM->>RM: distributeRewards(policyId, curator, restakerSplit, token, taskId)
    RM->>PRM: safeTransferFrom(premiumManager, rewardsManager, restakerShare)
    RM->>SSP: distributeRewards(..., tokenSource=rewardsManager)
    SSP->>RM: safeTransferFrom(rewardsManager, adapter, restakerShare)
Loading

Fee Split Logic

PremiumManager.getFeeSplits(grossPremium, platformFeeBps, poolFeeBps):

  • platformSplit = gross * platformFeeBps / 10_000
  • poolSplit = gross * poolFeeBps / 10_000
  • restakerSplit = gross - platformSplit - poolSplit

Guardrails:

  • platformFeeBps <= 2_500 (25% max for platform fee),
  • platformFeeBps + poolFeeBps <= 5_000 (combined fee cap of 50%).

Claim Flow

Components

  • ClaimManager - claim intake, evaluation, and settlement.
  • SpecRegistry + ISpec - payability decision logic. Only admin-approved ISpec implementations can be registered; once registered, a spec remains resolvable for existing policies regardless of subsequent revocation.
  • Core SlashingManager - committee slashing execution (token-native amounts, no USD conversion).
  • Swapper + quoteSwap - collateral token conversion and slippage-adjusted minimum output.

Claim Settlement Sequence

sequenceDiagram
    participant Claimer
    participant CM as ClaimManager
    participant SR as SpecRegistry
    participant Spec as ISpec
    participant SLM as Core SlashingManager
    participant SW as Swapper
    participant Beneficiary
    participant RM as RewardsManager

    Claimer->>CM: fileClaim(policyId, requestedAmount, evidenceHash, data)
    CM->>CM: validate claimApprovalRequired==false, claimer, window,<br/>remaining coverage, evidenceHash unique, !premiumDefaulted
    CM->>SR: resolveSpec(pool, specId)
    SR-->>CM: spec
    CM->>Spec: evaluate(context)
    Spec-->>CM: EvaluationResult

    alt non-payable
        CM-->>Claimer: claim rejected
    else payable
        CM->>SLM: previewSlashing(policyId, operator)
        SLM-->>CM: vaults[], tokens[], tokenStakes[] (token-native)
        CM->>SW: quoteSwap(tokenIn, payoutToken, stake) per cross-token vault
        CM->>CM: _computeVaultSlashes — proportional + slippage-inflated VaultSlash[]
        CM->>SLM: executeSlashing(policyId, operator, VaultSlash[], taskId)
        SLM-->>CM: collateralTokens[], collateralAmounts[]
        loop each collateral token
            alt token == payoutToken
                CM->>Beneficiary: transfer payout token (up to remainingPayout)
                CM->>RM: distributeRewards(surplus, if any)
            else token != payoutToken
                CM->>SW: executeSwap(tokenIn -> payoutToken, amountOutMin=quoteSwap×slippage)
                SW-->>CM: payoutToken amountOut
                CM->>Beneficiary: transfer payout token (up to remainingPayout)
                CM->>RM: distributeRewards(surplus, if any)
            end
        end
        CM->>CM: claimApprovalRequired[policyId] = true
    end
Loading

Withdrawal (No Shortfall / Happy Path)

sequenceDiagram
    participant User
    participant CVW as CoveredVaultWrapper
    participant MV as Morpho Vault

    User->>CVW: redeem/withdraw
    CVW->>CVW: _accruePremium() + compute userPrincipalForWithdrawal
    CVW->>MV: redeem(shares)
    MV-->>CVW: assetsReceived
    CVW->>CVW: insuredBasis = principal - premiumForWithdrawal
    Note over CVW: netAssets >= insuredBasis → no shortfall
    CVW-->>User: transfer netAssets
Loading

Withdrawal (With Shortfall / Claim Path)

sequenceDiagram
    participant User
    participant CVW as CoveredVaultWrapper
    participant MV as Morpho Vault
    participant CM as ClaimManager

    User->>CVW: redeem/withdraw
    CVW->>CVW: _accruePremium() + compute userPrincipalForWithdrawal
    CVW->>MV: redeem(shares)
    MV-->>CVW: assetsReceived
    CVW->>CVW: insuredBasis = principal - premiumForWithdrawal
    CVW->>CVW: shortfall = insuredBasis - netAssets > 0
    CVW->>CVW: _capAndApplyDeductible(shortfall, insuredBasis)
    CVW->>CM: fileClaim(policyId, claimAmount, evidenceHash, data)
    CM-->>CVW: payout (if approved)
    CVW-->>User: transfer netAssets + payout
Loading

Note: ClaimManager.fileClaim(...) performs file + resolve atomically. payoutToken is set to the Morpho vault's underlying token.

Coverage and Payout Controls

Remaining Coverage Accounting

For each policy:

  • requested claim must be <= current remaining coverage,
  • actual payout updates cumulative paid-out amount,
  • remaining coverage never drops below zero.

Claim Window

Claims are accepted only while policy is active:

  • startTime <= now < maturityTime.

Spec Approval Gate

Claim payout path is entered only if ISpec.evaluate(context).isPayable == true.

Curator Approval Gate (per successive claim)

After each approved claim, claimApprovalRequired[policyId] is set to true. The policy curator must call ClaimManager.approveNextClaim(policyId) before the next fileClaim will be accepted. This gives the curator a mandatory review window between successive payouts.

Premium Default Gate

fileClaim checks !metadata.premiumDefaulted. If the admin has flagged the policy as premium-defaulted (via PolicyManager), claims are rejected until the default is cleared.

Evidence Hash

Every call to fileClaim must supply a non-zero evidenceHash that is unique per policy. The ClaimManager maintains a per-policy set of consumed hashes and reverts with DuplicateEvidenceHash if the same hash is reused, whether the original claim was approved or rejected. This ensures each claim references distinct evidence and supports post-claim auditing.

Callers should supply a hash that identifies a retrievable evidence document, such as an IPFS CID digest (keccak256 of the CID multihash bytes), to enable off-chain verification.

Collateral Conversion Rules

In _processCollateral(...):

  • If collateral already equals payout token:
    • transfer directly up to remainingPayout; any surplus is forwarded to RewardsManager.
  • Else (cross-token):
    • _computeAmountOutMin calls Swapper.quoteSwap(tokenIn, tokenOut, amountIn) and applies maxSwapSlippageBps to get a slippage-adjusted minimum output,
    • approve Swapper for collateralAmount,
    • call Swapper.executeSwap(params) with the computed amountOutMin,
    • cap delivered amount to remainingPayout; any surplus swap output is forwarded to RewardsManager,
    • clear residual approval.

Vault slashes are pre-inflated in _computeVaultSlashes (by 1 / (1 - maxSwapSlippageBps) for cross-token vaults) so that worst-case slippage still yields the full requestedAmount to the beneficiary.

This supports heterogeneous slash collateral while preserving payout-token settlement without relying on an oracle.

Swapper Configuration for Cross-Token Payouts

When the vault collateral token differs from the policy payoutToken, a swap route must be configured in the Swapper before any claim can be paid out. The UniswapV3Adapter bridges the Swapper's static-calldata model to Uniswap V3.

One-time setup per (tokenIn, tokenOut) pair:

forge script script/DeployUniswapV3Adapter.s.sol \
  --rpc-url $RPC_URL --broadcast --slow -vvvv

Required env vars:

Variable Description
PRIVATE_KEY Admin key holding SWAP_MANAGER_ROLE on Swapper
SWAPPER Swapper proxy address (from ClaimManager.swapper())
SWAP_ROUTER Uniswap V3 SwapRouter02 (Sepolia: 0x3bFA4769FB09eefC5a80d6E87c3B9C650f7Ae48E)
TOKEN_IN Collateral token (e.g. Uniswap-native WETH on Sepolia: 0xfff9976782d46cc05630d1f6ebab18b2324d6b14)
TOKEN_OUT Payout token (e.g. Circle Sepolia USDC: 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238)
POOL_FEE Uniswap V3 fee tier (e.g. 3000 = 0.3%, 500 = 0.05%)

The script deploys the adapter, whitelists it in Swapper, and registers the route in a single transaction.

Verify before filing a claim:

FileClaim.s.sol Phase 2 (Swapper Readiness) automatically checks each committee vault's collateral token against the policy payout token and validates that a route is configured and the target is whitelisted. It reverts before broadcasting if any route is missing.

Risk and Operations

Configuration Risks

  • Missing or bad swap routes can impair payout conversion for non-payout collateral.
  • Incorrect fee settings can over-route or under-route premium shares.
  • Wrong pool/policyId passed to distributePremium can mis-route policy rewards.

Runtime Risks

  • If Core slashing returns no collateral, payout path reverts.
  • If Swapper.quoteSwap reverts or returns zero for a cross-token vault, that vault is excluded from slashing (VaultExcludedFromSlashing event emitted).
  • Claim timing outside coverage window always reverts.

Operational Controls

  • Keep claim swap routes and whitelisted targets maintained ahead of incidents.
  • Monitor PremiumDistributed, ClaimFiled, ClaimApproved, ClaimRejected, CollateralSwapped.
  • Validate policy-to-vault mappings before policy activation.

Migration Flow (Base Vault -> Covered Vault)

For existing base-vault users opting into insurance:

  1. User calls a migrator contract.
  2. Migrator redeems user position from base vault to underlying asset.
  3. Migrator deposits underlying into covered vault on behalf of user.
  4. Covered vault mints covered shares to user.

This migration is an integration-layer flow and usually implemented outside Coverage core contracts.

Practical Testing Coverage

The scripts in script/testing/ map directly to this document:

  • DistributePremium.s.sol — calls CoveredVaultWrapper.collectPremium(), validating the premium flush and distribution path.
  • FileClaim.s.sol — phases: prerequisites → Swapper readiness → pre-claim snapshot → file → result; validates claim evaluation and payout path including swap metrics.
  • CompleteCoverageFlow.s.sol — exercises both paths end-to-end; includes lightweight Swapper readiness logging and skip-if-zero-amount guards for re-runs.

Next Steps

Clone this wiki locally