# Weather Insurance

> A weather insurance dApp example.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

Source: https://dev.flare.network/fdc/guides/hardhat/weather-insurance

In this guide, we will examine an example of a simple insurance dApp that uses the FDC's [Web2Json](/fdc/guides/hardhat/web2-json) attestation type. The dApp will allow users to create insurance policies for temperatures at specific coordinates falling below a specified threshold. Other users will be able to claim those policies, and the policies will be settled automatically by the contract.

All the code described in this guide is available on GitHub, in the [Flare Hardhat starter](https://github.com/flare-foundation/flare-hardhat-starter) repository.

## The Process of Using the dApp[​](#the-process-of-using-the-dapp "Direct link to The Process of Using the dApp")

To start with, let us describe the requirements for our Weather Insurance dApp. We will do so by laying out how we expect the users to interact with the main contract. There will be three actors involved in the process:

-   **policyholder**: the entity possessing assets that they want to insure against unfavorable weather conditions
-   **insurer**: the entity willing to take on the risk in exchange for a premium
-   **contract**: the smart contract that will handle the logic of the exchange

With that, we can describe the process of using the Weather Insurance dApp.

1.  The policyholder creates a policy, specifying:

-   Location of insured asset (latitude, longitude)
-   Policy duration (start and expiration timestamp)
-   Hourly rainfall threshold constituting the loss
-   Premium amount
-   Loss coverage amount They also deposit the premium to the contract that will resolve the policy.

2.  The insurer accepts the policy and deposits the loss coverage amount to the contract. The contract pays out the deposited premium to the insurer. If no insurer has accepted the policy by the time it comes into effect (before its start timestamp), the policy is considered expired.
    
3.  The policy is resolved. This happens in three ways:
    

-   The policy has expired because no insurer accepted it in the allotted time. The contract returns the premium to the policyholder.
-   An insurer has accepted the policy, and proof has been provided to the contract, demonstrating that a loss has occurred (in this case, that the temperature at the specified location fell below the agreed-upon threshold). The contract pays out the deposited loss coverage amount to the policyholder.
-   An insurer has accepted the policy, and the expiration timestamp has been reached without valid proof having been provided. The contract returns the loss coverage deposit to the insurer.

With that we can now focus on the technical aspects of the procedure described above, starting with the main smart contract.

## The Contract[​](#the-contract "Direct link to The Contract")

The contract that we will define will be called `MinTempAgency`. This name coincides with the field name in the response on a call to the [OpenWeather](https://openweathermap.org/api) API, which we will be using to acquire weather data. We will use the [Flare Data Connector](/fdc/overview) to collect the weather data so, in keeping with the [Web2Json guide](/fdc/guides/hardhat/web2-json), we start by defining a `DataTransportObject` which will determine how the FDC should encode the data.

contracts/weatherInsurance/MinTempAgency.sol

```
struct DataTransportObject {    int256 latitude;    int256 longitude;    string description;    int256 temperature;    int256 minTemp;    uint256 windSpeed;    uint256 windDeg;}
```

In its response, the API returns the latitude and longitude of the closest weather station to the desired coordinates. The policyholder is thus responsible for providing these, otherwise, they will not be able to prove that a loss has occurred. We store the weather station's latitude and longitude in the policy so that we can later confirm that the proof pertains to the correct location.

The policy also includes a `minTemp` field, which will serve as the criterion for a loss. We save additional values to the policy, which may serve as an inspiration for defining additional policy types.

At the top of the contract, we define a `Policy` struct, which will represent a policy once it has been registered. In addition to the values originating from the `DataTransportObject`, we declare the following fields:

-   **holder**: the address of the policyholder that created the policy
-   **premium**: the deposited premium for the policy
-   **coverage**: the expected loss coverage amount if the policy is accepted
-   **status**: a `PolicyStatus` enum value describing the state of the policy, with either of the following values:
-   **Unclaimed**: the policyholder has created the policy, but no insurer has accepted it yet
-   **Open**: an insurer has accepted the policy, but it has not been resolved yet
-   **Settled**: the policy has been resolved
-   **id**: a unique value identifying the policy

The contract will save the policies to an array `registeredPolicies`. When a policy is accepted, the contract will save the insurer's address to the mapping `insurers`.

We also define several events that will be emitted at different stages of a policy's lifetime.

contracts/weatherInsurance/MinTempAgency.sol

```
contract MinTempAgency {    Policy[] public registeredPolicies;    mapping(uint256 => address) insurers;    enum PolicyStatus {        Unclaimed,        Open,        Settled    }    struct Policy {        address holder;        int256 latitude;        int256 longitude;        uint256 startTimestamp;        uint256 expirationTimestamp;        int256 minTempThreshold;        uint256 premium;        uint256 coverage;        PolicyStatus status;        uint256 id;    }    event PolicyCreated(uint256 id);    event PolicyClaimed(uint256 id);    event PolicySettled(uint256 id);    event PolicyExpired(uint256 id);    event PolicyRetired(uint256 id);    ...}
```

The function for policy creation requires a premium to be paid to the contract. The premium is thus not one of the parameters of this function. If no premium has been deposited, the function reverts. The function also ensures that the expiration timestamp is greater than the start timestamp.

If these two checks are passed, a new `Policy` struct is created and added to the array of registered policies. A `PolicyCreated` event is emitted, with the policy ID as value.

contracts/weatherInsurance/MinTempAgency.sol

```
    function createPolicy(        int256 latitude,        int256 longitude,        uint256 startTimestamp,        uint256 expirationTimestamp,        int256 minTempThreshold,        uint256 coverage    ) public payable {        require(msg.value > 0, "No premium paid");        require(startTimestamp < expirationTimestamp, "Value of startTimestamp larger than expirationTimestamp");        Policy memory newPolicy = Policy({            holder: msg.sender,            latitude: latitude,            longitude: longitude,            startTimestamp: startTimestamp,            expirationTimestamp: expirationTimestamp,            minTempThreshold: minTempThreshold,            premium: msg.value,            coverage: coverage,            status: PolicyStatus.Unclaimed,            id: registeredPolicies.length        });        registeredPolicies.push(newPolicy);        emit PolicyCreated(newPolicy.id);    }
```

info

Because Solidity does not support floating point numbers, we will store the fractional values as their `10^6` multiple. So, instead of Celsius, we will use micro-Celsius, instead of degrees for latitude and longitude micro-degrees, and so on.

The `claimPolicy` function first retrieves the policy with the given ID. It checks that the policy is still `Unclaimed`. If the policy's start timestamp has already passed, the function silently routes through `retireUnclaimedPolicy` (without reverting), so an insurer can never accidentally claim a stale policy.

Just like the premium, the coverage value is also not a parameter but the amount paid to the contract. The function checks that it has received a sufficient amount before continuing.

If all checks have passed, the policy's status is set to `Open`. The `registeredPolicies` array is updated, and the insurer added to the mapping `insurers`. Lastly, the premium is paid to the insurer, and a `PolicyClaimed` event is emitted.

contracts/weatherInsurance/MinTempAgency.sol

```
    function claimPolicy(uint256 id) public payable {        Policy memory policy = registeredPolicies[id];        require(policy.status == PolicyStatus.Unclaimed, "Policy already claimed");        if (block.timestamp > policy.startTimestamp) {            retireUnclaimedPolicy(id);        }        require(msg.value >= policy.coverage, "Insufficient coverage paid");        policy.status = PolicyStatus.Open;        registeredPolicies[id] = policy;        insurers[id] = msg.sender;        payable(msg.sender).transfer(policy.premium);        emit PolicyClaimed(id);    }
```

danger

Any coin transfer must be performed only after the state has been updated. Otherwise, the contract is open for a reentrancy attack.

The code that resolves a policy has been extended to provide a better description of conditions that revert the function. The function first collects the policy with the provided ID, and checks that its status is `Open`. Then, it validates the provided proof with the `isWeb2JsonProofValid` helper function. If the proof is valid, the `resolvePolicy` function decodes the enclosed data to at `DataTransportObject` struct.

Several checks follow. The first two ensure that we are currently within the time interval, described by the policy. We assume, that the data relates to the current weather conditions. For that reason, the function compares the timestamp of the current block to the policy's start and expiration timestamp.

If the current timestamp is smaller than the start timestamp, the function reverts. If it exceeds the expiration timestamp, we expire the policy.

Next, the function compares the coordinates provided by the proof to those of the policy, requiring they match. Lastly, it checks that the condition for a loss has been met; namely, that the minimum temperature in the proof falls below the threshold value set by the policy.

Finally, if all checks have passed, the function marks the policy as `Settled`, and transfers the coverage amount to the policyholder. A `PolicySettled` event is emitted.

contracts/weatherInsurance/MinTempAgency.sol

```
    function resolvePolicy(uint256 id, IWeb2Json.Proof calldata proof) public {        Policy memory policy = registeredPolicies[id];        require(policy.status == PolicyStatus.Open, "Policy not open");        require(isWeb2JsonProofValid(proof), "Invalid proof");        DataTransportObject memory dto = abi.decode(proof.data.responseBody.abiEncodedData, (DataTransportObject));        require(            block.timestamp >= policy.startTimestamp,            string.concat(                "Policy not yet in effect: ",                Strings.toString(block.timestamp),                " vs. ",                Strings.toString(policy.startTimestamp)            )        );        if (block.timestamp > policy.expirationTimestamp) {            expirePolicy(id);            return;        }        require(            dto.latitude == policy.latitude && dto.longitude == policy.longitude,            string.concat(                "Invalid coordinates: ",                Strings.toStringSigned(dto.latitude),                ", ",                Strings.toStringSigned(dto.longitude),                " vs. ",                Strings.toStringSigned(policy.latitude),                ", ",                Strings.toStringSigned(policy.longitude)            )        );        require(            dto.minTemp <= policy.minTempThreshold,            string.concat(                "Minimum temperature not met: ",                Strings.toStringSigned(dto.minTemp),                " vs. ",                Strings.toStringSigned(policy.minTempThreshold)            )        );        policy.status = PolicyStatus.Settled;        registeredPolicies[id] = policy;        payable(policy.holder).transfer(policy.coverage);        emit PolicySettled(id);    }
```

The `IWeb2Json.Proof` is validated as demonstrated in the [Web2Json guide](/fdc/guides/hardhat/web2-json).

contracts/weatherInsurance/MinTempAgency.sol

```
    function isWeb2JsonProofValid(IWeb2Json.Proof calldata _proof) private view returns (bool) {        return ContractRegistry.getFdcVerification().verifyWeb2Json(_proof);    }
```

Despite being called from within the function that resolves a policy, the `expirePolicy` function can serve as a standalone function. For that reason, it performs the two checks for the policy's expiration again; it ensures its status is `Open`, and that the timestamp of the current block is greater than the expiration timestamp of the policy.

If the checks pass, the policy is marked as `Settled`, and the coverage is returned to the insurer. A `PolicyExpired` event is emitted.

contracts/weatherInsurance/MinTempAgency.sol

```
    function expirePolicy(uint256 id) public {        Policy memory policy = registeredPolicies[id];        require(policy.status == PolicyStatus.Open, "Policy not open");        require(block.timestamp > policy.expirationTimestamp, "Policy not yet expired");        policy.status = PolicyStatus.Settled;        registeredPolicies[id] = policy;        payable(insurers[id]).transfer(policy.coverage);        emit PolicyExpired(id);    }
```

The last of the non-helper functions allows us to retire unclaimed policies. If the policy's status is `Unclaimed`, and the timestamp of the current block exceeds the policy's start timestamp, the policy is marked as `Settled`, and its premium is returned to the policyholder. A `PolicyRetired` event is emitted.

contracts/weatherInsurance/MinTempAgency.sol

```
    function retireUnclaimedPolicy(uint256 id) public {        Policy memory policy = registeredPolicies[id];        require(policy.status == PolicyStatus.Unclaimed, "Policy not unclaimed");        require(block.timestamp > policy.startTimestamp, "Policy not yet expired");        policy.status = PolicyStatus.Settled;        registeredPolicies[id] = policy;        payable(policy.holder).transfer(policy.premium);        emit PolicyRetired(id);    }
```

The remaining functions serve a utility purpose. The `getInsurer` function allows us to query policy insurers, while the `getAllPolicies` returns the whole `registeredPolicies` array.

contracts/weatherInsurance/MinTempAgency.sol

```
    function getInsurer(uint256 id) public view returns (address) {        return insurers[id];    }    function getAllPolicies() public view returns (Policy[] memory) {        return registeredPolicies;    }
```

Like in the [Web2Json guide](/fdc/guides/hardhat/web2-json), we define an `abiSignatureHack` function, that will allow us to extract the ABI signature of the `DataTransportObject` from the Hardhat-generated artifacts. This will be important in the next section of this guide, where we will submit a Web2Json attestation request to the FDC.

contracts/weatherInsurance/MinTempAgency.sol

```
    function abiSignatureHack(DataTransportObject memory dto) public pure {}
```

## The Scripts[​](#the-scripts "Direct link to The Scripts")

The following scripts reflect the process described at the start of this guide. Most of them are straightforward, performing a single function call.

The first script deploys the `MinTempAgency` contract and prints its address to the console.

scripts/weatherInsurance/minTemp/deployAgency.ts

```
import hre, { run } from "hardhat";import type { MinTempAgencyInstance } from "../../../typechain-types";const MinTempAgency = artifacts.require("MinTempAgency");// yarn hardhat run scripts/weatherInsurance/minTemp/deployAgency.ts --network coston2async function deployAndVerify() {  const args: readonly unknown[] = [];  const agency: MinTempAgencyInstance = await MinTempAgency.new(...args);  try {    await run("verify:verify", {      address: agency.address,      constructorArguments: args,    });  } catch (e: unknown) {    if (e instanceof Error) {      console.error("Verification error:", e.message);    } else {      console.error("Unknown verification error:", e);    }  }  console.log(    `(${hre.network.name}) MinTempAgency deployed to`,    agency.address,    "\n",  );}deployAndVerify().then(() => {  process.exit(0);});
```

We save the address to a `scripts/weatherInsurance/minTemp/config.ts` file because we will access it in other scripts.

scripts/weatherInsurance/minTemp/config.ts

```
export const agencyAddress = "0x5Ac39E3DC1Bb32c21A0ab3eF45395207d45c8EB6";
```

Next, we will create a new policy with the following parameters:

-   latitude: `46.419402127862405`
-   longitude: `15.587079308221126`
-   start timestamp: 30 seconds from now
-   expiration timestamp: an hour from now
-   minimum temperature threshold: 20 degrees Celsius (encoded as `20 * 10**6` micro-Celsius)
-   premium: `0.01 ether`
-   coverage: `0.1 ether`

note

We have set the minimum temperature threshold high enough that the policy will always be resolved successfully.

Because the response of the [OpenWeather](https://openweathermap.org/api) API includes not the provided coordinates, but those of the nearest weather station, we will first replace our latitude and longitude with those. This will ensure that the coordinates of the created policy match the ones in the proof data. Without this step, we could never prove that a loss has occurred.

We find the correct coordinates by making a GET request to the OpenWeather API. To use the API, we need to provide it with an API key, which is available on a free account. We read the API key from the `.env` file. Also, we explicitly state that we are using metric units, although this is the default.

The Web2Json verifier accepts the base URL and the query parameters as separate fields: `apiUrl` carries the bare endpoint and `queryParams` carries the per-request parameters as a JSON-encoded object. The starter encodes them like so:

```
const apiUrl = "https://api.openweathermap.org/data/2.5/weather";const apiId = process.env.OPEN_WEATHER_API_KEY ?? "";const units = "metric";function prepareQueryParams(policy: any) {  const queryParams = {    lat: policy.latitude / 10 ** 6,    lon: policy.longitude / 10 ** 6,    units: units,    appid: apiId,  };  return JSON.stringify(queryParams);}
```

The script collects the appropriate coordinates as described above. It multiplies them with `10^6` to avoid the floating point values. Lastly, it creates a new policy by calling the `createPolicy` function on the `MinTempAgency` at the address specified in the `config.ts` file.

scripts/weatherInsurance/minTemp/createPolicy.ts

```
import { agencyAddress } from "./config";import type { MinTempAgencyInstance } from "../../../typechain-types";const MinTempAgency = artifacts.require("MinTempAgency");// yarn hardhat run scripts/weatherInsurance/minTemp/createPolicy.ts --network coston2const latitude = 46.419402127862405;const longitude = 15.587079308221126;const apiId = process.env.OPEN_WEATHER_API_KEY ?? "";const units = "metric";const apiUrl = `https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${longitude}&appid=${apiId}&units=${units}`;const startTimestamp = Math.round(Date.now() / 1000) + 30;const expirationTimestamp = startTimestamp + 60 * 60;const minTempThreshold = 20 * 10 ** 6;const premium = 10;const coverage = 1000;async function getWeatherStationCoordinates(apiUrl: string) {  const response = await fetch(apiUrl, { method: "GET" });  const json = await response.json();  console.log("Response:", json, "\n");  return json.coord;}interface PolicyParameters {  latitude: number;  longitude: number;  startTimestamp: number;  expirationTimestamp: number;  minTempThreshold: number;  premium: number;  coverage: number;}async function createPolicy(  agency: MinTempAgencyInstance,  policyParameters: PolicyParameters,) {  const transaction = await agency.createPolicy(    policyParameters.latitude,    policyParameters.longitude,    policyParameters.startTimestamp,    policyParameters.expirationTimestamp,    policyParameters.minTempThreshold,    policyParameters.coverage,    { value: policyParameters.premium },  );  console.log("Transaction:", transaction.tx, "\n");}async function main() {  const coordinates = await getWeatherStationCoordinates(apiUrl);  console.log("Coordinates:", coordinates, "\n");  const policyParameters: PolicyParameters = {    latitude: coordinates.lat * 10 ** 6,    longitude: coordinates.lon * 10 ** 6,    startTimestamp,    expirationTimestamp,    minTempThreshold,    coverage,    premium,  };  console.log("Policy parameters:", policyParameters, "\n");  const agency: MinTempAgencyInstance = await MinTempAgency.at(agencyAddress);  console.log("MinTempAgency:", agency.address, "\n");  await createPolicy(agency, policyParameters);}main().then(() => {  process.exit(0);});
```

The script for claiming a policy has a helper function that calculates the policy coverage of a given policy. It then calls the `claimPolicy` function on the `MinTempAgency` at the address read from the `config.ts` file, paying the coverage value to the contract. In this example, the policy claimed has the ID of `0`.

scripts/weatherInsurance/minTemp/claimPolicy.ts

```
import { agencyAddress } from "./config";import type { MinTempAgencyInstance } from "../../../typechain-types";const MinTempAgency = artifacts.require("MinTempAgency");// yarn hardhat run scripts/weatherInsurance/minTemp/claimPolicy.ts --network coston2const policyId = 0;async function getPolicyCoverage(  agency: MinTempAgencyInstance,  policyId: number,) {  const policy = await agency.registeredPolicies(policyId);  const policyCoverage = policy.coverage;  console.log("Policy premium:", policyCoverage, "\n");  return policyCoverage;}async function main() {  const agency: MinTempAgencyInstance = await MinTempAgency.at(agencyAddress);  console.log("MinTempAgency:", agency.address, "\n");  const policyCoverage = await getPolicyCoverage(agency, policyId);  const transaction = await agency.claimPolicy(policyId, {    value: policyCoverage,  });  console.log("Transaction:", transaction.tx, "\n");}main().then(() => {  process.exit(0);});
```

The script for resolving a policy is slightly more complicated. It involves making a Web2Json attestation request to the FDC and providing the returned proof to the `MinTempAgency` contract. To learn more about the Web2Json attestation request look at the [related guide](/fdc/guides/hardhat/web2-json), or its [spec](/fdc/attestation-types/web2-json).

We prepare an attestation request by POSTing to the Web2Json verifier endpoint at `${VERIFIER_URL_TESTNET}/verifier/web2/Web2Json/prepareRequest`. The request body packages the `apiUrl`, `queryParams`, `postProcessJq` and `abiSignature` as separate fields so the verifier can compose the GET request itself.

We need to specify the jq filter that the FDC should apply to the data received in the response to the POST request to the above URL. Only a few fields are needed, and most must first be multiplied by `10^6` so that we can store them as `uint256` values in Solidity. We also employ some IF-statements, to avoid the error of multiplying `null` values. The filter we will be using is:

scripts/weatherInsurance/minTemp/resolvePolicy.ts

```
const postProcessJq = `{  latitude: (.coord.lat | if . !== null then .*pow(10;6) else 0 end | floor),  longitude: (.coord.lon | if . !== null then .*pow(10;6) else 0 end | floor),  description: .weather[0].description,  temperature: (.main.temp | if . !== null then .*pow(10;6) else 0 end | floor),  minTemp: (.main.temp_min | if . !== null then .*pow(10;6) else 0 end | floor),  windSpeed: (.wind.speed | if . !== null then . *pow(10;6) else 0 end | floor),  windDeg: .wind.deg  }`;
```

The last parameter missing is the ABI encoding for the FDC to store the processed data as. We extract it from the artifacts for the `MinTempAgency` generated by Hardhat, and more specifically from the parameters of the `abiSignatureHack` function.

scripts/weatherInsurance/minTemp/resolvePolicy.ts

```
const abiSignature = `{         \"components\": [           {             \"internalType\": \"int256\",             \"name\": \"latitude\",             \"type\": \"int256\"           },           {             \"internalType\": \"int256\",             \"name\": \"longitude\",             \"type\": \"int256\"           },           {             \"internalType\": \"string\",             \"name\": \"description\",             \"type\": \"string\"           },           {             \"internalType\": \"int256\",             \"name\": \"temperature\",             \"type\": \"int256\"           },           {             \"internalType\": \"int256\",             \"name\": \"minTemp\",             \"type\": \"int256\"           },           {             \"internalType\": \"uint256\",             \"name\": \"windSpeed\",             \"type\": \"uint256\"           },           {             \"internalType\": \"uint256\",             \"name\": \"windDeg\",             \"type\": \"uint256\"           }         ],         \"internalType\": \"struct DataTransportObject\",         \"name\": \"dto\",         \"type\": \"tuple\"       }`;
```

The intermediate steps more or less follow the ones in the [Web2Json guide](/fdc/guides/hardhat/web2-json). We make a POST request to the verifier server and thus prepare the attestation request. We submit the ABI-encoded request to the FDC, wait for the current voting round to finalize, and then collect the data from a DA Layer server. Finally, we decode the data to an `IWeb2Json.Response` struct and add it to an `IWeb2Json.Proof` struct, which we input into the `resolvePolicy` function, along with the policy ID, of the `MinTempAgency` at the address from the `config.ts` file.

The entire script looks as follows.

scripts/weatherInsurance/minTemp/resolvePolicy.ts

```
import { agencyAddress } from "./config";import type { MinTempAgencyInstance } from "../../../typechain-types";import {  prepareAttestationRequestBase,  submitAttestationRequest,  retrieveDataAndProofBase,  sleep,} from "../../fdcExample/Base";const { VERIFIER_URL_TESTNET, VERIFIER_API_KEY_TESTNET, COSTON2_DA_LAYER_URL } =  process.env;const MinTempAgency = artifacts.require("MinTempAgency");// yarn hardhat run scripts/weatherInsurance/minTemp/resolvePolicy.ts --network coston2const policyId = 0;// Request dataconst apiId = process.env.OPEN_WEATHER_API_KEY ?? "";const units = "metric";const apiUrl = "https://api.openweathermap.org/data/2.5/weather";const httpMethod = "GET";const headers = JSON.stringify({ "Content-Type": "application/json" });const body = "{}";const postProcessJq = `{  latitude: (.coord.lat | if . != null then .*pow(10;6) else 0 end | floor),  longitude: (.coord.lon | if . != null then .*pow(10;6) else 0 end | floor),  description: .weather[0].description,  temperature: (.main.temp | if . != null then .*pow(10;6) else 0 end | floor),  minTemp: (.main.temp_min | if . != null then .*pow(10;6) else 0 end | floor),  windSpeed: (.wind.speed | if . != null then . *pow(10;6) else 0 end | floor),  windDeg: .wind.deg  }`;const abiSignature = `{          "components": [            {              "internalType": "int256",              "name": "latitude",              "type": "int256"            },            {              "internalType": "int256",              "name": "longitude",              "type": "int256"            },            {              "internalType": "string",              "name": "description",              "type": "string"            },            {              "internalType": "int256",              "name": "temperature",              "type": "int256"            },            {              "internalType": "int256",              "name": "minTemp",              "type": "int256"            },            {              "internalType": "uint256",              "name": "windSpeed",              "type": "uint256"            },            {              "internalType": "uint256",              "name": "windDeg",              "type": "uint256"            }          ],          "internalType": "struct DataTransportObject",          "name": "dto",          "type": "tuple"        }`;// Configuration constantsconst attestationTypeBase = "Web2Json";const sourceIdBase = "PublicWeb2";const verifierUrlBase = VERIFIER_URL_TESTNET;async function getPolicy(agency: MinTempAgencyInstance, id: number) {  const response = await agency.registeredPolicies(id);  const policy = {    latitude: response.latitude,    longitude: response.longitude,    startTimestamp: response.startTimestamp,    expirationTimestamp: response.expirationTimestamp,    minTempThreshold: response.minTempThreshold,    premium: response.premium,    coverage: response.coverage,    status: response.status,    id: response.id,  };  console.log("Policy:", policy, "\n");  return policy;}function prepareQueryParams(policy: { latitude: number; longitude: number }) {  const queryParams = {    lat: policy.latitude / 10 ** 6,    lon: policy.longitude / 10 ** 6,    units: units,    appid: apiId,  };  return JSON.stringify(queryParams);}async function prepareAttestationRequest(  apiUrl: string,  httpMethod: string,  headers: string,  queryParams: string,  body: string,  postProcessJq: string,  abiSignature: string,) {  const requestBody = {    url: apiUrl,    httpMethod: httpMethod,    headers: headers,    queryParams: queryParams,    body: body,    postProcessJq: postProcessJq,    abiSignature: abiSignature,  };  console.log(    `Query string: ${apiUrl}?${queryParams.replaceAll(":", "=").replaceAll(",", "&").replaceAll("{", "").replaceAll("}", "").replaceAll('"', "")}\n`,  );  const url = `${verifierUrlBase}/verifier/web2/Web2Json/prepareRequest`;  const apiKey = VERIFIER_API_KEY_TESTNET;  return await prepareAttestationRequestBase(    url,    apiKey,    attestationTypeBase,    sourceIdBase,    requestBody,  );}async function retrieveDataAndProof(  abiEncodedRequest: string,  roundId: number,) {  const url = `${COSTON2_DA_LAYER_URL}/api/v1/fdc/proof-by-request-round-raw`;  console.log("Url:", url, "\n");  return await retrieveDataAndProofBase(url, abiEncodedRequest, roundId);}async function resolvePolicy(  agency: MinTempAgencyInstance,  id: number,  proof: {    proof: string;    response_hex: string;  },) {  console.log("Proof hex:", proof.response_hex, "\n");  // A piece of black magic that allows us to read the response type from an artifact  const IWeb2JsonVerification = await artifacts.require(    "IWeb2JsonVerification",  );  const responseType =    IWeb2JsonVerification._json.abi[0].inputs[0].components[1];  console.log("Response type:", responseType, "\n");  const decodedResponse = web3.eth.abi.decodeParameter(    responseType,    proof.response_hex,  );  console.log("Decoded proof:", decodedResponse, "\n");  for (let attempt = 1; attempt <= 5; attempt++) {    try {      const transaction = await agency.resolvePolicy(id, {        merkleProof: proof.proof,        data: decodedResponse,      });      console.log("Transaction:", transaction.tx, "\n");      break;    } catch (error) {      console.log("Error:", error, "\n");      await sleep(20000);    }  }}async function main() {  const agency: MinTempAgencyInstance = await MinTempAgency.at(agencyAddress);  console.log("MinTempAgency:", agency.address, "\n");  const policy = await getPolicy(agency, policyId);  const queryParams = prepareQueryParams(policy);  const data = await prepareAttestationRequest(    apiUrl,    httpMethod,    headers,    queryParams,    body,    postProcessJq,    abiSignature,  );  console.log("Data:", data, "\n");  const abiEncodedRequest = data.abiEncodedRequest;  const roundId = await submitAttestationRequest(abiEncodedRequest);  const proof = await retrieveDataAndProof(abiEncodedRequest, roundId);  await resolvePolicy(agency, policyId, proof);}void main().then(() => {  process.exit(0);});
```

The two remaining scripts handle the remaining two cases for a policy to reach the end of its lifetime. The first settles an `Open` policy that has reached its expiration timestamp. It does so by calling the `expirePolicy` function of the `MinTempAgency` at the address from the `config.ts` file.

scripts/weatherInsurance/minTemp/expirePolicy.ts

```
import { agencyAddress } from "./config";import type { MinTempAgencyInstance } from "../../../typechain-types";const MinTempAgency = artifacts.require("MinTempAgency");// yarn hardhat run scripts/weatherInsurance/minTemp/expirePolicy.ts --network coston2const policyId = 0;async function main() {  const agency: MinTempAgencyInstance = await MinTempAgency.at(agencyAddress);  console.log("MinTempAgency:", agency.address, "\n");  const transaction = await agency.expirePolicy(policyId);  console.log("Transaction:", transaction.tx, "\n");}main().then(() => {  process.exit(0);});
```

The second function settles an `Unclaimed` policy that has reached it expiration timestamp, through the `retireUnclaimedPolicy` of the `MinTempAgency` contract.

scripts/weatherInsurance/minTemp/retireUnclaimedPolicy.ts

```
import { agencyAddress } from "./config";import type { MinTempAgencyInstance } from "../../../typechain-types";const MinTempAgency = artifacts.require("MinTempAgency");// yarn hardhat run scripts/weatherInsurance/minTemp/retireUnclaimedPolicy.ts --network coston2const policyId = 1;async function main() {  const agency: MinTempAgencyInstance = await MinTempAgency.at(agencyAddress);  console.log("MinTempAgency:", agency.address, "\n");  const transaction = await agency.retireUnclaimedPolicy(policyId);  console.log("Transaction:", transaction.tx, "\n");}main().then(() => {  process.exit(0);});
```

## Modifications and enhancements[​](#modifications-and-enhancements "Direct link to Modifications and enhancements")

In the last section of this guide, we will describe several options for improving the described example. We can diversify the offered policy types, which would require only a small adjustment to the existing code. But an even further improvement and a paradigm shift would be to issue tokens to insurers and policyholders.

### Additional data[​](#additional-data "Direct link to Additional data")

An example of how the policy types can be extended is provided in the [Flare Hardhat starter](https://github.com/flare-foundation/flare-hardhat-starter) repository. The `WeatherIdAgency` checks that the ID of the current weather, as described by the [OpenWeather specification](https://openweathermap.org/weather-conditions), matches or exceeds the weather code threshold within the policy.

Other simple modification options are:

-   maximum temperature
-   atmospheric pressure
-   humidity
-   rainfall
-   snowfall
-   visibility
-   wind speed

### Issuing tokens[​](#issuing-tokens "Direct link to Issuing tokens")

Instead of running the insurance agency as a registry, we could issue tokens to represent the policies. One option would be to create an NFT when a policy is created, representing the policyholder's position, and a second NFT for the insurer's position when a policy is claimed. This would prevent either party's funds from being locked within a contract for the duration of the policy.

Another option would be to issue ERC1155 tokens expressing the stake. It would enable the trade of fractions of policies while optimizing gas consumption.

No doubt there is an even better token type for such use case. But that goes beyond the purpose of this guide.
