Introduction
In the world of finance, insurance is a key player. Traditional insurance systems, however, often suffer from bureaucratic processes and a lack of transparency. By leveraging blockchain technology, we can create an insurance system that is not only efficient but also transparent. In this tutorial, we’ll guide you on how to create a simple blockchain-based insurance platform on the Celo network. With Solidity for our smart contracts, JavaScript for general programming, and React and Tailwind Components for our user interface, we’ll automate claim processing and payout. Let’s dive in!
Prerequisites
Before proceeding, you should have a basic understanding of blockchain technology, specifically the Celo network. You should be comfortable with programming in JavaScript and have experience with Solidity for smart contract development. Familiarity with React and Tailwind CSS will be advantageous for the frontend development. Completion of the tutorial “Connect to Celo using hardhat” is also recommended.
Requirements
Before we begin, ensure that you have the following tools installed:
- Node.js, version 12.0.1 or later
- Metamask browser extension for interacting with the Celo network
- Truffle for developing our smart contracts
- React for building our user interface
- A Celo Faucet Account
To get a Celo test account, you can follow these steps:
- Visit the Celo Faucet at Fund Your Testnet Account
2. Click on “Connect Celo Extension Wallet” and follow the prompts to connect your Celo Extension Wallet.
3. After connecting your wallet, you will see your account address.
4. Click “Request Funds” to receive test tokens (cGLD and cUSD) on the Alfajores testnet. The faucet will send test tokens to your account.
5. Make sure your Celo Extension Wallet is set to the Alfajores Testnet.
Now you can use this test account in the tutorial. Ensure that you’re interacting with the Alfajores Testnet when working on your project.
Getting Started: Setting Up Our Development Environment
First, we need to set up our development environment. We will begin by installing the necessary dependencies for our project. Create a new folder for the project and initialize it with Node.js and NPM:
mkdir celo-insurance && cd celo-insurance
npm init -y
Initialize a new Truffle project:
truffle init
Install the OpenZeppelin Contracts library:
npm install @openzeppelin/contracts
Building Our Smart Contracts
Our insurance platform will rely on two main smart contracts: the InsuranceProvider
contract and the Policy
contract.
The InsuranceProvider
contract will handle the creation of new policies, storing them, and handling payouts. The Policy
contract will represent an individual insurance policy and will handle the specifics of each policy, such as the policyholder’s information, the policy terms, and claim filing.
The InsuranceProvider
Contract
First, we’ll create our InsuranceProvider
contract. This contract will manage the creation of new policies and handle the storage of policy contracts. Let’s define the basic structure:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "@openzeppelin/contracts/access/Ownable.sol";
import "./Policy.sol";
contract InsuranceProvider is Ownable {
uint256 public policyCounter;
mapping(uint256 => Policy) public policies;
event PolicyCreated(uint256 policyId, address policyAddress);
function createPolicy(
address policyholder,
uint256 premium,
uint256 coverageAmount
) public onlyOwner returns (uint256) {
policyCounter++;
Policy policy = new Policy(policyholder, premium, coverageAmount);
policies[policyCounter] = policy;
emit PolicyCreated(policyCounter, address(policy));
return policyCounter;
}
}
In this contract, we’re using the OpenZeppelin library for the Ownable
contract which restricts certain functions such as creating a policy to only the owner of the contract.
The createPolicy
function allows the owner to create a new policy contract. It takes the policyholder’s address and the premium and coverage amount for the policy. It then increments the policyCounter
, creates a new Policy
contract, and stores it in our policies
mapping.
We also emit a PolicyCreated
event every time a new policy is created. This allows front-end applications to listen for this event and react accordingly.
The Policy
Contract
Next, let’s create our Policy
contract. This contract will handle policy specifics such as the policyholder’s information, policy terms, and claim filing.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "@openzeppelin/contracts/access/AccessControl.sol";
contract Policy is AccessControl {
bytes32 public constant POLICYHOLDER_ROLE = keccak256("POLICYHOLDER_ROLE");
address public insuranceProvider;
uint256 public premium;
uint256 public coverageAmount;
uint256 public createdAt;
bool public active;
event ClaimFiled(uint256 amount);
event PolicyActivated();
constructor(
address policyholder,
uint256 _premium,
uint256 _coverageAmount
) {
_setupRole(POLICYHOLDER_ROLE, policyholder);
insuranceProvider = msg.sender;
premium = _premium;
coverageAmount = _coverageAmount;
active = false;
createdAt = block.timestamp;
}
function fileClaim(uint256 amount) public {
require(hasRole(POLICYHOLDER_ROLE, msg.sender), "Caller is not the policyholder");
require(active, "Policy is not active");
require(amount <= coverageAmount, "Claim amount exceeds coverage amount");
coverageAmount -= amount;
emit ClaimFiled(amount);
}
function activate() public {
require(msg.sender == insuranceProvider, "Caller is not the insurance provider");
require(!active, "Policy is already active");
active = true;
emit PolicyActivated();
}
}
In this contract, we’re using OpenZeppelin’s AccessControl
contract to manage roles. We define a POLICYHOLDER_ROLE
which will be assigned to the policyholder upon creation of the policy.
The fileClaim
function allows the policyholder to file a claim, as long as the policy is active and the claim amount does not exceed the coverage amount. If a claim is successfully filed, the coverage amount is reduced by the claim amount, and a ClaimFiled
event is emitted.
The activate
function allows the insurance provider to activate a policy. This can be done only by the insurance provider, and only if the policy is not already active. Upon successful activation, a PolicyActivated
event is emitted.
Compiling and Deploying the Smart Contract to the Celo Test Network
-
Create a new file
migrations/2_deploy_contracts.js
and add the following code:const InsuranceProvider = artifacts.require("InsuranceProvider"); module.exports = function(deployer) { deployer.deploy(InsuranceProvider); };
-
Update the
truffle-config.js
file to include the Celo-Alfajores network configuration:
const ContractKit = require("@celo/contractkit");
const Web3 = require("web3");
const web3 = new Web3("https://alfajores-forno.celo-testnet.org");
const kit = ContractKit.newKitFromWeb3(web3);
// Add your private key and account address
const privateKey = "your_private_key";
const accountAddress = "your_account_address";
kit.addAccount(privateKey);
module.exports = {
networks: {
development: { host: "127.0.0.1", port: 7545, network_id: "*" },
alfajores: {
provider: kit.connection.web3.currentProvider,
network_id: 44787,
from: accountAddress,
gas: 6721975,
gasPrice: 20000000000,
},
},
compilers: {
solc: {
version: "0.8.0",
},
},
};
- Compile the smart contract
Run the following command to compile the smart contract:
truffle compile
- Deploy the smart contract to the Celo network
Run the following command to deploy the MedicalRecord
smart contract to the Celo network:
truffle migrate --network alfajores
After the deployment is successful, you will see the smart contract address in the console output. Take note of the deployed contract address for future use.
Frontend Application with React and Tailwind CSS
Now that we have our smart contracts, let’s create a frontend application to interact with them. We will use React for our application framework and Tailwind CSS for our styling.
First, let’s create our React application and install the necessary dependencies.
npx create-react-app insurance-app
cd insurance-app
npm install ethers @openzeppelin/contracts tailwindcss@npm:@tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
Next, initialize Tailwind CSS by creating a configuration file:
npx tailwindcss init
In the tailwind.config.js
file, replace the contents with the following:
module.exports = {
purge: [],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
In src/index.css
, add Tailwind’s base, components, and utilities directives:
import { ethers } from 'ethers';
import InsuranceProvider from './InsuranceProvider.json';
import Policy from './Policy.json';
import { useState, useEffect } from 'react';
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = provider.getSigner();
const InsuranceProviderAddress = '0x36Bf04e9595E08bc6b3B5FBcF0CEd532a2f071a8';
const insuranceProvider = new ethers.Contract(InsuranceProviderAddress, InsuranceProvider.abi, signer);
function App() {
const [policies, setPolicies] = useState([]);
const [premium, setPremium] = useState();
const [coverage, setCoverage] = useState();
const [claimAmount, setClaimAmount] = useState();
const [selectedPolicyId, setSelectedPolicyId] = useState();
useEffect(() => {
async function fetchPolicies() {
const policyCount = await insuranceProvider.getPolicyCount();
const policyPromises = Array.from({length: policyCount}, (_, i) => insuranceProvider.policies(i));
const policyAddresses = await Promise.all(policyPromises);
const policyObjects = policyAddresses.map(address => {
return {
id: address,
contract: new ethers.Contract(address, Policy.abi, signer)
}
});
setPolicies(policyObjects);
}
fetchPolicies();
}, []);
async function createPolicy() {
try {
const tx = await insuranceProvider.createPolicy(signer.getAddress(), premium, coverage);
await tx.wait();
alert('Policy created');
} catch (error) {
console.error('An error occurred while creating the policy:', error);
}
}
async function fileClaim() {
try {
const selectedPolicy = policies.find(policy => policy.id === selectedPolicyId);
const tx = await selectedPolicy.contract.fileClaim(claimAmount);
await tx.wait();
alert('Claim filed');
} catch (error) {
console.error('An error occurred while filing the claim:', error);
}
}
async function activatePolicy() {
try {
const selectedPolicy = policies.find(policy => policy.id === selectedPolicyId);
const tx = await selectedPolicy.contract.activate();
await tx.wait();
alert('Policy activated');
} catch (error) {
console.error('An error occurred while activating the policy:', error);
}
}
return (
<div>
<input type="number" value={premium} onChange={e => setPremium(e.target.value)} placeholder="Premium" />
<input type="number" value={coverage} onChange={e => setCoverage(e.target.value)} placeholder="Coverage" />
<button onClick={createPolicy}>Create Policy</button>
<select value={selectedPolicyId} onChange={e => setSelectedPolicyId(e.target.value)}>
{policies.map(policy => <option key={policy.id} value={policy.id}>{policy.id}</option>)}
</select>
<input type="number" value={claimAmount} onChange={e => setClaimAmount(e.target.value)} placeholder="Claim Amount" />
<button onClick={fileClaim}>File Claim</button>
<button onClick={activatePolicy}>Activate Policy</button>
{policies.map(policy => {
return (
<div key={policy.id}>
<h2>Policy: {policy.id}</h2>
<button onClick={() => checkPolicyState(policy)}>Check Status</button>
<div id={`policy-${policy.id}`}></div>
</div>
);
})}
</div>
);
async function checkPolicyState(policy) {
try {
const isActive = await policy.contract.isActive();
const status = isActive ? 'Active' : 'Inactive';
const claimAmount = await policy.contract.claimAmount();
const statusElement = document.getElementById(`policy-${policy.id}`);
statusElement.textContent = `Status: ${status}, Claim Amount: ${claimAmount.toString()}`;
} catch (error) {
console.error('An error occurred while checking the policy status:', error);
}
}
}
export default App;
This is a basic example and a real production application would have more features such as a form to input the premium and coverage amounts, a list of all the created policies, and error handling, but this serves as a good starting point.
In this version of the application, users can create new policies, specify their premium and coverage amounts, file claims, activate their policies, and view their policy details. All created policies are tracked, and the status of each policy can be individually viewed.
Each policy is represented with its ID and has its own status checking button. Clicking the button will show the current status of the policy and the claim amount.
To test the application, run the following command in your terminal:
npm start
This will open a browser window with the application.
Conclusion
Congratulations! You have successfully built a basic insurance platform on the Celo network using Solidity for smart contracts, JavaScript for interacting with the blockchain, and React for the frontend. You learned how to create an insurance policy, file a claim, and check the status of a policy.
Next Steps
If you want to delve deeper into the world of DApp development, here are some suggestions:
- Refactor the contract to include more complex policy structures and claims processing mechanisms.
- Integrate a database to track more information about policies and claims.
- Implement a frontend library like Redux for better state management.
- Learn about the gas costs of Ethereum transactions and how you can optimize your smart contracts to save gas.
References
This tutorial was created using resources from the official documentation of Celo SDK, Solidity, Javascript, and React. Special thanks to the open-source community for providing such comprehensive resources.
Finally, you can access this tutorial code repo here.
About the Author
Oluwalana is a blockchain developer and technical writer, experienced in creating decentralized applications on Ethereum and Celo platforms. With a passion for knowledge-sharing, Oluwalana has authored various tutorials and articles on blockchain and other emerging technologies. Follow me on Twitter for insights on blockchain and emerging tech. For professional inquiries, kindly connect with me on LinkedIn and explore my work on GitHub.