Analyzing Smart Contract Public Data: AXA fizzy


Introduction

What Is fizzy

fizzy is an insurance service covering for flight delays. Settlements are made on the Ethereum blockchain via an open source smart contract. Payment is off chain and flight status updates are made outside of the smart contract.

Payment and reimbursement is made off chain and they didn’t use an oracle to update the flight status. But hey, that’s also a first experiment from a big insurance company. Some constraints, accounting and legal maybe, might be interfering with the code is law mantra. Keep in mind that’s also a way to start industrializing smart contract usage, short scoped projects are very good first steps IMO.

If you’re into this, there are also open source flight delay insurance smart contracts based on Ethereum:

What This Post Is About

Compared to private databases (or even private or consortium blockchains), a public blockchain like Ethereum is a way to store some immutable data publicly. This is good for customers, because they get more transparency about what happen behind the scene, this is good for service providers (like AXA in our case) because they can prove they’re not doing anything shady.

This might be a problem for service providers, thought. They might leak some of their secret sauce allowing competition to taste it and eventually copy it. This is what I’ll try to during a journey of analyzing data stored in the blockchain.

fizzy’s dev helped us, they:

  • published the source code, so we don’t have to do any reverse engineering to get information like state values.
  • made the contract emits events containing all the information we need for analysis. We could have fetched these information from the transaction input data as well, but it’s just more convenient to list them from logs.

Transparency is one reason to use a public blockchain.

Code

I published the code to fetch data from the blockchain and do the computations: https://github.com/maxme/fizzy-analysis

Analysis

Before analyzing data, here a quick introduction of how fizzy works:

  1. Visit https://fizzy.axa site,
  2. Fill your flight information, flight date, pick an option (pay more and get a better reimbursement in case of a late landing). Pay. The website will run a worker that calls the smart contract from an Ethereum account that owns it. The smart contract emits an InsuranceCreation event when this happens.
  3. …Wait for the flight to happen and be late or not…
  4. Someone (or something, we’ll get to that later in the post) calls the smart contract to update the insurance status. The contract emits an InsuranceUpdate event when this happens.

To illustrate this, here are the event definitions:

event InsuranceCreation(    // event sent when a new insurance contract is added to this smart contract
  bytes32 flightId,         // <carrier_code><flight_number>.<timestamp_in_sec_of_departure_date>
  uint32 premium,           // amount of the premium paid by the user
  uint32 indemnity,         // amount of the potential indemnity
  bytes32 productId         // ID string of the product linked to this insurance
);

event InsuranceUpdate(      // event sent when the situation of a particular insurance contract is resolved
  bytes32 productId,        // id string of the user linked to this account
  bytes32 flightId,         // <carrier_code><flight_number>.<timestamp_in_sec_of_departure_date>
  uint32 premium,           // amount of the premium paid by the user
  uint32 indemnity,         // amount of the potential indemnity
  uint8 status              // new status of the insurance contract. See above comment for potential values
);

Documentation about InsuranceUpdate’ status field:

/*
 * Status:
 * 1: flight landed before the limit
 * 2: flight landed after the limit
 * 3: insurance contract cancelled by the user
 * 4: flight cancelled
 * 5: flight redirected
 * 6: flight diverted
 */

From fizzy’s FAQ:

fizzy only covers flights as scheduled at the time of purchase of your policy. In the event of a cancellation, you are not eligible to compensation. Also, flights diverted to a different arrival airport are not covered. In both cases though, you are eligible to the reimbursement of your policy.

So, a customer gets its indemnity when the flight is late and gets its premium back when the flight is diverted, rerouted or cancelled.

Who’s Updating Flight States?

Well that’s probably a script. I don’t see any way to prove this, but looking at the time updates are done. And the fact that the volume is very low, I really doubt there is a team somewhere working 24 hours a day, 7 days a week to check a flight state and click on a button that calls a smart contract.

There are more open and transparent ways to deal with this than having a closed script (or team), by using an oracle, or using an oracle service like: Oraclize (used by Etherisc FlightDelay and InsurEth), Mobius, ChainLink.

Profitable or Not?

If we look at the big picture, sum up premiums and substract the sum of paid indemnities and reimbursed premiums, we get the gross profit on this service. Of course this doesn’t take account of the development cost, Ethereum gas, server hosting, customer support, etc.

Total premium received (revenue): 39773€
Total indemnity paid (expense): 16746€
Total premium reimbursed (expense): 23€
---
Gross profit: 23004€

Note that these numbers can’t be verified, the payment and reimbursement are made off chain, so this might be fake data. That might be a way to obfuscate real data at the cost of burning some gas.

Growing Business?

I plotted the contract activity over time to see if I could learn anything about it. Well, this was surprising. Here is the plot aggregated per day using a logarithmic scale on the vertical axis:

fizzy growth chart days

Something happened on 2018, July 26th! Other than that, it’s pretty flat and the next plot confirms this. Contract interactions, aggregated per month (linear scale):

fizzy growth chart days

If we ignore the peak in July, there is ~25 contract interactions per month. It’s not growing. I honestly didn’t expect much transaction there. Travellers (myself included) are not really looking for an extra insurance out of the flight buying process. I guess the next step for this kind of insurance is to be integrated into the buying process, make partnership with air companies or travel agencies, etc.

Last chart, the distribution of the insurance updates over time:

fizzy growth chart update days

The peak of insurance creations are covering flights over the 2 next months after July 26th (beginning of October, things seem back to normal). And it seems that a problem happened on August 9th, maybe the InsuranceUpdate bot crashed or couldn’t process transactions for some reasons. Looks like it was fixed the next day.

Mystery of July 26th

Something strange happened on 2018, July 26th, we should investigate this. Here is the distribution of the contract transactions grouped by air companies on July 26th:

{
  AF: 11133, KL: 107
}

AF is the code for AirFrance and KL the code KLM (an AirFrance subsidiary). So all insurances were subscribed for AirFrance flights on that day. This is suspicious if you compare this with insurances bought before that date:

{
  AF: 19, BA: 1, DL: 1, CX: 1, U2: 3, AZ: 10, FR: 8, KL: 3, '0B': 1, SN: 1, A3: 1, LH: 1, SE: 1, DY: 2, BV: 1, AC: 1, AA: 2, AY: 1
}

I’m honestly not sure what happened, I have a few hypothesis:

  • Internal testing, not sure why you would do this. No real payment or reimbursement, we can’t verify this as payments are made offline. Gas consumption being pretty low, it won’t be very expensive.
  • Inflate numbers (revenue) by faking contract interactions. I don’t believe this, they would spread it over weeks/months.
  • AXA and AirFrance partnered to insure some of their customers. Lot of creation in short period of time (10 hours), maybe they decided to insure ~10000 flights to give AirFrance a better idea of how things worked.

Premiums and Indemnities

From fizzy’s FAQ:

Prices are determined by fizzy’s proprietary algorithm that leverages the skills of the parametric insurance of AXA. Your pricing is tailored to the risk of your plane being late!

People can pick different options when they buy their insurance (cheap premium -> low indemnity, or higher premium -> higher indemnity), we could dig a bit deeper and try to reverse engineer the function computePremium(flighId, date, indemnity, option=[1,2,3]) but this is probably easier and more reliable to do so by using past flight data (as they probably do) especially cause we only have very few data points.

Code to compute the ratio premium/indemnity distribution:

const premiumPercentageDistribution = insuranceCreations
  .map(log => Math.round((100 * log.premium) / log.indemnity))
  .reduce((acc, perc) => {
    acc[perc] ? (acc[perc] += 1) : (acc[perc] = 1);
    return acc;
  }, {});

From that, we get:

{
  "1%": 4,
  "2%": 1207,
  "3%": 5608,
  "4%": 3216,
  "5%": 952,
  "6%": 354,
  "7%": 47,
  "8%": 15,
  "9%": 15,
  "10%": 18,
  "11%": 1,
  "12%": 6,
  "16%": 1
}

Meaning: 1207 contracts have a premium cost of ~2% of the indemnity, 5608 contracts have a premium cost of ~3% of the indemnity, etc. So most premiums are valued between 2% to 6% of the indemnity.

Other insights

The contract owner (creator) address 0x50e00de2c5cc4e456cf234fcb1a0efa367ed016e has been used for quite some time (September 2015) and was used for mining (even solo mining). Kraken was used to transfer in and out (and probably sell/buy).

Conclusion

I could get some information from the analysis, nothing ground breaking but July 26th was a nice surprise. Real time monitoring of your competitor business seems like something you want to look at, but today with this volume this is clearly not interesting for anyone.

I’m actually pleased that it was quite easy to fetch logs, decode them and compute some relevant data. Even if you don’t run a full mainnet node, it’s doable thanks to infura. Reviewing smart contracts code for bugs is interesting, but doing this kind of analysis is also pretty fun.

I realized that I was using the same set of scripts in different projects, so I decided to start generalizing them and publish them in my eth-toolbox.