// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import "./IVaultfireIdentity.sol"; import "./IVaultfireBonds.sol"; import "./IVaultfireReputation.sol"; /// @title AgentInsurancePool — On-chain AI agent insurance via pooled coverage /// @author Vaultfire Protocol /// @notice Agents deposit premiums into a shared insurance pool. If an agent causes /// verified damage, the pool automatically compensates the affected party. /// Risk tiers are based on trust scores — higher trust = lower premium. /// @dev Integrates with ERC-8004 identity, bonds, and reputation registries. contract AgentInsurancePool { // ═══════════════════════════════════════════════════ // STRUCTS // ═══════════════════════════════════════════════════ struct Policy { address agent; // Insured agent (ERC-8004 registered) uint256 premiumPaid; // Total premium deposited (wei) uint256 coverageLimit; // Max payout per claim (wei) uint256 activeSince; // Timestamp when policy activated uint256 expiresAt; // Timestamp when policy expires uint256 riskTier; // 1 = low risk, 2 = medium, 3 = high uint256 claimsPaid; // Total claims paid out for this agent bool active; // Whether policy is currently active } struct Claim { uint256 policyId; // Policy being claimed against address claimant; // Party filing the claim (affected party) address agent; // Agent the claim is against uint256 amount; // Requested payout amount (wei) uint256 filedAt; // Timestamp when claim was filed uint256 resolvedAt; // Timestamp when resolved (0 if pending) ClaimStatus status; // Current claim status string evidenceURI; // IPFS/URI to evidence of damage } enum ClaimStatus { Pending, Approved, Denied, Paid } // ═══════════════════════════════════════════════════ // STATE // ═══════════════════════════════════════════════════ IVaultfireIdentity public immutable identityRegistry; IVaultfireBonds public immutable bondsRegistry; IVaultfireReputation public immutable reputationRegistry; address public owner; address public arbiter; // Authorized claim resolver uint256 public totalPoolBalance; uint256 public totalPolicies; uint256 public totalClaims; uint256 public totalClaimsPaid; // Risk tier premium multipliers (basis points — 100 = 1%) uint256 public constant LOW_RISK_RATE = 100; // 1% of coverage uint256 public constant MEDIUM_RISK_RATE = 250; // 2.5% of coverage uint256 public constant HIGH_RISK_RATE = 500; // 5% of coverage // Reputation thresholds for risk tiers uint256 public constant LOW_RISK_MIN_REP = 70; uint256 public constant MEDIUM_RISK_MIN_REP = 40; // Below 40 = high risk uint256 public constant MIN_POLICY_DURATION = 30 days; uint256 public constant MAX_POLICY_DURATION = 365 days; uint256 public constant MAX_CLAIM_AMOUNT_BPS = 10000; // 100% of coverage mapping(uint256 => Policy) public policies; mapping(uint256 => Claim) public claims; mapping(address => uint256[]) public agentPolicies; mapping(address => bool) public hasActivePolicy; // ═══════════════════════════════════════════════════ // EVENTS // ═══════════════════════════════════════════════════ event PolicyCreated(uint256 indexed policyId, address indexed agent, uint256 coverageLimit, uint256 premium, uint256 riskTier); event PolicyExpired(uint256 indexed policyId, address indexed agent); event ClaimFiled(uint256 indexed claimId, uint256 indexed policyId, address indexed claimant, uint256 amount); event ClaimResolved(uint256 indexed claimId, ClaimStatus status, uint256 payout); event PremiumDeposited(address indexed agent, uint256 amount); event ArbiterUpdated(address indexed oldArbiter, address indexed newArbiter); // ═══════════════════════════════════════════════════ // MODIFIERS // ═══════════════════════════════════════════════════ modifier onlyOwner() { require(msg.sender == owner, "AgentInsurancePool: caller is not owner"); _; } modifier onlyArbiter() { require(msg.sender == arbiter, "AgentInsurancePool: caller is not arbiter"); _; } modifier onlyRegisteredAgent(address agent) { (string memory uri, bool active, , ) = identityRegistry.getAgent(agent); require(bytes(uri).length > 0 && active, "AgentInsurancePool: agent not registered or inactive"); _; } // ═══════════════════════════════════════════════════ // CONSTRUCTOR // ═══════════════════════════════════════════════════ constructor( address _identityRegistry, address _bondsRegistry, address _reputationRegistry ) { require(_identityRegistry != address(0), "AgentInsurancePool: zero identity address"); require(_bondsRegistry != address(0), "AgentInsurancePool: zero bonds address"); require(_reputationRegistry != address(0), "AgentInsurancePool: zero reputation address"); identityRegistry = IVaultfireIdentity(_identityRegistry); bondsRegistry = IVaultfireBonds(_bondsRegistry); reputationRegistry = IVaultfireReputation(_reputationRegistry); owner = msg.sender; arbiter = msg.sender; // Owner is initial arbiter } // ═══════════════════════════════════════════════════ // POLICY MANAGEMENT // ═══════════════════════════════════════════════════ /// @notice Create an insurance policy for a registered agent /// @param coverageLimit Maximum payout per claim (wei) /// @param duration Policy duration in seconds function createPolicy( uint256 coverageLimit, uint256 duration ) external payable onlyRegisteredAgent(msg.sender) { require(!hasActivePolicy[msg.sender], "AgentInsurancePool: agent already has active policy"); require(coverageLimit > 0, "AgentInsurancePool: zero coverage"); require(duration >= MIN_POLICY_DURATION && duration <= MAX_POLICY_DURATION, "AgentInsurancePool: invalid duration"); uint256 riskTier = _calculateRiskTier(msg.sender); uint256 requiredPremium = _calculatePremium(coverageLimit, riskTier, duration); require(msg.value >= requiredPremium, "AgentInsurancePool: insufficient premium"); uint256 policyId = totalPolicies; totalPolicies++; policies[policyId] = Policy({ agent: msg.sender, premiumPaid: msg.value, coverageLimit: coverageLimit, activeSince: block.timestamp, expiresAt: block.timestamp + duration, riskTier: riskTier, claimsPaid: 0, active: true }); agentPolicies[msg.sender].push(policyId); hasActivePolicy[msg.sender] = true; totalPoolBalance += msg.value; emit PolicyCreated(policyId, msg.sender, coverageLimit, msg.value, riskTier); emit PremiumDeposited(msg.sender, msg.value); } /// @notice Expire a policy that has passed its expiration date /// @param policyId The policy to expire function expirePolicy(uint256 policyId) external { Policy storage policy = policies[policyId]; require(policy.active, "AgentInsurancePool: policy not active"); require(block.timestamp >= policy.expiresAt, "AgentInsurancePool: policy not expired"); policy.active = false; hasActivePolicy[policy.agent] = false; emit PolicyExpired(policyId, policy.agent); } // ═══════════════════════════════════════════════════ // CLAIMS // ═══════════════════════════════════════════════════ /// @notice File a claim against an insured agent /// @param policyId Policy to claim against /// @param amount Requested payout amount (wei) /// @param evidenceURI URI pointing to evidence of damage function fileClaim( uint256 policyId, uint256 amount, string calldata evidenceURI ) external { Policy storage policy = policies[policyId]; require(policy.active, "AgentInsurancePool: policy not active"); require(block.timestamp < policy.expiresAt, "AgentInsurancePool: policy expired"); require(amount > 0 && amount <= policy.coverageLimit, "AgentInsurancePool: invalid claim amount"); require(policy.claimsPaid + amount <= policy.coverageLimit, "AgentInsurancePool: exceeds coverage limit"); require(bytes(evidenceURI).length > 0, "AgentInsurancePool: empty evidence"); uint256 claimId = totalClaims; totalClaims++; claims[claimId] = Claim({ policyId: policyId, claimant: msg.sender, agent: policy.agent, amount: amount, filedAt: block.timestamp, resolvedAt: 0, status: ClaimStatus.Pending, evidenceURI: evidenceURI }); emit ClaimFiled(claimId, policyId, msg.sender, amount); } /// @notice Resolve a claim (approve or deny) — arbiter only /// @param claimId The claim to resolve /// @param approved Whether to approve the claim function resolveClaim(uint256 claimId, bool approved) external onlyArbiter { Claim storage claim = claims[claimId]; require(claim.status == ClaimStatus.Pending, "AgentInsurancePool: claim not pending"); claim.resolvedAt = block.timestamp; if (approved) { require(totalPoolBalance >= claim.amount, "AgentInsurancePool: insufficient pool balance"); Policy storage policy = policies[claim.policyId]; require(policy.claimsPaid + claim.amount <= policy.coverageLimit, "AgentInsurancePool: exceeds coverage"); claim.status = ClaimStatus.Paid; policy.claimsPaid += claim.amount; totalPoolBalance -= claim.amount; totalClaimsPaid += claim.amount; (bool sent, ) = claim.claimant.call{value: claim.amount}(""); require(sent, "AgentInsurancePool: payout failed"); emit ClaimResolved(claimId, ClaimStatus.Paid, claim.amount); } else { claim.status = ClaimStatus.Denied; emit ClaimResolved(claimId, ClaimStatus.Denied, 0); } } // ═══════════════════════════════════════════════════ // VIEW FUNCTIONS // ═══════════════════════════════════════════════════ /// @notice Calculate the risk tier for an agent based on reputation function _calculateRiskTier(address agent) internal view returns (uint256) { (uint256 score, , ) = reputationRegistry.getReputation(agent); if (score >= LOW_RISK_MIN_REP) return 1; // Low risk if (score >= MEDIUM_RISK_MIN_REP) return 2; // Medium risk return 3; // High risk } /// @notice Calculate required premium for a policy function _calculatePremium( uint256 coverageLimit, uint256 riskTier, uint256 duration ) internal pure returns (uint256) { uint256 rate; if (riskTier == 1) rate = LOW_RISK_RATE; else if (riskTier == 2) rate = MEDIUM_RISK_RATE; else rate = HIGH_RISK_RATE; // Premium = coverage * rate * (duration / 365 days) / 10000 return (coverageLimit * rate * duration) / (365 days * 10000); } /// @notice Get premium quote for an agent function getPremiumQuote( address agent, uint256 coverageLimit, uint256 duration ) external view returns (uint256 premium, uint256 riskTier) { riskTier = _calculateRiskTier(agent); premium = _calculatePremium(coverageLimit, riskTier, duration); } /// @notice Get all policy IDs for an agent function getAgentPolicies(address agent) external view returns (uint256[] memory) { return agentPolicies[agent]; } /// @notice Get pool stats function getPoolStats() external view returns ( uint256 balance, uint256 policyCount, uint256 claimCount, uint256 paidOut ) { return (totalPoolBalance, totalPolicies, totalClaims, totalClaimsPaid); } // ═══════════════════════════════════════════════════ // ADMIN // ═══════════════════════════════════════════════════ /// @notice Update the arbiter address function setArbiter(address newArbiter) external onlyOwner { require(newArbiter != address(0), "AgentInsurancePool: zero arbiter"); emit ArbiterUpdated(arbiter, newArbiter); arbiter = newArbiter; } /// @notice Transfer ownership function transferOwnership(address newOwner) external onlyOwner { require(newOwner != address(0), "AgentInsurancePool: zero owner"); owner = newOwner; } }