Introduction
Insurance has been a crucial risk management tool for centuries. Yet, traditional insurance systems often suffer from inefficiencies, including lack of transparency, high operational costs, and unequal access. The advent of blockchain technology brings the promise of overcoming these challenges, offering a more transparent, cost-efficient, and accessible alternative. A key feature of this new paradigm is decentralized peer-to-peer insurance pools.
This tutorial aims to equip you with the necessary skills to create a decentralized insurance peer pool. Through this hands-on approach, you’ll gain invaluable insights into the mechanisms of decentralized insurance, as we delve into the creation of smart contracts, management of pooled risk, and fair claim payouts.
In this tutorial, we will create a P2P Insurance Pool using Solidity, the leading programming language for implementing smart contracts on the Celo blockchain. Our P2P Insurance Pool will allow participants to join the pool, contribute funds, make claims, and receive payouts. We’ll use Solidity to program the rules of our insurance pool and deploy it as a smart contract on the Celo network.
Ready to dive in? Let’s get started on our journey to decentralized insurance.
Here’s the github repo of our code. source code
Prerequisites
To follow this tutorial, you will need the following:
- Solidity programming language.
- Remix IDE.
- The celo Extension Wallet.
Smart Contract Developement
The complete contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract InsurancePeerPool {
struct Member {
uint256 contribution; // Contribution amount from member
uint256 insuranceAmount; // Insurance amount allocated to the member
bool isActive; // Whether the member is active or not
}
mapping(address => Member) public members;
address[] public memberAddresses;
address public owner;
event ContributionAdded(address indexed member, uint256 contribution);
event InsuranceClaimed(address indexed member, uint256 amount);
event ContributionWithdrawn(address indexed member, uint256 amount);
event InsuranceAllocated(address indexed member, uint256 amount);
event FundsWithdrawn(address indexed owner, uint256 amount);
modifier onlyActiveMember() {
require(members[msg.sender].isActive, "You are not an active member.");
_;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only the contract owner can call this function.");
_;
}
constructor() {
owner = msg.sender;
}
function join() external payable {
require(!members[msg.sender].isActive, "You are already a member.");
require(msg.value > 0, "Contribution amount must be greater than 0.");
Member memory newMember = Member({
contribution: msg.value,
insuranceAmount: 0,
isActive: true
});
members[msg.sender] = newMember;
memberAddresses.push(msg.sender);
}
function contribute() external payable onlyActiveMember {
require(msg.value > 0, "Contribution amount must be greater than 0.");
members[msg.sender].contribution += msg.value;
emit ContributionAdded(msg.sender, msg.value);
payable(address(this)).transfer(msg.value);
}
function claimInsurance(uint256 amount) external onlyActiveMember {
require(amount > 0, "Claim amount must be greater than 0.");
require(amount <= members[msg.sender].insuranceAmount, "Insufficient insurance amount.");
members[msg.sender].insuranceAmount -= amount;
payable(msg.sender).transfer(amount);
emit InsuranceClaimed(msg.sender, amount);
}
function withdrawContribution(uint256 amount) external onlyActiveMember {
require(amount > 0, "Withdrawal amount must be greater than 0.");
require(amount <= members[msg.sender].contribution, "Insufficient contribution balance.");
members[msg.sender].contribution -= amount;
payable(msg.sender).transfer(amount);
emit ContributionWithdrawn(msg.sender, amount);
}
function allocateInsurance(address memberAddress, uint256 amount) external onlyOwner {
require(members[memberAddress].isActive, "Member is inactive.");
require(amount > 0, "Allocation amount must be greater than 0.");
members[memberAddress].insuranceAmount += amount;
emit InsuranceAllocated(memberAddress, amount);
}
function deactivateMember(address memberAddress) external onlyOwner {
require(members[memberAddress].isActive, "Member is already inactive.");
members[memberAddress].isActive = false;
}
function activateMember(address memberAddress) external onlyOwner {
require(!members[memberAddress].isActive, "Member is already active.");
members[memberAddress].isActive = true;
}
function getMemberCount() public view returns (uint256) {
return memberAddresses.length;
}
function getContractBalance() external view returns (uint256) {
return address(this).balance;
}
function withdrawFunds(uint256 amount) external onlyOwner {
require(amount > 0, "Withdrawal amount must be greater than 0.");
require(amount <= address(this).balance, "Insufficient contract balance.");
payable(msg.sender).transfer(amount);
emit FundsWithdrawn(msg.sender, amount);
}
}
Breakdown
Contract Structure and Variables
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
Next, the contract declaration and struct definition are provided:
contract InsurancePeerPool {
struct Member {
uint256 contribution; // Contribution amount from member
uint256 insuranceAmount; // Insurance amount allocated to the member
bool isActive; // Whether the member is active or not
}
// Mapping to store members' data
mapping(address => Member) public members;
// Array to store member addresses for iteration
address[] public memberAddresses;
// Address of the contract owner
address public owner;
// Events for logging contract events
event ContributionAdded(address indexed member, uint256 contribution);
event InsuranceClaimed(address indexed member, uint256 amount);
event ContributionWithdrawn(address indexed member, uint256 amount);
event InsuranceAllocated(address indexed member, uint256 amount);
event FundsWithdrawn(address indexed owner, uint256 amount);
The Member
struct defines the data structure for each member, including their contribution amount, allocated insurance amount, and active status.
The members
mapping is used to store the Member
struct for each member, using their address as the key.
The memberAddresses
array is used to store the addresses of all the members for easier iteration and retrieval.
The owner
variable stores the address of the contract owner.
Events are defined to log important contract events, such as adding contributions, claiming insurance, withdrawing contributions, allocating insurance, and withdrawing funds.
Modifiers
The contract defines two modifiers:
modifier onlyActiveMember() {
require(members[msg.sender].isActive, "You are not an active member.");
_;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only the contract owner can call this function.");
_;
}
The onlyActiveMember
modifier ensures that only active members can call certain functions, such as contributing funds or claiming insurance.
The onlyOwner
modifier restricts certain functions to be called only by the contract owner.
Constructor
The contract constructor initializes the contract owner:
constructor() {
owner = msg.sender;
}
The owner
variable is set to the address of the contract deployer (the one who deploys the contract).
Functions
Join
function join() external payable {
require(!members[msg.sender].isActive, "You are already a member.");
require(msg.value > 0, "Contribution amount must be greater than 0.");
Member memory newMember = Member({
contribution: msg.value,
insuranceAmount: 0,
isActive: true
});
members[msg.sender] = newMember;
memberAddresses.push(msg.sender);
}
join()
function : Allows a member to join the insurance pool by contributing funds. It checks if the member is not already active and the contribution amount is greater than zero. If the conditions are met, a new Member
struct is created and added to the members
mapping, and the member’s address is added to the memberAddresses
array.
Contribute
function contribute() external payable onlyActiveMember {
require(msg.value > 0, "Contribution amount must be greater than 0.");
members[msg.sender].contribution += msg.value;
emit ContributionAdded(msg.sender, msg.value);
payable(address(this)).transfer(msg.value);
}
contribute()
function : Allows an active member to contribute additional funds to their existing contribution amount. The function checks if the member is active and the contribution amount is greater than zero. If so, the member’s contribution is updated.
Claim Insurance
function claimInsurance(uint256 amount) external onlyActiveMember {
require(amount > 0, "Claim amount must be greater than 0.");
require(amount <= members[msg.sender].insuranceAmount, "Insufficient insurance amount.");
members[msg.sender].insuranceAmount -= amount;
payable(msg.sender).transfer(amount);
emit InsuranceClaimed(msg.sender, amount);
}
claimInsurance(uint256 amount)
function : Allows an active member to claim insurance by specifying the desired claim amount. The function checks if the claim amount is greater than zero and does not exceed the member’s allocated insurance amount. If the conditions are met, the member’s insurance amount is reduced, and the specified amount is transferred to the member’s address.
Withdraw Contribution
function withdrawContribution(uint256 amount) external onlyActiveMember {
require(amount > 0, "Withdrawal amount must be greater than 0.");
require(amount <= members[msg.sender].contribution, "Insufficient contribution balance.");
members[msg.sender].contribution -= amount;
payable(msg.sender).transfer(amount);
emit ContributionWithdrawn(msg.sender, amount);
}
withdrawContribution(uint256 amount)
function : Allows an active member to withdraw their unused contribution amount. The function checks if the withdrawal amount is greater than zero and does not exceed the member’s contribution balance. If the conditions are met, the member’s contribution is reduced, and the specified amount is transferred to the member’s address.
Allocate Insurance
function allocateInsurance(address memberAddress, uint256 amount) external onlyOwner {
require(members[memberAddress].isActive, "Member is inactive.");
require(amount > 0, "Allocation amount must be greater than 0.");
members[memberAddress].insuranceAmount += amount;
emit InsuranceAllocated(memberAddress, amount);
}
allocateInsurance(address memberAddress, uint256 amount)
function : Allows the contract owner to allocate insurance amounts to specific members. It checks if the member is active and the allocation amount is greater than zero. If the conditions are met, the member’s insurance amount is increased.
Deactivate and Activate Member
function deactivateMember(address memberAddress) external onlyOwner {
require(members[memberAddress].isActive, "Member is already inactive.");
members[memberAddress].isActive = false;
}
function activateMember(address memberAddress) external onlyOwner {
require(!members[memberAddress].isActive, "Member is already active.");
members[memberAddress].isActive = true;
}
deactivateMember(address memberAddress)
function: Allows the contract owner to deactivate a member. It checks if the member is already inactive and sets their active status to false.activateMember(address memberAddress)
function: Allows the contract owner to activate a member. It checks if the member is already active and sets their active status to true.
Get Member Count and Get Contract Balance
function getMemberCount() public view returns (uint256) {
return memberAddresses.length;
}
function getContractBalance() external view returns (uint256) {
return address(this).balance;
}
getMemberCount()
: Returns the total number of members in the pool.getContractBalance()
: Returns the current balance of the contract (in wei).
Withdraw Funds
function withdrawFunds(uint256 amount) external onlyOwner {
require(amount > 0, "Withdrawal amount must be greater than 0.");
require(amount <= address(this).balance, "Insufficient contract balance.");
payable(msg.sender).transfer(amount);
emit FundsWithdrawn(msg.sender, amount);
}
withdrawFunds(uint256 amount)
function : Allows the contract owner to withdraw funds from the contract. It checks if the withdrawal amount is greater than zero and does not exceed the contract balance. If the conditions are met, the specified amount is transferred to the owner’s address.
Deployment
Install the Celo Plugin
First, you’ll need to install the Celo Plugin for Remix. To do this, open Remix and click on the Plugin Manager
icon on the left-hand side. Search for Celo
and click the Install
button next to the Celo Plugin. Once the installation is complete, you’ll see a new Celo
tab appear in the sidebar.
Connect to the Celo Alfajores Testnet
To deploy our smart contract successfully, we need the celo extention wallet which can be downloaded from here
Next, we need to fund our newly created wallet which can done using the celo alfojares faucet Here
Next, you’ll need to connect Remix to the Celo Testnet. Click on the Celo
tab in the sidebar and then click on the Connect to Network
button.
Compile Contract
Open the contract file in Remix and click on the Solidity Compiler
tab in the sidebar. Click the `Compile button to compile the contract.
Deploy the Contract
Click on the Deploy & Run Transactions
tab in the sidebar. In the Contract
dropdown menu, select the contract.
Once the contract is deployed, you can interact with it using the functions in the Deployed Contracts
section of the Deploy & Run Transactions
tab.
Conclusion
The InsurancePeerPool smart contract provides a decentralized insurance pool where members can join, contribute funds, claim insurance, and manage their contributions. The contract owner has additional privileges to allocate insurance amounts, manage member status, and withdraw funds.
Extra Resources
Learn more by following these links.
About the author
I’ am a passionate web3 developer who loves to write and learn new skills.