Building an Insurance Peer Pool on the Celo Blockchain

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.

3 Likes

Fantastic news! Your proposal has landed in this week’s top voted list. As you begin your project journey, remember to align with our community and technical guidelines, ensuring a high quality platform for our developers. Congratulations! :mortar_board: :seedling:

Note: @kingokonn based on the number of votes you received, we would like to proceed with your tutorial. However, it lacks specific technical details, making it unclear what you plan to build and demonstrate in the article. Kindly revise the article to align with our proposal guidelines. Once you are ready, we will conduct an additional review and proceed with rewarding you for this tutorial. Thank you!

2 Likes

@Celo_Academy I have expanded more on the value of the tutorial

2 Likes

the content is hidden for me, for anyone else it is like this?

2 Likes

@kingokonn can you view the topic now?

2 Likes

@Celo_Academy yes i can

2 Likes

Exactly, same here.

2 Likes

@kingokonn i’ll be reviewing this

2 Likes

@4undRaiser okay

2 Likes