This is the first part of the GIF Tutorial aimed at explaining how to build products on top of Etherisc’s Generic Insurance Framework. In this article, we will go through all the steps of creating your very first product on top of GIF. So put your fingers on the keyboard and let’s get moving.

First of all, what is “GIF”? The acronym stands for “Generic Insurance Framework”. Its main goal is to provide a simple and clean interface to build decentralized insurance products. Core contracts are aimed to be deployed on-chain.

A product contract acts as a client and utilizes generic methods which are important parts of every policy lifecycle. From this point of view, a product’s business logic could be defined in a single smart contract and all the hard work is delegated to the GIF.

Some insurance terminology

Before we start, let’s define some insurance specific terminology.

An insurance company is a legal body which is qualified in accepting risks against a premium. In most countries, insurance companies need a license. The insurance company is not necessarily the entity which runs the technical process; it can also be outsourced to a service provider. Most of the other functions in the value chain can also be taken by other parties (e.g. distribution, claims management etc.). The only function which cannot be delegated is the actual transfer of the risk.

is a legal body which is qualified in accepting risks against a premium. In most countries, insurance companies need a license. The insurance company is not necessarily the entity which runs the technical process; it can also be outsourced to a service provider. Most of the other functions in the value chain can also be taken by other parties (e.g. distribution, claims management etc.). The only function which cannot be delegated is the actual transfer of the risk. An application is a formal request of the customer for an insurance policy. It is a signal from customer to the insurance company: “I ask for insurance cover”.

is a formal request of the customer for an insurance policy. It is a signal from customer to the insurance company: “I ask for insurance cover”. The application is then checked by the insurance company and either accepted or declined. The process of accepting an application is called underwriting. The person who performs this action is an “ underwriter ”. By underwriting, the insurance company commits itself to transfer the risk from the customer to the insurance company. This fact is documented in a contract. This contract is called a policy . A contract is binding for both parties: The customer is committed to paying the premium and the insurance company is committed to covering a loss in case an insured event occurs.

The person who performs this action is an “ ”. By underwriting, the insurance company commits itself to transfer the risk from the customer to the insurance company. This fact is documented in a contract. This contract is called a . A contract is binding for both parties: The customer is committed to paying the premium and the insurance company is committed to covering a loss in case an insured event occurs. If an insured event occurs, the customer typically files a claim — a step which can be omitted in case of parametric insurance, where a data-driven oracle takes the responsibility to file the claim automatically.

— a step which can be omitted in case of parametric insurance, where a data-driven oracle takes the responsibility to file the claim automatically. The claim is then checked by the insurance company and if the insurance company decides that the claim is valid then a payout is triggered (e.g. the insurance company will deny the claim if the premium has not been paid). Again, in a parametric case, the insurance company can delegate the decision to a rule-based engine or to an external independent oracle.

is triggered (e.g. the insurance company will deny the claim if the premium has not been paid). Again, in a parametric case, the insurance company can delegate the decision to a rule-based engine or to an external independent oracle. Payouts can be done in one or more parts.

GIF provides generic functions for this lifecycle and a generic workflow which controls the sequence of states. In the next section, we will describe these functions and how they work in detail. Every function can be assigned to a role, which can be defined by the product designer. Typical roles are e.g. underwriter, claims manager, application manager, bookkeeper but of course the product designer is not limited to these.

Generic Lifecycle Functions

Here is the list of magic methods available for every product:

_register (each product needs to be registered in order to get access to the GIF functionality)

(each product needs to be registered in order to get access to the GIF functionality) _newApplication (to put application data into the storage)

(to put application data into the storage) _underwrite (to underwite an application and create a new policy)

(to underwite an application and create a new policy) _decline (to decline an application)

(to decline an application) _newClaim (to create a new claim for policy)

(to create a new claim for policy) _confirmClaim (to confirm a claim and create a payout object)

(to confirm a claim and create a payout object) _declineClaim (to decline a claim)

(to decline a claim) _payout (to confirm an off-chain payout)

(to confirm an off-chain payout) _request (to request data or action from the oracle)

(to request data or action from the oracle) _createRole (to create roles for actors)

(to create roles for actors) _addRoleToAccount (to assign roles to actors)

The names of these methods start with an underscore to highlight the fact that these are internal methods and you can override them in your product. For example, you are free to have the newApplication method in your contract and use _newApplication in it as well.

Idea for a new product

In this article, we will build a very simple product. The main goal is to provide you a path through the process of building your own product. For this case, we will build an insurance product for an electronic store, which sells screens for laptops.

Let’s assume that a screen is unrepairable. If it’s broken (and of course only if it is an insurance case), a payout should be equal to the price of the screen.

The pricing model is very simple. Let’s assume that a policy premium should be 10% of the laptop screen price.

A policy expiration period is 1 year.

Thу following actors will take part in the process: an application manager, an underwriter, a claims manager and a bookkeeper.

Here are the steps of the product life cycle:

The customer applies for a policy.

The application manager creates an application for the policy on behalf of the customer.

The underwriter underwrites or declines the application.

If the application is underwritten, a new policy is issued.

If something wrong happens with the laptop screen, the customer can create a claim.

The claims manager confirms or declines this claim.

If the claim is confirmed, a new payout is created.

Then, the bookkeeper should pay to the customer and confirm that the payout has been executed.

After the expiration period ends, the policy should be expired.

Setting up a development environment

Prerequisites:

Node.js should be installed on your computer. If you already have it, then check its version. It shouldn’t be too old. Use at least version 8.12.0 or later (to see if Node.js is installed, open your terminal and type “node -v”, this should show you a current version).

The Etherisc team has created a “sandbox” environment where product builders are able to experiment with their products in a test mode. The sandbox consists of 3 parts:

Core smart contracts deployed to the Rinkeby testnet.

Microservices deployed to the Kubernetes cluster. Microservices represent a utility layer. They are available to provide useful functionality for product builders (off-chain data storage, watch Ethereum events, interact with smart contracts (call data and send transactions), send emails, telegram notifications, etc.). More detailed information about available microservices will be published in the next articles.

Microservices represent a utility layer. They are available to provide useful functionality for product builders (off-chain data storage, watch Ethereum events, interact with smart contracts (call data and send transactions), send emails, telegram notifications, etc.). More detailed information about available microservices will be published in the next articles. CLI tool (gifcli). A command line interface to interact with the sandbox environment.

Install gifcli tool.

npm install -g @etherisc/gifcli

Check the “gifcli version” command. When installed correctly, it should return a version.

gifcli version

Registration in sandbox

In order to create products, you have to be registered as a product owner. Use the “gifcli user:register” command to register in the sandbox. It will require to insert some information: firstname, lastname, email, and password.

gifcli user:register

After the registration, the product owner is permitted to create a product. Let’s create the first one.

gifcli product:create

That’s it. Now we can start creating a smart contract for the product.

Coding part

Configure a Truffle project

Now we are ready to build a product. Create a new directory for it.

mkdir estore-insurance

cd estore-insurance

Install required dependencies:

Truffle — a development environment for smart contracts, truffle-hdwallet-provider — we use it to sign transactions during the deployment with a mnemonic account.

@etherisc/gif — GIF core smart contracts — we need the Product.sol contract to inherit from it.

openzeppelin-solidity — a library for smart contract development — we will use Ownerable.sol from it.

The last command here will bootstrap a typical Truffle project for us.

npm init -y npm install truffle [email protected] @etherisc/gif openzeppelin-solidity ./node_modules/.bin/truffle init

Then add “compile” and “migrate” commands to the scripts section in the package.json file. The first one will be used to compile smart contracts. The second one is needed to deploy them to the relevant network (we will deploy to the Rinkeby testnet).

Then add “compile” and “migrate” commands to the scripts section in the package.json file. The first one will be used to compile smart contracts. The second one is needed to deploy them to the relevant network (we will deploy to the Rinkeby testnet).

...

"scripts": {

"compile": "truffle compile",

"migrate": "truffle migrate"

},

...

File: ./package.json

We are almost ready. Now we need to configure Truffle. Open the truffle-config.js file and replace its content with the lines below:

const HDWalletProvider = require("truffle-hdwallet-provider"); module.exports = {

networks: {

rinkeby: {

provider: () => new

HDWalletProvider("<! — MNEMONIC →","https://rinkeby.infura.io/v3/<!--INFURA-KEY-->"),

network_id: 4,

confirmation: 2,

timeoutBlocks: 200,

skipDryRun: true,

gas: 6600000,

gasPrice: 10 * (10 ** 9),

},

},

compilers: {

solc: {

version: "0.5.2",

settings: {

optimizer: {

enabled: true,

runs: 200,

},

},

},

},

};

File: ./truffle-config.js

As you can see from the code, in order to deploy the contract you will need to provide MNEMONIC and INFURA-KEY variables. MNEMONIC could be generated on this website: https://iancoleman.io/bip39/#english. Set Coin as “ETH — Ethereum”, find your account at the top of Derived Addresses.

Then, fund your account with some test ethers (you can get it from https://faucet.rinkeby.io/). To get INFURA-KEY, register on https://infura.io/register, create a new project, and get your key.

Create a smart contract

Create a new file “EStoreInsurance.sol” in the contracts directory with the following content:

pragma solidity 0.5.2; import "@etherisc/gif/contracts/Product.sol"; contract EStoreInsurance is Product {

bytes32 public constant NAME = "EStoreInsurance";

bytes32 public constant POLICY_FLOW = "PolicyFlowDefault";



constructor(address _productService)

public

Product(_productService, NAME, POLICY_FLOW) {}

}

File: ./contracts/EStoreInsurance.sol

First of all, we imported the Product contract from the GIF package and inherited from it. From now, if we deploy this contract, our product will become a client for GIF contracts through the ProductService contract (we pass its address as a constructor argument). Look at these magic constants: NAME and POLICY_FLOW. NAME is a name for your product. The only restriction here is that it should fit the 32-bytes length. The POLICY_FLOW constant should be defined to choose the core contract, which will represent the policy lifecycle for this product. Right now, we have only this contract — “PolicyFlowDefault”. And we have plans to create different versions of it for different policy lifecycles.

In the constructor, we call the Product’s constructor and pass the address of ProductService to it (this address is published in GIF repository on Github). The Product’s constructor will call the “register” function and your product contract will be proposed as a product (It should be approved by the administrator before this product starts to accept new applications. But don’t worry, in the sandbox, the product will be approved automatically).

Then let’s define a risk. So far, as each product has its own set of fields we keep such data on the product contract side. In this particular case, risk contains brand, model, and year fields.

...

bytes32 public constant POLICY_FLOW = "PolicyFlowDefault"; struct Risk {

bytes32 brand;

bytes32 model;

uint256 year;

} mapping(bytes32 => Risk) public risks; constructor(address _productService)

...

File: ./contracts/EStoreInsurance.sol

Add a public function to calculate a premium based on a certain price. This function could be used to provide quotas to applicants.

...

constructor(address _productService)

public

Product(_productService, NAME, POLICY_FLOW) {} function getQuote(uint256 _price) public pure returns (uint256 _premium) {

require(_price > 0, "ERROR::INVALID PRICE");

_premium = _price.div(10);

}

File: ./contracts/EStoreInsurance.sol

Now we can define a function that will be used to apply for a policy.

...

_premium = _price.div(10);

} function applyForPolicy(

bytes32 _brand,

bytes32 _model,

uint256 _year,

uint256 _price,

uint256 _premium,

bytes32 _currency,

bytes32 _bpExternalKey

)external onlySandbox {

require(_premium > 0, "ERROR:INVALID_PREMIUM");

require(getQuote(_price) == _premium, "ERROR::INVALID_PREMIUM"); bytes32 riskId = keccak256(abi.encodePacked(_brand, _model, _year));

risks[riskId] = Risk(_brand, _model, _year); uint256[] memory payoutOptions = new uint256[](1);

payoutOptions[0] = _price; uint256 applicationId = _newApplication(_bpExternalKey, _premium, _currency, payoutOptions); emit LogRequestUnderwriter(applicationId);

}

File: ./contracts/EStoreInsurance.sol

We use the modifier “onlySandbox” here. It restricts permissions for this method to the sandbox account. As you will see later, as a product builder you can utilize the sandbox microservice to send transactions to your contract. The applyForPolicy method also contains the _newApplication invocation. It will create a new application in GIF core contracts.

Create other required functions in the same manner.

...

emit LogRequestUnderwriter(applicationId);

} function underwriteApplication(uint256 _applicationId) external onlySandbox

{

uint256 policyId = _underwrite(_applicationId); emit LogApplicationUnderwritten(_applicationId, policyId);

} function declineApplication(uint256 _applicationId) external onlySandbox {

_decline(_applicationId); emit LogApplicationDeclined(_applicationId);

} function createClaim(uint256 _policyId) external onlySandbox {

uint256 claimId = _newClaim(_policyId); emit LogRequestClaimsManager(_policyId, claimId);

} function confirmClaim(uint256 _applicationId, uint256 _claimId) external onlySandbox {

uint256[] memory payoutOptions = _getPayoutOptions(_applicationId);

uint256 payoutId = _confirmClaim(_claimId, payoutOptions[0]); emit LogRequestPayout(payoutId);

} function confirmPayout(uint256 _claimId, uint256 _amount) external

onlySandbox {

_payout(_claimId, _amount); emit LogPayout(_claimId, _amount);

}

File: ./contracts/EStoreInsurance.sol

Don’t forget to define events for your product.

contract EStoreInsurance is Product {

event LogRequestUnderwriter(uint256 applicationId);

event LogApplicationUnderwritten(uint256 applicationId, uint256 policyId);

event LogApplicationDeclined(uint256 applicationId);

event LogRequestClaimsManager(uint256 policyId, uint256 claimId);

event LogClaimDeclined(uint256 claimId);

event LogRequestPayout(uint256 payoutId);

event LogPayout(uint256 claimId, uint256 amount); bytes32 public constant NAME = "EStoreInsurance";

...

File: ./contracts/EStoreInsurance.sol

Now is the time to deploy this contract. In the migrations folder, create the “2_deploy_EStoreInsurance.js” file with the content:

const EStoreInsurance = artifacts.require("EStoreInsurance"); const GIF_PRODUCT_SERVICE_CONTRACT ="0x6520354fa128cc6483B9662548A597f7FcB7a687"; module.exports = deployer => deployer.deploy(EStoreInsurance, GIF_PRODUCT_SERVICE_CONTRACT);

File: ./migrations/2_deploy_EStoreInsurance.js

Look how we use the address of the ProductService contract to define GIF core contract. This address is published in GIF repository on Github. The product will interact with it.

npm run compile

If everything is fine, the contract will be compiled without issues.

npm run migrate -- --network rinkeby

The contract will be deployed on the Rinkeby Ethereum testnet.

Interact with the smart contract

Send artifacts of your deployment to GIF Sandbox:

gifcli artifact:send --file ./build/contracts/EStoreInsurance.json --network rinkeby

After that, the product will be approved automatically and you can start interacting with it. Enter the console mode:

gifcli console

Run these commands one by one to go through the whole policy lifecycle from application creation to policy payout.

First of all, execute this command. If your product is approved, you will get information about it.

gif.product.get()

Create a new customer. This data is private and available only to the product owner.

Now start a new business process.

gif.bp.create({ customerId: "GET-CUSTOMER-ID-FROM-PREV-COMMAND" })

Here is how you can call your contract data:

gif.contract.call("EStoreInsurance", "getQuote", [100])

Now let’s apply for a policy:

gif.contract.send("EStoreInsurance", "applyForPolicy", [ "APPLE", "A1278", "2012", 1000, 100, "EUR", "PUT-BP-KEY-HERE"])

Check if it is created:

gif.application.getById(1)

gif.application.list()

Underwrite the application. A new policy should be issued.

gif.contract.send("EStoreInsurance", "underwriteApplication", [1])

Check the new policy:

gif.policy.getById(1) gif.policy.list()

Create a claim for the policy:

gif.contract.send("EStoreInsurance", "createClaim", [1])

Check the claim:

gif.claim.getById(1) gif.claim.list()

Confirm the claim. A new payout should be created.

gif.contract.send("EStoreInsurance", "confirmClaim", [ 1, 1 ])

Check the payout status:

gif.payout.getById(1) gif.payout.list()

As soon as we use fiat payments here, the external payout should be confirmed. Let’s do it.

gif.contract.send("EStoreInsurance", "confirmPayout", [ 1, 100 ])

And check the final payout status:

gif.payout.getById(1) gif.payout.list()

Congratulations!) You have just built your first insurance product!

In the next post, we will dive into the high-level architecture of GIF smart contracts from a bird’s eye view. You will see how these just-defined functions play together, how we handle payments for premiums and payouts, and how we handle data protection for the customer data.

Stay tuned!