InsurancePool Contract
The InsurancePool contract collects fees from loans and covers lender losses when borrowers default.Contract Overview
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
contract InsurancePool is ReentrancyGuard, AccessControl {
IERC20 public immutable usdc;
uint256 public totalPoolBalance;
uint256 public totalClaimsPaid;
uint256 public totalFeesCollected;
// Coverage ratio: 80% of defaults covered
uint256 public constant COVERAGE_RATIO = 80;
// Minimum pool balance before restricting new loans
uint256 public minimumPoolBalance;
struct Claim {
uint256 loanId;
uint256 amount;
uint256 timestamp;
bool paid;
}
mapping(uint256 => Claim) public claims;
uint256 public claimCounter;
// Surplus distribution tracking
mapping(address => uint256) public contributorShares;
uint256 public totalShares;
}
Key Functions
Deposit Insurance Fee
Called when a new loan is created:function deposit(uint256 amount) external onlyRole(LOAN_CONTRACT_ROLE) {
usdc.transferFrom(msg.sender, address(this), amount);
totalPoolBalance += amount;
totalFeesCollected += amount;
emit FeeDeposited(amount, totalPoolBalance);
}
Claim for Default
Called when a loan defaults:function claimForDefault(
uint256 loanId,
uint256 requestedAmount
) external onlyRole(LOAN_CONTRACT_ROLE) returns (uint256 paidAmount) {
require(claims[loanId].loanId == 0, "Already claimed");
// Calculate coverage (80% of loss)
uint256 maxCoverage = (requestedAmount * COVERAGE_RATIO) / 100;
// Pay what we can, up to max coverage
paidAmount = maxCoverage > totalPoolBalance ? totalPoolBalance : maxCoverage;
claims[++claimCounter] = Claim({
loanId: loanId,
amount: paidAmount,
timestamp: block.timestamp,
paid: true
});
totalPoolBalance -= paidAmount;
totalClaimsPaid += paidAmount;
// Transfer to Trust Vault to cover staker losses
usdc.transfer(msg.sender, paidAmount);
emit ClaimPaid(loanId, paidAmount, totalPoolBalance);
return paidAmount;
}
Voluntary Contribution
Users can contribute to increase coverage and earn surplus distributions:function contribute(uint256 amount) external nonReentrant {
require(amount >= MIN_CONTRIBUTION, "Below minimum");
usdc.transferFrom(msg.sender, address(this), amount);
// Calculate shares based on contribution
uint256 shares = (totalShares == 0)
? amount
: (amount * totalShares) / totalPoolBalance;
contributorShares[msg.sender] += shares;
totalShares += shares;
totalPoolBalance += amount;
emit ContributionMade(msg.sender, amount, shares);
}
Withdraw Contribution
Contributors can withdraw their share plus earned surplus:function withdrawContribution(uint256 shares) external nonReentrant {
require(contributorShares[msg.sender] >= shares, "Insufficient shares");
uint256 withdrawAmount = (shares * totalPoolBalance) / totalShares;
// Ensure minimum pool balance maintained
require(
totalPoolBalance - withdrawAmount >= minimumPoolBalance,
"Would breach minimum"
);
contributorShares[msg.sender] -= shares;
totalShares -= shares;
totalPoolBalance -= withdrawAmount;
usdc.transfer(msg.sender, withdrawAmount);
emit ContributionWithdrawn(msg.sender, withdrawAmount, shares);
}
Distribute Surplus
When pool grows beyond target, distribute surplus to contributors:function distributeSurplus() external onlyRole(POOL_MANAGER_ROLE) {
uint256 targetBalance = calculateTargetBalance();
require(totalPoolBalance > targetBalance, "No surplus");
uint256 surplus = totalPoolBalance - targetBalance;
// Keep 20% as buffer
uint256 distributableSurplus = (surplus * 80) / 100;
// Distribute pro-rata to contributors
// (Actual distribution happens via claim mechanism)
emit SurplusDistributed(distributableSurplus, totalShares);
}
View Functions
Get Pool Status
function getPoolStatus() external view returns (
uint256 balance,
uint256 claimsPaid,
uint256 feesCollected,
uint256 coverageCapacity,
uint256 utilizationRatio
) {
uint256 capacity = (totalPoolBalance * 100) / COVERAGE_RATIO;
uint256 utilization = totalClaimsPaid > 0
? (totalClaimsPaid * 100) / totalFeesCollected
: 0;
return (
totalPoolBalance,
totalClaimsPaid,
totalFeesCollected,
capacity,
utilization
);
}
Get Contributor Info
function getContributorInfo(address contributor) external view returns (
uint256 shares,
uint256 shareValue,
uint256 percentageOfPool
) {
uint256 userShares = contributorShares[contributor];
uint256 value = totalShares > 0
? (userShares * totalPoolBalance) / totalShares
: 0;
uint256 percentage = totalShares > 0
? (userShares * 10000) / totalShares
: 0;
return (userShares, value, percentage);
}
Events
event FeeDeposited(uint256 amount, uint256 newBalance);
event ClaimPaid(uint256 indexed loanId, uint256 amount, uint256 remainingBalance);
event ContributionMade(address indexed contributor, uint256 amount, uint256 shares);
event ContributionWithdrawn(address indexed contributor, uint256 amount, uint256 shares);
event SurplusDistributed(uint256 amount, uint256 totalShares);
event MinimumBalanceUpdated(uint256 oldMinimum, uint256 newMinimum);
Pool Economics
Fee Collection
┌─────────────────────────────────────────────────────────────────┐
│ Every Loan: 10% Insurance Fee │
├─────────────────────────────────────────────────────────────────┤
│ $100 loan requested │
│ └── $10 → Insurance Pool │
│ └── $90 → Borrower receives │
└─────────────────────────────────────────────────────────────────┘
Default Coverage
┌─────────────────────────────────────────────────────────────────┐
│ When Loan Defaults: 80% Coverage │
├─────────────────────────────────────────────────────────────────┤
│ $100 loan defaults │
│ └── $80 → Covered by Insurance Pool │
│ └── $20 → Absorbed by Trust Vault stakers │
└─────────────────────────────────────────────────────────────────┘
Surplus Distribution
| Pool Status | Action |
|---|---|
| Below minimum | New loans restricted |
| At target | Normal operation |
| Above target | Surplus distributed to contributors |
Constants
| Constant | Value | Description |
|---|---|---|
COVERAGE_RATIO | 80 | Percentage of defaults covered |
INSURANCE_FEE_BPS | 1000 | 10% fee on loans |
MIN_CONTRIBUTION | 100 USDC | Minimum voluntary contribution |
SURPLUS_THRESHOLD | 120% | When to distribute surplus |
Access Control
| Role | Permissions |
|---|---|
LOAN_CONTRACT_ROLE | Deposit fees, claim for defaults |
POOL_MANAGER_ROLE | Distribute surplus, update parameters |
DEFAULT_ADMIN_ROLE | Manage roles |
Integration Example
import { ethers } from 'ethers';
// Check pool status
const status = await insurancePool.getPoolStatus();
console.log(`Pool Balance: $${ethers.formatUnits(status.balance, 6)}`);
console.log(`Coverage Capacity: $${ethers.formatUnits(status.coverageCapacity, 6)}`);
// Contribute to pool
await usdc.approve(insurancePool.address, contribution);
await insurancePool.contribute(ethers.parseUnits("1000", 6));
// Check your contribution
const info = await insurancePool.getContributorInfo(myAddress);
console.log(`Your share value: $${ethers.formatUnits(info.shareValue, 6)}`);
Related Documentation
Trust Vault
Main liquidity pool
P2P Loan
Loan contract integration