前言
本文围绕去中心化保险展开,系统梳理其相关理论体系与代码实操流程;整体基于 Hardhat V3 开发框架,依托 OpenZeppelin V5 合约库及 Solidity 0.8.24 + 版本,完整覆盖从合约开发、本地测试、部署上线到落地应用的全流程。
去中心化保险知识梳理
概述
去中心化保险是区块链技术与传统保险行业结合的产物,旨在通过智能合约、通证经济和去中心化自治组织,解决传统保险中存在的信任缺失、效率低下和不透明等痛点。
1. 核心定义
去中心化保险是指不依赖中心化保险公司或中介机构,而是利用区块链技术(主要是智能合约)来自动执行保险协议、管理资金池、评估风险并进行理赔的一种新型保险模式。
- 本质:将保险业务逻辑代码化。
- 载体:智能合约。
2. 主要特征
与传统保险相比,去中心化保险具有以下显著特征:
-
透明性
- 所有的保单条款、资金流向、理赔逻辑都写在智能合约中,链上公开可查,不可篡改。用户可以随时验证资金池的健康状况。
-
自动化
- 依靠 ** 预言机(Oracle)** 获取外部数据(如是否发生航班延误、资产是否被盗),一旦触发理赔条件,智能合约自动执行赔付,无需人工干预。
-
无需信任
- 用户不需要信任保险公司的 “良心”,只需要信任代码逻辑和数学算法。
-
去中介化
- 去除了传统保险的层层代理和行政机构,减少了中间环节的抽成和运营成本。
3. 核心优势
- 降低成本:由于去除了中间商和自动化处理,运营成本和佣金大幅降低,从而可能提供更低的保费。
- 防止拒赔风险:传统保险常因条款模糊或人为因素导致拒赔;去中心化保险由代码强制执行,只要满足合约预设条件,赔付是必然发生的。
- 准入门槛低:基于公链的全球属性,任何有互联网连接的人(甚至无银行账户人群)都可以购买保险或成为承保人(提供流动性)。
- 资金效率高:部分保险协议允许资金提供者(承保人)在提供保险资金的同时,将闲置资金用于其他 DeFi 收益挖矿,提高了资本利用率。
4. 典型应用场景
去中心化保险目前主要分为两大方向:DeFi 原生保险和现实世界保险。
智能合约开发、测试、部署
智能合约
- 预言机合约:为便于本地测试,我们会使用模拟预言机;而在生产环境中,则应使用专用的预言机工具库。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
contract MockWeatherOracle is AccessControl {
bytes32 public constant REPORTER_ROLE = keccak256("REPORTER_ROLE");
// requestId => 结果 (例如:航班是否延延误)
mapping(bytes32 => bool) private _results;
constructor(address admin, address reporter) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
_grantRole(REPORTER_ROLE, reporter);
}
// 由预言机节点(Reporter)调用,写入链外数据
function fulfillData(bytes32 requestId, bool result) external onlyRole(REPORTER_ROLE) {
_results[requestId] = result;
}
// 供保险合约查询
function checkCondition(bytes32 requestId) external view returns (bool) {
return _results[requestId];
}
}
- 保险合约
import assert from "node:assert/strict";
import { describe, it, beforeEach } from "node:test";
import hre from "hardhat";
import { parseEther, keccak256, toBytes, formatEther } from "viem";
// 使用 node:test 时,需要确保你的 hardhat 环境已经正确加载 viem 扩展
// 全局变量类型可以根据实际合约ABI定义,这里使用 any 简化示例
let owner: any, investor: any, publicClient: any, testClient: any;
let WeatherOracle: any, SimpleInsurance: any;
describe("SimpleInsurance Test", async function () {
// Hardhat 的 viem 扩展可以帮助获取客户端
const { viem } = await hre.network.connect();
beforeEach(async function () {
publicClient = await viem.getPublicClient();
// owner 账户将作为 Admin 和资金注入者
// investor 账户将作为 User 和 Reporter(为了测试方便,实际中应不同)
[owner, investor] = await viem.getWalletClients();
testClient = await viem.getTestClient();
console.log("owner 地址:", owner.account.address);
console.log("investor (user/reporter) 地址:", investor.account.address);
// 1. 部署 WeatherOracle
// owner是admin, investor是reporter角色
WeatherOracle = await viem.deployContract("MockWeatherOracle", [owner.account.address, investor.account.address]);
console.log("WeatherOracle 地址:", WeatherOracle.address);
// 2. 部署 SimpleInsurance
SimpleInsurance = await viem.deployContract("SimpleInsurance", [ owner.account.address, WeatherOracle.address]);
console.log("SimpleInsurance 地址:", SimpleInsurance.address);
// 3. 注入资金池
await owner.sendTransaction({
to: SimpleInsurance.address,
value: parseEther("20"), // 注入 20 ETH 作为理赔资金
});
console.log("资金池已注入 20 ETH");
});
it("购买与理赔流程测试", async function () {
const policyIndex = BigInt(0);
const requestId = keccak256(toBytes("AIRCRAFT_CRASH_EVENT_XYZ"));
const premiumAmount = parseEther("1");
// --- 步骤 A: 投资者 (用户) 购买保险 ---
console.log("\n--- A. 用户购买保险 ---");
const userBalanceBeforePurchase = await publicClient.getBalance({ address: investor.account.address });
await SimpleInsurance.write.purchasePolicy({
value: premiumAmount,
account: investor.account,
});
const userBalanceAfterPurchase = await publicClient.getBalance({ address: investor.account.address });
// 验证余额减少了至少 1 ETH (扣除 gas)
assert(userBalanceAfterPurchase < userBalanceBeforePurchase, "用户余额应减少");
console.log("用户投保成功,保费:", formatEther(premiumAmount), "ETH");
// --- 步骤 B: 投资者 (扮演 Reporter 角色) 提交预言机数据 ---
console.log("\n--- B. 预言机节点提交数据 ---");
// 使用 investor 账户,因为它在 beforeEach 中被授予了 REPORTER_ROLE
await WeatherOracle.write.fulfillData([requestId, true], {
account: investor.account,
});
const conditionStatus = await WeatherOracle.read.checkCondition([requestId]);
assert.strictEqual(conditionStatus, true, "预言机数据应为真");
console.log("预言机数据提交成功,条件满足。");
// --- 步骤 C: 投资者 (用户) 发起理赔 ---
console.log("\n--- C. 用户发起理赔 ---");
const balanceBeforeClaim = await publicClient.getBalance({ address: investor.account.address });
await SimpleInsurance.write.requestClaim([policyIndex, requestId], {
account: investor.account,
});
const balanceAfterClaim = await publicClient.getBalance({ address: investor.account.address });
// 验证余额增加显著,理赔金额为 10 ETH
assert(balanceAfterClaim > balanceBeforeClaim, "理赔后用户余额应增加");
console.log("理赔成功!余额变化:", formatEther(balanceAfterClaim - balanceBeforeClaim));
// 验证保单状态
// userPolicies 函数返回一个 struct: (uint256 coverage, bool isActive, bool isClaimed)
// isClaimed 字段索引为 2
const policyStatus = await SimpleInsurance.read.userPolicies([investor.account.address, policyIndex]);
assert.strictEqual(policyStatus[2], true, "保单应标记为已理赔");
console.log("测试通过:购买与理赔流程跑通。");
});
});
测试脚本
测试说明:主要针对购买与理赔流程的测试
import assert from "node:assert/strict";
import { describe, it, beforeEach } from "node:test";
import hre from "hardhat";
import { parseEther, keccak256, toBytes, formatEther } from "viem";
// 使用 node:test 时,需要确保你的 hardhat 环境已经正确加载 viem 扩展
// 全局变量类型可以根据实际合约ABI定义,这里使用 any 简化示例
let owner: any, investor: any, publicClient: any, testClient: any;
let WeatherOracle: any, SimpleInsurance: any;
describe("SimpleInsurance Test", async function () {
// Hardhat 的 viem 扩展可以帮助获取客户端
const { viem } = await hre.network.connect();
beforeEach(async function () {
publicClient = await viem.getPublicClient();
// owner 账户将作为 Admin 和资金注入者
// investor 账户将作为 User 和 Reporter(为了测试方便,实际中应不同)
[owner, investor] = await viem.getWalletClients();
testClient = await viem.getTestClient();
console.log("owner 地址:", owner.account.address);
console.log("investor (user/reporter) 地址:", investor.account.address);
// 1. 部署 WeatherOracle
// owner是admin, investor是reporter角色
WeatherOracle = await viem.deployContract("MockWeatherOracle", [owner.account.address, investor.account.address]);
console.log("WeatherOracle 地址:", WeatherOracle.address);
// 2. 部署 SimpleInsurance
SimpleInsurance = await viem.deployContract("SimpleInsurance", [ owner.account.address, WeatherOracle.address]);
console.log("SimpleInsurance 地址:", SimpleInsurance.address);
// 3. 注入资金池
await owner.sendTransaction({
to: SimpleInsurance.address,
value: parseEther("20"), // 注入 20 ETH 作为理赔资金
});
console.log("资金池已注入 20 ETH");
});
it("购买与理赔流程测试", async function () {
const policyIndex = BigInt(0);
const requestId = keccak256(toBytes("AIRCRAFT_CRASH_EVENT_XYZ"));
const premiumAmount = parseEther("1");
// --- 步骤 A: 投资者 (用户) 购买保险 ---
console.log("\n--- A. 用户购买保险 ---");
const userBalanceBeforePurchase = await publicClient.getBalance({ address: investor.account.address });
await SimpleInsurance.write.purchasePolicy({
value: premiumAmount,
account: investor.account,
});
const userBalanceAfterPurchase = await publicClient.getBalance({ address: investor.account.address });
// 验证余额减少了至少 1 ETH (扣除 gas)
assert(userBalanceAfterPurchase < userBalanceBeforePurchase, "用户余额应减少");
console.log("用户投保成功,保费:", formatEther(premiumAmount), "ETH");
// --- 步骤 B: 投资者 (扮演 Reporter 角色) 提交预言机数据 ---
console.log("\n--- B. 预言机节点提交数据 ---");
// 使用 investor 账户,因为它在 beforeEach 中被授予了 REPORTER_ROLE
await WeatherOracle.write.fulfillData([requestId, true], {
account: investor.account,
});
const conditionStatus = await WeatherOracle.read.checkCondition([requestId]);
assert.strictEqual(conditionStatus, true, "预言机数据应为真");
console.log("预言机数据提交成功,条件满足。");
// --- 步骤 C: 投资者 (用户) 发起理赔 ---
console.log("\n--- C. 用户发起理赔 ---");
const balanceBeforeClaim = await publicClient.getBalance({ address: investor.account.address });
await SimpleInsurance.write.requestClaim([policyIndex, requestId], {
account: investor.account,
});
const balanceAfterClaim = await publicClient.getBalance({ address: investor.account.address });
// 验证余额增加显著,理赔金额为 10 ETH
assert(balanceAfterClaim > balanceBeforeClaim, "理赔后用户余额应增加");
console.log("理赔成功!余额变化:", formatEther(balanceAfterClaim - balanceBeforeClaim));
// 验证保单状态
// userPolicies 函数返回一个 struct: (uint256 coverage, bool isActive, bool isClaimed)
// isClaimed 字段索引为 2
const policyStatus = await SimpleInsurance.read.userPolicies([investor.account.address, policyIndex]);
assert.strictEqual(policyStatus[2], true, "保单应标记为已理赔");
console.log("测试通过:购买与理赔流程跑通。");
});
});
部署脚本
// scripts/deploy.js
import { network, artifacts } from "hardhat";
import { parseUnits } from "viem";
async function main() {
// 连接网络
const { viem } = await network.connect({ network: network.name });//指定网络进行链接
// 获取客户端
const [deployer, investor] = await viem.getWalletClients();
const publicClient = await viem.getPublicClient();
const deployerAddress = deployer.account.address;
console.log("部署者的地址:", deployerAddress);
// 加载合约天气预言机
const MockWeatherOracleArtifact = await artifacts.readArtifact("MockWeatherOracle");
// 部署(构造函数参数:recipient, initialOwner)
const MockWeatherOracleHash = await deployer.deployContract({
abi: MockWeatherOracleArtifact.abi,//获取abi
bytecode: MockWeatherOracleArtifact.bytecode,//硬编码
args: [deployerAddress, investor.account.address],//
});
// 等待确认并打印地址
const MockWeatherOracleReceipt = await publicClient.waitForTransactionReceipt({ hash: MockWeatherOracleHash });
console.log("MockWeatherOracle合约地址:", MockWeatherOracleReceipt.contractAddress);
// 部署SimpleBond合约
const SimpleInsuranceArtifact = await artifacts.readArtifact("SimpleInsurance");
// 1. 部署合约并获取交易哈希
const SimpleInsuranceHash = await deployer.deployContract({
abi: SimpleInsuranceArtifact.abi,
bytecode: SimpleInsuranceArtifact.bytecode,
args: [deployerAddress, MockWeatherOracleReceipt.contractAddress],
});
const SimpleBondReceipt = await publicClient.waitForTransactionReceipt({
hash: SimpleInsuranceHash
});
console.log("SimpleInsurance合约地址:", SimpleBondReceipt.contractAddress);
}
main().catch(console.error);
结语
至此,关于去中心化保险的核心理论体系梳理与代码实操落地已全部完成。从架构设计到合约编写,再到最终的部署与测试,我们已完整走完了从概念到应用的全流程。希望这份指南能为你在 Web3 金融领域的探索提供坚实的技术支撑