-
Notifications
You must be signed in to change notification settings - Fork 0
06 premiums claims
Coverage economics are split into two operational paths:
-
premium path: continuous accrual in
CoveredVaultWrapperagainst depositor principal →collectPremium()flush → fee split inPremiumManager→ restaker reward routing, -
claim path: withdrawal-time shortfall detection →
fileClaimevaluation → 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
buyeris expected to be theCoveredVaultWrapperor its operator contract.
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.
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
Accounting details:
-
userPrincipal[receiver]tracks cost-basis (not yield); premium accrues on principal only, - a per-user
userPremiumDebtoffset prevents the depositor from inheriting liability that accrued before they arrived.
-
CoveredVaultWrapper- accrues premiums internally; exposescollectPremium()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.
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)
PremiumManager.getFeeSplits(grossPremium, platformFeeBps, poolFeeBps):
platformSplit = gross * platformFeeBps / 10_000poolSplit = gross * poolFeeBps / 10_000restakerSplit = gross - platformSplit - poolSplit
Guardrails:
-
platformFeeBps <= 2_500(25% max for platform fee), -
platformFeeBps + poolFeeBps <= 5_000(combined fee cap of 50%).
-
ClaimManager- claim intake, evaluation, and settlement. -
SpecRegistry+ISpec- payability decision logic. Only admin-approvedISpecimplementations 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.
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->>PM: distributeSurplusAsRewards(surplus, if any)
PM->>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->>PM: distributeSurplusAsRewards(surplus, if any)
PM->>RM: distributeRewards(surplus, if any)
end
end
CM->>CM: claimApprovalRequired[policyId] = true
end
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
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
Note: ClaimManager.fileClaim(...) performs file + resolve atomically. payoutToken is set to the Morpho vault's underlying token.
For each policy:
- requested claim must be <= current remaining coverage,
- actual payout updates cumulative paid-out amount,
- remaining coverage never drops below zero.
Claims are accepted only while policy is active:
-
startTime <= now < maturityTime.
Claim payout path is entered only if ISpec.evaluate(context).isPayable == true.
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.
fileClaim checks !metadata.premiumDefaulted. If the admin has flagged the policy as premium-defaulted (via PolicyManager), claims are rejected until the default is cleared.
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.
In _processCollateral(...):
- If collateral already equals payout token:
- transfer directly up to
remainingPayout; any surplus is forwarded toRewardsManagerviaPremiumManager.distributeSurplusAsRewards.
- transfer directly up to
- Else (cross-token):
-
_computeAmountOutMincallsSwapper.quoteSwap(tokenIn, tokenOut, amountIn)and appliesmaxSwapSlippageBpsto get a slippage-adjusted minimum output, - approve
SwapperforcollateralAmount, - call
Swapper.executeSwap(params)with the computedamountOutMin, - cap delivered amount to
remainingPayout; any surplus swap output is forwarded toRewardsManagerviaPremiumManager.distributeSurplusAsRewards, - 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.
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 -vvvvRequired 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.
- 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/policyIdpassed todistributePremiumcan mis-route policy rewards.
- If Core slashing returns no collateral, payout path reverts.
- If
Swapper.quoteSwapreverts or returns zero for a cross-token vault, that vault is excluded from slashing (VaultExcludedFromSlashingevent emitted). - Claim timing outside coverage window always reverts.
- 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.
For existing base-vault users opting into insurance:
- User calls a migrator contract.
- Migrator redeems user position from base vault to underlying asset.
- Migrator deposits underlying into covered vault on behalf of user.
- Covered vault mints covered shares to user.
This migration is an integration-layer flow and usually implemented outside Coverage core contracts.
The scripts in script/testing/ map directly to this document:
-
DistributePremium.s.sol— callsCoveredVaultWrapper.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.