Розробка параметричного страхування на блокчейні
Традиційне страхування — це суб'єктивна оцінка збитків, довга урегулювання претензій, паперовий документообіг та ризик відмови у виплаті. Параметричне страхування працює по-іншому: виплата відбувається автоматично при досягненні заздалегідь оговореного параметра (температура нижче -20°C, ціна ETH падає на 30%, затримка рейсу понад 3 години). Блокчейн + oracle роблять таке страхування повністю прозорим та позбавленим людської помилки при розрахунку виплат.
Як це працює на рівні протоколу
Структура контракту параметричного страхування:
Застрахований → Policy (контракт) → Oracle (умова) → AutoPayout
↑
Risk Pool (ліквідність для виплат)
Ключові компоненти:
Policy — індивідуальний страховий договір. Містить параметри: застрахований адрес, умова виплати, суму покриття, період дії, сплачену премію.
Risk Pool — пул ліквідності, з якого відбуваються виплати. Аналог страхового резерву. Наповнюється преміями застрахованих та/або капіталом LP (liquidity providers).
Oracle — джерело даних для перевірки умови. Chainlink для ціннісних даних, Chainlink Functions для користувацьких API (погода, авіарейси), UMA для суб'єктивних параметрів.
Trigger — функція перевірки умови та ініціації виплати. Викликається автоматично (Chainlink Automation) або вручну після наступління події.
Архітектура смарт-контрактів
Розбиваємо на три контракти для розділення відповідальності:
// 1. PolicyManager — управління полісами
contract PolicyManager {
struct Policy {
address holder;
address token; // валюта виплати (USDC)
uint256 coverage; // сума покриття
uint256 premium; // сплачена премія
uint256 startTime;
uint256 endTime;
bytes32 conditionId; // посилання на умову в ConditionRegistry
PolicyStatus status;
}
enum PolicyStatus { Active, Triggered, Expired, Claimed }
mapping(bytes32 => Policy) public policies;
IConditionRegistry public conditionRegistry;
IRiskPool public riskPool;
function createPolicy(
address token,
uint256 coverage,
bytes32 conditionId,
uint256 duration
) external payable returns (bytes32 policyId) {
uint256 premium = calculatePremium(coverage, conditionId, duration);
require(msg.value >= premium || IERC20(token).transferFrom(msg.sender, address(this), premium));
policyId = keccak256(abi.encodePacked(msg.sender, conditionId, block.timestamp));
policies[policyId] = Policy({
holder: msg.sender,
token: token,
coverage: coverage,
premium: premium,
startTime: block.timestamp,
endTime: block.timestamp + duration,
conditionId: conditionId,
status: PolicyStatus.Active
});
riskPool.lockLiquidity(policyId, coverage);
emit PolicyCreated(policyId, msg.sender, coverage);
}
}
// 2. ConditionRegistry — реєстр умов виплат
contract ConditionRegistry {
struct Condition {
ConditionType condType;
address oracle;
bytes32 feedId; // Chainlink feed ID
int256 threshold; // поріг значення
ComparisonType comparison; // BELOW, ABOVE, EQUALS
uint256 confirmations; // кількість підтверджень oracle
}
enum ConditionType { PriceFeed, CustomAPI, ManualOracle }
enum ComparisonType { Below, Above, Equals }
function checkCondition(bytes32 conditionId) public view returns (bool triggered, int256 currentValue) {
Condition storage cond = conditions[conditionId];
if (cond.condType == ConditionType.PriceFeed) {
(, int256 price,, uint256 updatedAt,) = AggregatorV3Interface(cond.oracle).latestRoundData();
// Перевірка свіжості даних
require(block.timestamp - updatedAt < STALE_THRESHOLD, "Stale oracle data");
currentValue = price;
triggered = _compare(price, cond.threshold, cond.comparison);
}
}
}
// 3. RiskPool — управління ліквідністю
contract RiskPool {
mapping(bytes32 => uint256) public lockedLiquidity;
uint256 public totalLocked;
uint256 public totalAvailable;
// LP можуть вносити ліквідність та отримувати yield від премій
mapping(address => uint256) public lpShares;
uint256 public totalShares;
function deposit(uint256 amount) external {
USDC.transferFrom(msg.sender, address(this), amount);
uint256 shares = totalShares == 0 ? amount : (amount * totalShares) / totalAvailable;
lpShares[msg.sender] += shares;
totalShares += shares;
totalAvailable += amount;
}
function payout(bytes32 policyId, address recipient, uint256 amount) external onlyPolicyManager {
require(lockedLiquidity[policyId] >= amount, "Insufficient locked liquidity");
lockedLiquidity[policyId] -= amount;
totalLocked -= amount;
USDC.transfer(recipient, amount);
}
}
Інтеграція Oracle: головна технічна складність
Весь протокол залежить від надійності даних oracle. Три вектори атак, які потрібно закрити:
Маніпуляція oracle через flash loan. Якщо умова виплати — «ціна ETH упала нижче $1000», зловмисник бере flash loan, продає ETH на DEX до потрібної ціни, отримує виплату, викупує ETH, повертає кредит. Захист: не використовувати spot price від DEX oracle. Тільки Chainlink Data Feeds з агрегацією від кількох нод, або TWAP за період, несумісний з flash loan (TWAP > 1 блока вже захищений).
Застарілі дані. Chainlink oracle перестає оновлюватися (проблеми ноди, мережа перевантажена). latestRoundData() повертає старі дані. Контракт повинен перевіряти updatedAt та відхиляти дані старіші за X хвилин.
(, int256 price,, uint256 updatedAt,) = priceFeed.latestRoundData();
require(block.timestamp - updatedAt <= MAX_STALENESS, "Oracle data too old");
require(price > 0, "Invalid price");
Єдина точка відмови oracle. Один Chainlink feed — це довіра одному джерелу. Для критичних умов використовуємо кілька джерел oracle з медіаною:
function getMedianPrice(address[] memory feeds) internal view returns (int256) {
int256[] memory prices = new int256[](feeds.length);
for (uint i = 0; i < feeds.length; i++) {
(, prices[i],,,) = AggregatorV3Interface(feeds[i]).latestRoundData();
}
return median(prices); // сортування + середній елемент
}
Розрахунок премій
Актуарна математика для смарт-контрактів — нетривіальна задача. Спрощені підходи:
Фіксований коефіцієнт. premium = coverage * rate, де rate встановлюється адміністратором на основі історичних даних. Просто, але не адаптивно.
Динамічна премія через implied volatility. Для цінових триггерів — премія зростає при високій волатильності активу. Дорого по gas для onchain розрахунку. Рішення: розрахунок офчейн, підпис через EIP-712, верифікація onchain.
Bonding curve для Risk Pool. Чим менше вільної ліквідності у пулі — тим дорожче нова поліса. Природний механізм балансування: при високому попиту на покриття ціна зростає, привертаючи нових LP.
Типи параметричних продуктів
| Продукт | Параметр | Oracle |
|---|---|---|
| Крипто price protection | Ціна активу < N | Chainlink Price Feed |
| DeFi депозит страховка | TVL протоколу < X | Користувацький + Chainlink |
| Авіастрахування | Затримка рейсу > 3ч | Chainlink Functions + FlightAware API |
| Погодне страхування | Температура < -20°C | Chainlink + OpenWeatherMap |
| Смарт-контракт аудит | Exploit (втрата TVL > Y%) | Multisig oracle |
Нормативні розгляди
DeFi страхування — регуляторно чутлива область. Nexus Mutual працює як discretionary mutual, не страховик. Etherisc отримав ліцензію в деяких юрисдикціях. На рівні смарт-контрактів: умови обслуговування, геоблокування для регульованих ринків, KYC для виплат понад поріг.
Процес розробки
Проектування (3-5 днів). Визначаємо продуктову логіку: типи полісок, oracle стратегія, механіка Risk Pool, токеноміка LP-токенів. Актуарний розрахунок базових ставок.
Розробка контрактів (7-10 днів). PolicyManager, ConditionRegistry, RiskPool. Інтеграція Chainlink Automation для автоматичних триггерів. Тести на Foundry з форком mainnet — імітуємо різні цінові сценарії.
Security review (3-5 днів). Slither + Mythril. Особлива увага на oracle шляхи, арифметика у розрахунку премій (overflow/precision), reentrancy при payout.
Frontend та The Graph (5-7 днів). Subgraph для історії полісок, React-дашборд застрахованого, LP-інтерфейс.
Testnet та аудит (1-2 тижні). Запуск на Sepolia/Mumbai, імітація страхових подій, зовнішній аудит перед mainnet.
Загальний термін для базового протоколу з одним типом страхування — 4-6 тижнів. Повноцінна багатопродуктова платформа — 3-4 місяці.







