前言本文围绕去中心化保险展开,系统梳理其相关理论体系与代码实操流程;整体基于HardhatV3开发框架,依托OpenZeppelinV5合约库及Solidity0.8.24+版本,完整覆盖从合约开发、本地测试、部署上线到落地应用的全流程。去中心化保险知识梳理概述去中心
本文围绕去中心化保险展开,系统梳理其相关理论体系与代码实操流程;整体基于 Hardhat V3 开发框架,依托 OpenZeppelin V5 合约库及 Solidity 0.8.24 + 版本,完整覆盖从合约开发、本地测试、部署上线到落地应用的全流程。
去中心化保险知识梳理
概述
去中心化保险是区块链技术与传统保险行业结合的产物,旨在通过智能合约、通证经济和去中心化自治组织,解决传统保险中存在的信任缺失、效率低下和不透明等痛点。
1. 核心定义
去中心化保险是指不依赖中心化保险公司或中介机构,而是利用区块链技术(主要是智能合约)来自动执行保险协议、管理资金池、评估风险并进行理赔的一种新型保险模式。
与传统保险相比,去中心化保险具有以下显著特征:
透明性
自动化
无需信任
去中介化
去中心化保险目前主要分为两大方向: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 金融领域的探索提供坚实的技术支撑 如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!