Build parametric insurance smart contract through chainlink prediction organization

Time:2021-12-5

Build parametric insurance smart contract through chainlink prediction organization

Blockchain technology has unique attributes and can be used to create innovative decentralized insurance products, bringing many benefits to insurance suppliers and customers. In this technical tutorial, we will show you:

  • Main characteristics of decentralized parametric insurance contract
  • Why does chainlink Oracle play an important role in these new insurance products
  • Advantages of using chainlink price feed in decentralized insurance contracts
  • How to put everything together and create a usable parametric crop insurance contract
  • How to use the chainlink node to automatically update insurance contracts

The complete code of the following example can be viewed on Remix or GitHub, including all the functions mentioned below and all the required help functions.

Decentralized insurance

Decentralized insurance uses blockchain technology and smart contracts to replace traditional insurance protocols. Decentralized insurance products have three main characteristics.

Data driven automation

The most important point of decentralized insurance contract is that it is data-driven and automatic. This means that the insurance contract automatically executes the logic without manual intervention, and depends on the safe and accurate data obtained from the outside to determine the execution of the contract logic. These insurance smart contracts can also be connected with external outputs, such as payment processors or enterprise financial systems, so as to trigger payment.

Smart contract

Smart contract represents the insurance contract between the insurer and the customer. In essence, it is the insurer’s commitment to compensate for the loss, damage or liability specified by the customer. If it is parametric insurance, it is to hedge the risk of specific events. It contains all the details of the insurance contract, such as the index (e.g. rainfall in crop insurance contract), details of customer payment (e.g. wallet address, or customer ID of external payment system), contract date or period, measurement location of the index, threshold and agreed compensation value. Because insurance contracts are stored and executed on blockchains that usually run on a large number of nodes, they are highly deterministic and are not easy to be attacked or tampered with by hackers.

Claim settlement process

Different from traditional insurance contracts, in decentralized insurance contracts, the claim settlement process is automatically processed as part of contract execution. Customers do not need to submit claims, provide any evidence, or interact with insurance companies or smart contracts. When the smart contract thinks that compensation should occur, the compensation will be automatically triggered as part of contract execution. This can be done by paying customers directly in the chain, or through external payment channels or financial systems connected by smart contracts.

Create data-driven parametric insurance contracts

Now that we know what constitutes a decentralized parametric insurance contract, we will build a simple example to show the above three concepts. In this scenario, we will create a parameterized crop insurance contract with the following properties.

  • If there is no rain within the specified time, the contract will pay the agreed value to the customer, which is currently set to three days for demonstration. The contract will obtain rainfall data from two different data sources to alleviate any data integrity problems, and then average the results.
  • The contract will be fully funded by eth equivalent to the value of USD for the agreed compensation amount to ensure complete certainty when triggering the claim. It will use chainlink eth / USD price feed to determine the number of eth required for the contract.

Build parametric insurance smart contract through chainlink prediction organization
Decentralized insurance architecture

Establish insurance contract factory

First, we need to create a master “contract factory” contract, which will generate multiple insurance protocols and allow us to interact with them. This contract will be owned by the insurance company and provide sufficient Eth and link funds for each generated insurance contract to ensure that once the insurance contract is generated, it can perform all required operations, including compensation, throughout its lifetime.

First, our solidity code contains two contracts, one is the insuranceprovider contract and the other is the insurancecontract contract. The insuranceprovider contract generates many insurance contracts.

The constructor of the insuranceprovider contract initializes the chainlink eth / USD price feed on the Kovan network. The constructor of the insurancecontract contract is defined as follows, which will be further enriched later.

pragma solidity 0.4.24;
pragma experimental ABIEncoderV2;

//Truffle Imports
import "chainlink/contracts/ChainlinkClient.sol";
import "chainlink/contracts/vendor/Ownable.sol";
import "chainlink/contracts/interfaces/LinkTokenInterface.sol";

contract InsuranceProvider {
    
    constructor()   public payable {
        priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
    }

}

contract InsuranceContract is ChainlinkClient, Ownable  {
    
    constructor(address _client, uint _duration, uint _premium, uint _payoutValue, string _cropLocation, address _link, uint256 _oraclePaymentAmount)  payable Ownable() public {
        
    }
}

The general structure of the insuranceprovider contract is as follows:

  • Each generated contract is stored in the of the insurance contractcontractsThe Ethereum address of the generated contract is used as the key in the map. Value is an instantiated insurancecontract solid smart contract.
//here is where all the insurance contracts are stored.
mapping (address => InsuranceContract) contracts;
  • newContractThe function receives the required input and generates a new insurance contract, passing all the required parameters according to the previously defined constructor definition. It also sends eth equal to the payment amount, so that the generated contract funds are sufficient. It uses chainlink eth / USD price feed to complete this conversion. It then stores the generated contract incontractsMap and transfer enough links to the generated contract, so that it has enough data requests twice a day and a margin. This margin is to take into account the time that additional calls may be required after the contract expires. When the contract ends, any remaining links will be returned to the insurance provider.
function newContract(address _client, uint _duration, uint _premium, uint _payoutValue, string _cropLocation) public payable onlyOwner() returns(address) {
        

        //create contract, send payout amount so contract is fully funded plus a small buffer
        InsuranceContract i = (new InsuranceContract).value((_payoutValue * 1 ether).div(uint(getLatestPrice())))(_client, _duration, _premium, _payoutValue, _cropLocation, LINK_KOVAN,ORACLE_PAYMENT);
         
        contracts[address(i)] = i;  //store insurance contract in contracts Map
        
        //emit an event to say the contract has been created and funded
        emit contractCreated(address(i), msg.value, _payoutValue);
        
        //now that contract has been created, we need to fund it with enough LINK tokens to fulfil 1 Oracle request per day, with a small buffer added
        LinkTokenInterface link = LinkTokenInterface(i.getChainlinkToken());
        link.transfer(address(i), ((_duration.div(DAY_IN_SECONDS)) + 2) * ORACLE_PAYMENT.mul(2));
        
        
        return address(i);
        
    }
  • updateContractThe function is used to update the data of the insurance contract and check whether the threshold for triggering payment has been reached or whether the contract has reached the end date.
function updateContract(address _contract) external {
        InsuranceContract i = InsuranceContract(_contract);
        i.updateContract();
    }
  • last,getContractRainfallThe function returns the rainfall in millimeters for a given insurance contract,getContractRequestCountThe function is used to see how many data requests are successfully returned to the insurance contract.
function getContractRainfall(address _contract) external view returns(uint) {
        InsuranceContract i = InsuranceContract(_contract);
        return i.getCurrentRainfall();
    }

    function getContractRequestCount(address _contract) external view returns(uint) {
        InsuranceContract i = InsuranceContract(_contract);
        return i.getRequestCount();
    }

Get external data

The generated insurance contract needs external data to execute normally. This is where the chainlink network works, because you can use it to connect insurance contracts to multiple rainfall data sources. In this example, we will use job specification on two different chainlink nodes to obtain data from two different weather APIs, and then take the average value on the chain to get the final result. Both weather APIs need to register to get a free API key to use in each request.

  • WeatherBit Weather API
  • OpenWeather API
  • LinkPool GET>Uint256 Job
  • Steelblock GET>Uint256 Job

Once we write down the weather API key, the job specification ID and the Oracle contract above, we can create it nowInsuranceContractContract, fill in the required constant fields. In the production scenario, these constant fields are privately stored on the chainlink node and are not visible on the chain, but they are left in the contract for the convenience of following the demonstration. We also store the required JSON paths. When the chainlink node obtains weather data from each API, we need to traverse these paths to find the total daily rainfall (in millimeters).

    string constant WORLD_WEATHER_ONLINE_URL = "http://api.worldweatheronline.com/premium/v1/weather.ashx?";
    string constant WORLD_WEATHER_ONLINE_KEY = "insert API key here";
    string constant WORLD_WEATHER_ONLINE_PATH = "data.current_condition.0.precipMM";
      
    string constant WEATHERBIT_URL = "https://api.weatherbit.io/v2.0/current?";
    string constant WEATHERBIT_KEY = "insert API key here";
    string constant WEATHERBIT_PATH = "data.0.precip";

Completion of insurance contract

The next step is to complete the insurance contract, which represents the crop insurance contract between the customer and the insurance company.

The contract is instantiated and all required values are passed to the constructor. It has also done the following:

  • Use chainlink eth / USD price feed to check whether enough eth has been sent to ensure sufficient funds when triggering payment.
  • Set some variables required for contract execution
  • Set the jobid and Oracle array to contain the values obtained from the two job specifications in the “get external data” section above. However, if you want to run your own chainlink node, set both requests to use your job specification and Oracle contract so that you can see the output of each job. To do so, you need to create a new job specification on market.link. Like this example, you only need to modify the address in runlog initiator to your Oracle contract.
constructor(address _client, uint _duration, uint _premium, uint _payoutValue, string _cropLocation, 
                address _link, uint256 _oraclePaymentAmount)  payable Ownable() public {
        
        priceFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
        
        //initialize variables required for Chainlink Node interaction
        setChainlinkToken(_link);
        oraclePaymentAmount = _oraclePaymentAmount;
        
        //first ensure insurer has fully funded the contract
        require(msg.value >= _payoutValue.div(uint(getLatestPrice())), "Not enough funds sent to contract");
        
        //now initialize values for the contract
        insurer= msg.sender;
        client = _client;
        startDate = now + DAY_IN_SECONDS; //contract will be effective from the next day
        duration = _duration;
        premium = _premium;
        payoutValue = _payoutValue;
        daysWithoutRain = 0;
        contractActive = true;
        cropLocation = _cropLocation;
        
        //if you have your own node and job setup you can use it for both requests
        oracles[0] = 0x05c8fadf1798437c143683e665800d58a42b6e19;
        oracles[1] = 0x05c8fadf1798437c143683e665800d58a42b6e19;
        jobIds[0] = 'a17e8fbf4cbf46eeb79e04b3eb864a4e';
        jobIds[1] = 'a17e8fbf4cbf46eeb79e04b3eb864a4e';

        emit contractCreated(insurer,
                             client,
                             duration,
                             premium,
                             payoutValue);
    }

Then, we create a function to call to request rainfall data from each chainlink node and weather API. This function is called by the primary insurance provider contract. It establishes the required URL for each request, and then calls for each requestcheckRainfallFunction. But before that, it called acheckEndContractFunction to check whether the contract end date has arrived, and will continue only if the contract is still valid. thischeckEndContractThe function is defined as follows.

function updateContract() public onContractActive() returns (bytes32 requestId)   {
        //first call end contract in case of insurance contract duration expiring, if it hasn't then this function execution will resume
        checkEndContract();
        
        //contract may have been marked inactive above, only do request if needed
        if (contractActive) {
            dataRequestsSent = 0;
            //First build up a request to World Weather Online to get the current rainfall
            string memory url = string(abi.encodePacked(WORLD_WEATHER_ONLINE_URL, "key=",WORLD_WEATHER_ONLINE_KEY,"&q=",cropLocation,"&format=json&num_of_days=1"));
            checkRainfall(oracles[0], jobIds[0], url, WORLD_WEATHER_ONLINE_PATH);

            
            // Now build up the second request to WeatherBit
            url = string(abi.encodePacked(WEATHERBIT_URL, "city=",cropLocation,"&key=",WEATHERBIT_KEY));
            checkRainfall(oracles[1], jobIds[1], url, WEATHERBIT_PATH);    
        }
    }

Now we can createcheckRainfallFunction. This is the function that actually executes the external data request. It receives all the required parameters, establishes a request, and then sends it to the specified chainlink node Oracle contract.

In our presentation, pass tocheckRainfallIn function_pathThe value of the variable is used to traverse the path of the JSON returned by the request to find the current rainfall. These values depend on which weather API is called, and both options are stored in static variables in our contract and passed to as needed_pathFunction arguments.

  • World Weather Online API response format
  • Weatherbit API response format
    string constant WORLD_WEATHER_ONLINE_PATH = "data.current_condition.0.precipMM";
    string constant WEATHERBIT_PATH = "data.0.precip";

function checkRainfall(address _oracle, bytes32 _jobId, string _url, string _path) private onContractActive() returns (bytes32 requestId)   {

        //First build up a request to get the current rainfall
        Chainlink.Request memory req = buildChainlinkRequest(_jobId, address(this), this.checkRainfallCallBack.selector);
           
        req.add("get", _url); //sends the GET request to the oracle
        req.add("path", _path);
        req.addInt("times", 100);
        
        requestId = sendChainlinkRequestTo(_oracle, req, oraclePaymentAmount); 
            
        emit dataRequestSent(requestId);
    }

Then, we create a callback function that is called when the chainlink node sends back a response. This function receives the updated rainfall data at the specified location. If it is the second data update (that is, both requests have been responded), it performs the average calculation, and then updates the contract with the latest rainfall data.

The callback function also checks whether the parameterized loss is realized according to the rainfall data of the current contract. In this example, it checks the number of consecutive days without rain according to the given threshold. If the compensation conditions are met, callpayoutContractFunction.

 function checkRainfallCallBack(bytes32 _requestId, uint256 _rainfall) public recordChainlinkFulfillment(_requestId) onContractActive() callFrequencyOncePerDay()  {
        //set current temperature to value returned from Oracle, and store date this was retrieved (to avoid spam and gaming the contract)
       currentRainfallList[dataRequestsSent] = _rainfall; 
       dataRequestsSent = dataRequestsSent + 1;
       
       //set current rainfall to average of both values
       if (dataRequestsSent > 1) {
          currentRainfall = (currentRainfallList[0].add(currentRainfallList[1]).div(2));
          currentRainfallDateChecked = now;
          requestCount +=1;
        
          //check if payout conditions have been met, if so call payoutcontract, which should also end/kill contract at the end
          if (currentRainfall == 0 ) { //temp threshold has been  met, add a day of over threshold
              daysWithoutRain += 1;
          } else {
              //there was rain today, so reset daysWithoutRain parameter 
              daysWithoutRain = 0;
              emit ranfallThresholdReset(currentRainfall);
          }
       
          if (daysWithoutRain >= DROUGHT_DAYS_THRESDHOLD) {  // day threshold has been met
              //need to pay client out insurance amount
              payOutContract();
          }
       }
       
       emit dataReceived(_rainfall);
        
    }

Next we createpayoutContractFunction. As a claim processing step, this function performs the insurer to automatically pay the agreed value to the customer. We take extra care here to ensure that it can only be called when the contract is still active (i.e. not ended), and can only be called internally by other contract functions. It also returns any remaining links to the insurance provider master contract and sets the contract to the completed state to prevent any further action on it.

function payOutContract() private onContractActive()  {
        
        //Transfer agreed amount to client
        client.transfer(address(this).balance);
        
        //Transfer any remaining funds (premium) back to Insurer
        LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
        require(link.transfer(insurer, link.balanceOf(address(this))), "Unable to transfer");
        
        emit contractPaidOut(now, payoutValue, currentRainfall);
        
        //now that amount has been transferred, can end the contract 
        //mark contract as ended, so no future calls can be done
        contractActive = false;
        contractPaid = true;
    
    }  

Finally, we create a function to deal with such a scenario: the contract end date has arrived and the payment has not been triggered. We need to return the funds in the contract and mark it as end. This function checks whether the entire contract receives enough data requests. One data request needs to be received every day, and only one request is allowed to be missed in total. Therefore, if the duration of the contract is 30 days, there must be at least 29 successful data requests. If the contract receives sufficient requests within its life cycle, all funds will be returned to the insurance provider. Otherwise, if there are not enough data requests throughout the duration of the contract, the customer will automatically receive the premium as a refund, and the insurer will get back any remaining funds.

This scheme also uses chainlink eth / USD price feed to determine the correct eth quantity and return it to the customer. This check gives the customer some assurance that the insurance provider will not try to play with the contract by not updating rainfall data until the end date is reached. This function will also return any remaining link back to the insurance provider contract.

function checkEndContract() private onContractEnded()   {
        //Insurer needs to have performed at least 1 weather call per day to be eligible to retrieve funds back.
        //We will allow for 1 missed weather call to account for unexpected issues on a given day.
        if (requestCount >= (duration.div(DAY_IN_SECONDS) - 1)) {
            //return funds back to insurance provider then end/kill the contract
            insurer.transfer(address(this).balance);
        } else { //insurer hasn't done the minimum number of data requests, client is eligible to receive his premium back
            client.transfer(premium.div(uint(getLatestPrice())));
            insurer.transfer(address(this).balance);
        }
        
        //transfer any remaining LINK tokens back to the insurer
        LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
        require(link.transfer(insurer, link.balanceOf(address(this))), "Unable to transfer remaining LINK tokens");
        
        //mark contract as ended, so no future state changes can occur on the contract
        contractActive = false;
        emit contractEnded(now, address(this).balance);
    }

Deployment and testing contract

First, we need to deploy the insuranceprovider contract and use some Eth and link to fund it for use in the generated insurancecontract contract.

After completing these tasks, we can create a new insurancecontract and pass the required values. Please pay attention to the following points.

  • The unit of duration is seconds. In this demonstration, 1 day is shortened to 60 seconds (as specified in the day_in_seconds constant), so the contract period of 300 seconds represents 5 days.
  • The premium and payoutvalue parameters are in USD and multiplied by 100000000. For example, 100 USD is 1000000000.

Build parametric insurance smart contract through chainlink prediction organization
Create a new insurance contract
After the insurance contract is generated, we can obtain its address through the transaction in Etherscan or through the transaction output.

Build parametric insurance smart contract through chainlink prediction organization

View the generated contract on Etherscan

Then, we can pass in the generated contract addressupdateContractFunction to start transferring rainfall data into the contract.

Build parametric insurance smart contract through chainlink prediction organization

Renewal of insurance contract

When both chainlink nodes process the job request and return a result, we can callgetContractRainfallandgetContractRequestCountFunction to see the update of average rainfall and the increase in the number of data requests. A data request means that both nodes return a result, which is averaged and stored in the contract. In this example, Iowa currently has an average rainfall of 0.6mm from two data sources. We can also call the help functiongetContractStatusTo verify that the contract is still active.

Build parametric insurance smart contract through chainlink prediction organization

Get the status of the insurance contract

During the validity of the contract (this demonstration is 5 times / 300 seconds), repeat this step every day (this example is 1 minute) to end the contract. If the number of days without rain reachesDROUGHT_DAYS_THRESHOLDThe contract will pay the agreed amount to the customer and the contract status ends.

In order to achieve the purpose of demonstration, we created an insurance contract for a place without rainfall, and repeated the above steps three times in three minutes to demonstrate what happened during compensation. In this case, we can see that the latest rainfall is 0, the number of requests is 3, and the contract is no longer active.

Build parametric insurance smart contract through chainlink prediction organization

Get the status of the insurance contract

If we go to Ethereum to check the contract, we will find that the agreed USD eth compensation value has been transferred back to the customer wallet address specified when the contract was created above, and the insurance contract no longer holds any eth or link. Since the insurance contract is now completed, any subsequent operations on the insurance contract will be rejected.

Build parametric insurance smart contract through chainlink prediction organization
Verify the payment of insurance contract

Automatically update data

In the current version of the contract, someone must call it manuallyupdateContractFunction to let the contract communicate with the chainlink node and obtain rainfall data. This is not ideal because it needs to be called multiple times throughout the duration of the contract. A good automation method is to use the cron initiator of the chainlink node.

Cron initiator is a method of scheduling circular jobs on chainlink nodes using simple cron syntax. In this case, what we can do is to create a new job specification on the chainlink node and use the cron initiator to trigger the job specification once a day. But for the purposes of this demonstration, we will use the constant seconds mentioned earlier_ IN_ Day, set it to trigger every minute.

The rest of the job specification will simply call the deployed smart contract each time cron job triggers the execution of the job specificationupdateContractFunction. The idea is that the insurance front end will have all the relevant details (contract address, start date, end date) and can pass them in.

{
   "initiators": [
      {
         "type": "cron",
         "params": {
            "schedule": "CRON_TZ=UTC 0/' + 6 + ' * * * * *"
         }
      }
   ],
   "tasks": [
      {
         "type": "ethtx",
         "confirmations": 0,
         "params": {
            "address": "' + address + '",
            "functionSelector": "checkContract()"
         }
      }
   ],
   "startAt": "' + startDate + '",
   "endAt": "' + endDate + '"
}

Our idea is that the decentralized insurance application front end will send a request to the chainlink node API, dynamically generate a new job specification, and provide all the correct details required for the node to automatically start updating the insurance contract regularly, without having to manually create this work specification through the chainlink node front end interface.

To do this, we first need the IP address and port of the chainlink node, as well as the user name and password of the login node. These are the cookies used to generate the next request.

curl -c cookiefile -X POST -H 'Content-Type: application/json' -d '{"email":"[email protected]", "password":"password"}' http://35.189.58.211:6688/sessions

After completing these tasks, we will get a response to show that the authentication is successful.

{"data":{"type":"session","id":"sessionID","attributes":{"authenticated":true}}}

Then we can send another post request to the chainlink node API, this time to the / V2 / specs endpoint. The JSON in the request is the address of the generated insurance contract updated regularly, as well as the start and end date / time (and the specified time offset if necessary), so that the node knows when to stop updating the insurance contract regularly.

curl  -b cookiefile -X POST -H 'Content-Type: application/json' -d '{"initiators":[{"type":"cron","params":{"schedule":"CRON_TZ=UTC 0/60 * * * * *"}}],"tasks":[{"type":"ethtx","confirmations":0,"params":{"address":"0xdC71C577A67058fE1fF4Df8654291e00deC28Fbf","functionSelector":"updateContract()"}}],"startAt": "2020-11-10T15:37:00+10:30","endAt": "2020-11-10T15:42:00+10:30"}' http://35.189.58.211:6688/v2/specs

This command will return a success message, which contains the details of the generated job specification. After that, you can log in to the front end of the chainlink node and see the newly created job specification.

Build parametric insurance smart contract through chainlink prediction organization
Cron starter working specification syntax
After the job specification is created, it will start executing the request according to the parameters set in the cron initiator. We can monitor this at the front end of the chainlink node.
Build parametric insurance smart contract through chainlink prediction organization
After the node successfully completes the request, it can return to the smart contract and see that its status has been successfully updated.

Build parametric insurance smart contract through chainlink prediction organization

summary

In this technical article, we have demonstrated how to establish a decentralized crop insurance product to compensate farmers for the drought period. We have shown the importance of accurate and decentralized data in insurance contracts and the role of chainlink Oracle in providing these data safely.

We also demonstrated how to use deterministic smart contracts connected to external data and events to completely reduce the overhead and management cost of handling insurance claims, and how to use chainlink decentralized pricing to accurately determine the correct compensation amount when the contract terms are based on US dollars but paid in cryptocurrency. Finally, we also demonstrated how the chainlink node cron initiator can be used in conjunction with the chainlink node API to automatically schedule and execute smart contract updates.

Although this demonstration contains many functions, it can be used as a basic template to build a complete and feature rich decentralized insurance product. Developers can build on this template in various ways, such as removing manual data aggregation and using chainlink’s aggregator or precoordinator contract. Another option is to securitize insurance contracts as collateral for the defi ecosystem or other markets.

If you are a developer and want to connect your smart contract to off chain data and systems, please visit the development documentation and join the technical discussion on discord. If you want to arrange a phone call to discuss deeper integration, please contact here.