# Web2Json

> Retrieve arbitrary Web2 data.

> 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/web2-json

The `Web2Json` attestation type enables data collection from an arbitrary Web2 source, thought the source has to be whitelisted by the Flare Network in advance. You can learn more about it in the official [specification](/fdc/attestation-types/web2-json).

We will now demonstrate how the FDC protocol can be used to collect the data of a given [Star Wars API](https://swapi.dev/) request. The request we will be making is `https://swapi.info/api/people/3`. The same procedure works for all public APIs.

In this guide, we will follow the steps outlined in the [FDC overview](/fdc/overview). We will define a `scripts/fdcExample/Web2Json.ts` file that will encapsulate the whole process. The shared helpers used below live in the starter under `scripts/utils/fdc.ts` and `scripts/utils/getters.ts`.

scripts/fdcExample/Web2Json.ts

```
import { run, web3 } from "hardhat";import { StarWarsCharacterListV2Instance } from "../../typechain-types";import {  prepareAttestationRequestBase,  submitAttestationRequest,  retrieveDataAndProofBase,} from "../utils/fdc";const StarWarsCharacterListV2 = artifacts.require("StarWarsCharacterListV2");const { VERIFIER_URL_TESTNET, VERIFIER_API_KEY_TESTNET, COSTON2_DA_LAYER_URL } = process.env;...async function main() {  const data = await prepareAttestationRequest(    apiUrl,    postprocessJq,    abiSignature  );  console.log("Data:", data, "\n");  const abiEncodedRequest = data.abiEncodedRequest;  const roundId = await submitAttestationRequest(abiEncodedRequest);  const proof = await retrieveDataAndProof(abiEncodedRequest, roundId);  const characterList: StarWarsCharacterListV2Instance =    await deployAndVerifyContract();  await interactWithContract(characterList, proof);}void main().then((data) => {  process.exit(0);});
```

The function names mostly mirror the steps described in the [FDC guide](/fdc/overview).

## Prepare request[​](#prepare-request "Direct link to Prepare request")

In this guide we will demonstrate how to prepare an attestation request through a verifier server. At the end of the section we will provide a breakdown of the abi encoded request; thus we will demonstrate how it can be constructed manually.

To use the verifier server, we send a request to its `prepareRequest` endpoint. A JSON request to the verifier follows the same structure for all attestation types, with field values varying between types.

### Required Fields[​](#required-fields "Direct link to Required Fields")

-   `attestationType` is the UTF8 hex string encoding of the attestation type name, zero-padded to 32 bytes.
-   `sourceId` is the UTF8 hex string encoding of the data source identifier name, zero-padded to 32 bytes.
-   `requestBody` is different for each attestation type.

In the case of `Web2Json`, `requestBody` is a JSON containing the fields:

-   `url`: url of the data source; as `string`
-   `httpMethod`: one of `GET`, `POST`, `PUT`, `PATCH` and `DELETE`
-   `headers`: request headers as a stringified JSON; `{}` if no headers are required (defaults to `{"Content-Type": "application/json"}`)
-   `queryParams`: request query parameters as a stringified JSON; `{}` if no query parameters are required,
-   `body`: request body as a stringified JSON; `{}` if no body is required,
-   `postProcessJq`: JQ filter to postprocess the json data received from the URL; as `string`
-   `abiSignature`: ABI signature of the Solidity struct that will be used to decode the data; as `string`

### Reference Documentation[​](#reference-documentation "Direct link to Reference Documentation")

-   [Web2Json Specification](/fdc/attestation-types/web2-json)
-   [Verifier Interactive Docs](https://fdc-verifiers-testnet.flare.network/verifier/web2/api-doc#/)

### Example Values[​](#example-values "Direct link to Example Values")

-   `url`: the above address `https://swapi.info/api/people/3`
-   `httpMethod`: `GET`
-   `headers`: `{}`, which defaults to value `{"Content-Type": "application/json"}`
-   `queryParams`: `{}`
-   `body`: `{}`
-   `postProcessJq`: `{name: .name, height: .height, mass: .mass, numberOfFilms: .films | length, uid: (.url | split("/") | .[-1] | tonumber)}`
-   `abiSignature`:

```
{"components": [{"internalType": "string", "name": "name", "type": "string"},{"internalType": "uint256", "name": "height", "type": "uint256"},{"internalType": "uint256", "name": "mass", "type": "uint256"},{"internalType": "uint256", "name": "numberOfFilms", "type": "uint256"},{"internalType": "uint256", "name": "uid", "type": "uint256"}],"name": "task","type": "tuple"}
```

scripts/fdcExample/Web2Json.ts

```
// Request dataconst apiUrl = "https://swapi.info/api/people/3";const postProcessJq = `{name: .name, height: .height, mass: .mass, numberOfFilms: .films | length, uid: (.url | split("/") | .[-1] | tonumber)}`;const httpMethod = "GET";const headers = "{}";const queryParams = "{}";const body = "{}";const abiSignature = `{"components": [{"internalType": "string", "name": "name", "type": "string"},{"internalType": "uint256", "name": "height", "type": "uint256"},{"internalType": "uint256", "name": "mass", "type": "uint256"},{"internalType": "uint256", "name": "numberOfFilms", "type": "uint256"},{"internalType": "uint256", "name": "uid", "type": "uint256"}],"name": "task","type": "tuple"}`;// Configuration constantsconst attestationTypeBase = "Web2Json";const sourceIdBase = "PublicWeb2";const verifierUrlBase = VERIFIER_URL_TESTNET;
```

### Encoding Functions[​](#encoding-functions "Direct link to Encoding Functions")

To encode values into UTF8 hex:

-   `toUtf8HexString`: Converts a string to UTF8 hex.
-   `toHex`: Zero-right-pads the string to 32 bytes.

These functions are included in the [Base library](https://github.com/flare-foundation/flare-hardhat-starter/blob/master/scripts/fdcExample/Base.ts) within the [example repository](https://github.com/flare-foundation/flare-hardhat-starter/), but they can also be defined locally in your contract or script.

scripts/utils/fdc.ts

```
function toHex(data: string) {  var result = "";  for (var i = 0; i < data.length; i++) {    result += data.charCodeAt(i).toString(16);  }  return result.padEnd(64, "0");}
```

scripts/utils/fdc.ts

```
function toUtf8HexString(data: string) {  return "0x" + toHex(data);}
```

Because of the `console.log` commands it will produce JSON strings that represent valid requests; we can then pass this to the [interactive verifier](https://fdc-verifiers-testnet.flare.network/verifier/web2/api-doc/) to check the response.

The process of posting a request to a verifier server is identical for all attestation types. It differs only in values used. For that reason we define a base function that the `prepareAttestationRequest` function will call.

The `prepareAttestationRequestBase` function formulates a request for the verifier server, and posts it to the given URL.

scripts/utils/fdc.ts

```
async function prepareAttestationRequestBase(  url: string,  apiKey: string,  attestationTypeBase: string,  sourceIdBase: string,  requestBody: any,) {  console.log("Url:", url, "\n");  const attestationType = toUtf8HexString(attestationTypeBase);  const sourceId = toUtf8HexString(sourceIdBase);  const request = {    attestationType: attestationType,    sourceId: sourceId,    requestBody: requestBody,  };  console.log("Prepared request:\n", request, "\n");  const response = await fetch(url, {    method: "POST",    headers: {      "X-API-KEY": apiKey,      "Content-Type": "application/json",    },    body: JSON.stringify(request),  });  if (response.status != 200) {    throw new Error(      `Response status is not OK, status ${response.status} ${response.statusText}\n`,    );  }  console.log("Response status is OK\n");  return await response.json();}
```

In the example repository, it is once again included within the [Base](https://github.com/flare-foundation/flare-foundry-starter/blob/master/scripts/fdcExample/Base.s.sol) library file.

We construct the URL by appending to the verifier address \`\`the path`Web2Json/prepareRequest`. Thus, the function that prepares the verifier request looks like:

scripts/fdcExample/Web2Json.ts

```
async function prepareAttestationRequest(  apiUrl: string,  postProcessJq: string,  abiSignature: string,) {  const requestBody = {    url: apiUrl,    httpMethod: httpMethod,    headers: headers,    queryParams: queryParams,    body: body,    postProcessJq: postProcessJq,    abiSignature: abiSignature,  };  const url = `${verifierUrlBase}/verifier/web2/Web2Json/prepareRequest`;  const apiKey = VERIFIER_API_KEY_TESTNET;  return await prepareAttestationRequestBase(    url,    apiKey,    attestationTypeBase,    sourceIdBase,    requestBody,  );}
```

<details>
<summary>Understanding the abiEncodedRequest.</summary>

Understanding the `abiEncodedRequest`.

If everything went right, the `abiEncodedRequest` should look something like this (minus the line breaks - we split it after the `0x` symbol and then after every 64 characters (32 bytes), for the sake of clarity).

```
0x576562324a736f6e0000000000000000000000000000000000000000000000005075626c6963576562320000000000000000000000000000000000000000000064f3f301397c2dfa2f9043325e161596786109b9195520ca8623a6c6d46cf4b6000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002c0000000000000000000000000000000000000000000000000000000000000001f68747470733a2f2f73776170692e696e666f2f6170692f70656f706c652f33000000000000000000000000000000000000000000000000000000000000000003474554000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027b7d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027b7d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000027b7d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000787b6e616d653a202e6e616d652c206865696768743a202e6865696768742c206d6173733a202e6d6173732c206e756d6265724f6646696c6d733a202e66696c6d73207c206c656e6774682c207569643a20282e75726c207c2073706c697428222f2229207c202e5b2d315d207c20746f6e756d626572297d000000000000000000000000000000000000000000000000000000000000000000000000000001737b22636f6d706f6e656e7473223a205b7b22696e7465726e616c54797065223a2022737472696e67222c20226e616d65223a20226e616d65222c202274797065223a2022737472696e67227d2c7b22696e7465726e616c54797065223a202275696e74323536222c20226e616d65223a2022686569676874222c202274797065223a202275696e74323536227d2c7b22696e7465726e616c54797065223a202275696e74323536222c20226e616d65223a20226d617373222c202274797065223a202275696e74323536227d2c7b22696e7465726e616c54797065223a202275696e74323536222c20226e616d65223a20226e756d6265724f6646696c6d73222c202274797065223a202275696e74323536227d2c7b22696e7465726e616c54797065223a202275696e74323536222c20226e616d65223a2022756964222c202274797065223a202275696e74323536227d5d2c226e616d65223a20227461736b222c2274797065223a20227475706c65227d00000000000000000000000000
```

Let's break it down line by line:

-   **First line:** `toUtf8HexString("Web2Json")`
-   **Second line:** `toUtf8HexString("PublicWeb2")`
-   **Third line:** message integrity code (MIC), a hash of the whole response salted with a string `"Flare"`, ensures the integrity of the attestation
-   **Remaining lines:** ABI encoded `Web2Json.RequestBody` Solidity struct

What this demonstrates is that, with some effort, the `abiEncodedRequest` can be constructed manually.

</details>

## Submit request to FDC[​](#submit-request-to-fdc "Direct link to Submit request to FDC")

This step transitions from offchain request preparation to onchain interaction with the FDC protocol. We will submit the validated request to the blockchain using deployed official Flare smart contracts. To streamline the process of accessing these, the [Flare smart contracts periphery package](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) is shipped with the `ContractRegistry` library.

The `IFlareContractRegistry` deployed at `0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019` on every Flare-family network resolves names to live contract addresses. We use it to discover:

-   `FdcHub`: for posting the request to
-   `FdcRequestFeeConfigurations`: calculates the fee of the request
-   `FlareSystemsManager`: for calculating the round ID
-   `Relay`: confirms the round has finalized
-   `FdcVerification`: exposes the FDC protocol id and verification entry points

The starter resolves these from TypeScript via small wrappers in `scripts/utils/getters.ts` that call `IFlareContractRegistry.getContractAddressByName(...)` and return a typechain instance.

To submit the attestation request, we first access the deployed `FdcHub` contract. We determine the fee for our attestation type, and then request the attestation of the FDC, paying the required fee. Lastly, we calculate the voting round Id from the transaction that carried the attestation request; we will need it to query the data and proof.

scripts/utils/fdc.ts

```
async function submitAttestationRequest(abiEncodedRequest: string) {  const fdcHub = await getFdcHub();  const requestFee = await getFdcRequestFee(abiEncodedRequest);  const transaction = await fdcHub.requestAttestation(abiEncodedRequest, {    value: requestFee,  });  console.log("Submitted request:", transaction.tx, "\n");  const roundId = await calculateRoundId(transaction);  console.log(    `Check round progress at: https://${hre.network.name}-systems-explorer.flare.rocks/voting-round/${roundId}?tab=fdc\n`,  );  return roundId;}
```

scripts/utils/getters.ts

```
async function getFdcHub() {  const FlareContractRegistry = artifacts.require("IFlareContractRegistry");  const registry: IFlareContractRegistryInstance =    await FlareContractRegistry.at(      "0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019",    );  const fdcHubAddress: string =    await registry.getContractAddressByName("FdcHub");  return await FdcHub.at(fdcHubAddress);}
```

The request fee is obtained from the `fdcRequestFeeConfigurations` contract. We once again resolve its address through the `IFlareContractRegistry`.

scripts/utils/fdc.ts

```
async function getFdcRequestFee(abiEncodedRequest: string) {  const FlareContractRegistry = artifacts.require("IFlareContractRegistry");  const registry: IFlareContractRegistryInstance =    await FlareContractRegistry.at(      "0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019",    );  const fdcRequestFeeConfigurationsAddress: string =    await registry.getContractAddressByName("FdcRequestFeeConfigurations");  const fdcRequestFeeConfigurations: IFdcRequestFeeConfigurationsInstance =    await FdcRequestFeeConfigurations.at(fdcRequestFeeConfigurationsAddress);  return await fdcRequestFeeConfigurations.getRequestFee(abiEncodedRequest);}
```

The round ID is calculate from the timestamp of the block, containing the transaction requesting attestation. We first subtract from the block timestamp the timestamp of the first voting epoch. Then, we divide the number by the duration of the voting epoch (90 seconds). Instead of hard-coding them, we retrieve these values from another official Flare contract, the `flareSystemsManager`.

scripts/utils/fdc.ts

```
async function calculateRoundId(transaction: any) {  const blockNumber = transaction.receipt.blockNumber;  const block = await ethers.provider.getBlock(blockNumber);  const blockTimestamp = BigInt(block!.timestamp);  const flareSystemsManager: IFlareSystemsManagerInstance =    await getFlareSystemsManager();  const firsVotingRoundStartTs = BigInt(    await flareSystemsManager.firstVotingRoundStartTs(),  );  const votingEpochDurationSeconds = BigInt(    await flareSystemsManager.votingEpochDurationSeconds(),  );  console.log("Block timestamp:", blockTimestamp, "\n");  console.log("First voting round start ts:", firsVotingRoundStartTs, "\n");  console.log(    "Voting epoch duration seconds:",    votingEpochDurationSeconds,    "\n",  );  const roundId = Number(    (blockTimestamp - firsVotingRoundStartTs) / votingEpochDurationSeconds,  );  console.log("Calculated round id:", roundId, "\n");  console.log(    "Received round id:",    Number(await flareSystemsManager.getCurrentVotingEpochId()),    "\n",  );  return roundId;}
```

We obtain the `flareSystemsManager` contract through the `IFlareContractRegistry`.

scripts/utils/getters.ts

```
async function getFlareSystemsManager() {  const FlareContractRegistry = artifacts.require("IFlareContractRegistry");  const registry: IFlareContractRegistryInstance =    await FlareContractRegistry.at(      "0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019",    );  const flareSystemsManagerAddress: string =    await registry.getContractAddressByName("FlareSystemsManager");  return await FlareSystemsManager.at(flareSystemsManagerAddress);}
```

## Retrieve data and proof[​](#retrieve-data-and-proof "Direct link to Retrieve data and proof")

To retrieve the data and proof, we must first wait for the voting round in which the attestation request was submitted to finalize; this takes no more than 180 seconds, but is on average much less. After the round has been finalized, we post a request to a DA Layer provider.

We can check if the request was submitted successfully on the [AttestationRequests](https://coston2-systems-explorer.flare.rocks/attestation-request) page on the Flare Systems Explorer website. To check if the round has been finalized, go to [Finalizations](https://coston2-systems-explorer.flare.rocks/finalizations) page.

If you want to learn more about how the FDC protocol works, check [here](/fdc/overview).

Because the process includes waiting for the voting round to finalize, we prepare a `sleep` function. The function pauses the execution of the script for a given number of milliseconds.

scripts/utils/fdc.ts

```
function sleep(ms: number) {  return new Promise((resolve) => setTimeout(resolve, ms));}
```

The only difference between the `retrieveDataAndProof` functions of all six attestation types is the URL of the DA Layer server. For that reason, we will define as separate `retrieveDataAndProofBase` function that will handle most of the logic. The function waits for the round to finalize - rechecking every 10 seconds if necessary. Then, it prepares a proof request, and posts it to the DA Layer server. Because it might take a few seconds for the server to generate the proof, the function ensures that the response actually contains a sufficient response, and retries otherwise.

scripts/utils/fdc.ts

```
async function retrieveDataAndProofBase(  url: string,  abiEncodedRequest: string,  roundId: number,) {  console.log("Waiting for the round to finalize...");  // We check every 10 seconds if the round is finalized  const relay: IRelayInstance = await getRelay();  const fdcVerification: IFdcVerificationInstance = await getFdcVerification();  const protocolId = await fdcVerification.fdcProtocolId();  while (!(await relay.isFinalized(protocolId, roundId))) {    await sleep(10000);  }  console.log("Round finalized!\n");  const request = {    votingRoundId: roundId,    requestBytes: abiEncodedRequest,  };  console.log("Prepared request:\n", request, "\n");  await sleep(10000);  var proof = await postRequestToDALayer(url, request, true);  console.log("Waiting for the DA Layer to generate the proof...");  while (proof.response_hex == undefined) {    await sleep(5000);    proof = await postRequestToDALayer(url, request, false);  }  console.log("Proof generated!\n");  console.log("Proof:", proof, "\n");  return proof;}
```

We access the Flare's official `Relay` and `FdcVerification` contracts with helper functions.

scripts/utils/getters.ts

```
async function getRelay() {  const FlareContractRegistry = artifacts.require("IFlareContractRegistry");  const registry: IFlareContractRegistryInstance =    await FlareContractRegistry.at(      "0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019",    );  const relayAddress: string = await registry.getContractAddressByName("Relay");  return await IRelay.at(relayAddress);}async function getFdcVerification() {  const FlareContractRegistry = artifacts.require("IFlareContractRegistry");  const registry: IFlareContractRegistryInstance =    await FlareContractRegistry.at(      "0xaD67FE66660Fb8dFE9d6b1b4240d8650e30F6019",    );  const fdcVerificationAddress: string =    await registry.getContractAddressByName("FdcVerification");  return await IFdcVerification.at(fdcVerificationAddress);}
```

The following function posts a proof request to the DA Layer.

scripts/utils/fdc.ts

```
async function postRequestToDALayer(  url: string,  request: any,  watchStatus: boolean = false,) {  const response = await fetch(url, {    method: "POST",    headers: {      //   "X-API-KEY": "",      "Content-Type": "application/json",    },    body: JSON.stringify(request),  });  if (watchStatus && response.status != 200) {    throw new Error(      `Response status is not OK, status ${response.status} ${response.statusText}\n`,    );  } else if (watchStatus) {    console.log("Response status is OK\n");  }  return await response.json();}
```

The main prepare the URL of the DA Layer's `proof-by-request-raw` endpoint. We contact this specific endpoint, because it return the abi encoded `IWeb2Json.Response` struct, and is thus unambiguous.

scripts/fdcExample/Web2Json.ts

```
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);}
```

The response the DA Layer server returns has the following fields:

-   The field `attestationType` holds the UTF8 encoded hex string of the attestation type name, padded to 32 bytes. Thus, it should match the value of the `attestationType` parameter in the Prepare the request step. In our case, that value is `0x4164647265737356616c69646974790000000000000000000000000000000000`.
-   The array `proofs` holds the Merkle proofs of our attestation request.
-   Lastly, `responseHex` is the ABI encoding of the chosen attestation type response struct. In this case, it is the `IWeb2Json.Response` struct.

We can ascertain the form of the proof request, as well as examine the response in advance, trough the [interactive documentation](https://ctn2-data-availability.flare.network/api-doc#/) of the DA Layer server.

<details>
<summary>An example complete proof response and decoded IWeb2Json.Response.</summary>

An example complete proof response and decoded `IWeb2Json.Response`.

An example DA Layer response for a request using the data provided in this example is:

```
{  response_hex: '0x  0000000000000000000000000000000000000000000000000000000000000020  576562324a736f6e000000000000000000000000000000000000000000000000  5075626c69635765623200000000000000000000000000000000000000000000  00000000000000000000000000000000000000000000000000000000000ef309  0000000000000000000000000000000000000000000000000000000000000000  00000000000000000000000000000000000000000000000000000000000000c0  0000000000000000000000000000000000000000000000000000000000000520  00000000000000000000000000000000000000000000000000000000000000e0  0000000000000000000000000000000000000000000000000000000000000120  0000000000000000000000000000000000000000000000000000000000000160  00000000000000000000000000000000000000000000000000000000000001a0  00000000000000000000000000000000000000000000000000000000000001e0  0000000000000000000000000000000000000000000000000000000000000220  00000000000000000000000000000000000000000000000000000000000002c0  000000000000000000000000000000000000000000000000000000000000001f  68747470733a2f2f73776170692e696e666f2f6170692f70656f706c652f3300  0000000000000000000000000000000000000000000000000000000000000003  4745540000000000000000000000000000000000000000000000000000000000  0000000000000000000000000000000000000000000000000000000000000002  7b7d000000000000000000000000000000000000000000000000000000000000  0000000000000000000000000000000000000000000000000000000000000002  7b7d000000000000000000000000000000000000000000000000000000000000  0000000000000000000000000000000000000000000000000000000000000002  7b7d000000000000000000000000000000000000000000000000000000000000  0000000000000000000000000000000000000000000000000000000000000078  7b6e616d653a202e6e616d652c206865696768743a202e6865696768742c206d  6173733a202e6d6173732c206e756d6265724f6646696c6d733a202e66696c6d  73207c206c656e6774682c207569643a20282e75726c207c2073706c69742822  2f2229207c202e5b2d315d207c20746f6e756d626572297d0000000000000000  0000000000000000000000000000000000000000000000000000000000000173  7b22636f6d706f6e656e7473223a205b7b22696e7465726e616c54797065223a  2022737472696e67222c20226e616d65223a20226e616d65222c202274797065  223a2022737472696e67227d2c7b22696e7465726e616c54797065223a202275  696e74323536222c20226e616d65223a2022686569676874222c202274797065  223a202275696e74323536227d2c7b22696e7465726e616c54797065223a2022  75696e74323536222c20226e616d65223a20226d617373222c20227479706522  3a202275696e74323536227d2c7b22696e7465726e616c54797065223a202275  696e74323536222c20226e616d65223a20226e756d6265724f6646696c6d7322  2c202274797065223a202275696e74323536227d2c7b22696e7465726e616c54  797065223a202275696e74323536222c20226e616d65223a2022756964222c20  2274797065223a202275696e74323536227d5d2c226e616d65223a2022746173  6b222c2274797065223a20227475706c65227d00000000000000000000000000  0000000000000000000000000000000000000000000000000000000000000020  0000000000000000000000000000000000000000000000000000000000000100  0000000000000000000000000000000000000000000000000000000000000020  00000000000000000000000000000000000000000000000000000000000000a0  0000000000000000000000000000000000000000000000000000000000000060  0000000000000000000000000000000000000000000000000000000000000020  0000000000000000000000000000000000000000000000000000000000000006  0000000000000000000000000000000000000000000000000000000000000003  0000000000000000000000000000000000000000000000000000000000000005  52322d4432000000000000000000000000000000000000000000000000000000',  attestation_type: '0x576562324a736f6e000000000000000000000000000000000000000000000000',  proof: [    '0x7f550d4fb5b312798757f8851bdaa92b3f6da403aa191b0a9d53bd7b03fc5b25',    '0x7494ee278799510e6bbbcc5b1cff1c0da0fdca3531b1ca1e54b7d4a38e729bfc',    '0xeab2d61332b9f1edceb7198357b737c2caf1e1b5abf8a3c86b205b29ecf6f08a',    '0x962db69b54aba0bd4225b831d07080ab986e4fc1203a5a87c8302cf5e8509ea8'  ]}
```

The `proof` field is dependent on the round in which the attestation request was submitted; it contains proofs for all of the requests submitted in that round. In the case of a single attestation request it is an empty list `[]` (the proof is the merkle root itself).

The decoded `IWeb2Json.Response` struct is:

```
[  '0x576562324a736f6e000000000000000000000000000000000000000000000000',  '0x5075626c69635765623200000000000000000000000000000000000000000000',  '979721',  '0',  [    'https://swapi.info/api/people/3',    'GET',    '{}',    '{}',    '{}',    '{name: .name, height: .height, mass: .mass, numberOfFilms: .films | length, uid: (.url | split("/") | .[-1] | tonumber)}',    '{"components": [{"internalType": "string", "name": "name", "type": "string"},{"internalType": "uint256", "name": "height", "type": "uint256"},{"internalType": "uint256", "name": "mass", "type": "uint256"},{"internalType": "uint256", "name": "numberOfFilms", "type": "uint256"},{"internalType": "uint256", "name": "uid", "type": "uint256"}],"name": "task","type": "tuple"}',    url: 'https://swapi.info/api/people/3',    httpMethod: 'GET',    headers: '{}',    queryParams: '{}',    body: '{}',    postProcessJq: '{name: .name, height: .height, mass: .mass, numberOfFilms: .films | length, uid: (.url | split("/") | .[-1] | tonumber)}',    abiSignature: '{"components": [{"internalType": "string", "name": "name", "type": "string"},{"internalType": "uint256", "name": "height", "type": "uint256"},{"internalType": "uint256", "name": "mass", "type": "uint256"},{"internalType": "uint256", "name": "numberOfFilms", "type": "uint256"},{"internalType": "uint256", "name": "uid", "type": "uint256"}],"name": "task","type": "tuple"}'  ],  [    '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000552322d4432000000000000000000000000000000000000000000000000000000',    abiEncodedData: '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000552322d4432000000000000000000000000000000000000000000000000000000'  ],  attestationType: '0x576562324a736f6e000000000000000000000000000000000000000000000000',  sourceId: '0x5075626c69635765623200000000000000000000000000000000000000000000',  votingRound: '979721',  lowestUsedTimestamp: '0',  requestBody: [    'https://swapi.info/api/people/3',    'GET',    '{}',    '{}',    '{}',    '{name: .name, height: .height, mass: .mass, numberOfFilms: .films | length, uid: (.url | split("/") | .[-1] | tonumber)}',    '{"components": [{"internalType": "string", "name": "name", "type": "string"},{"internalType": "uint256", "name": "height", "type": "uint256"},{"internalType": "uint256", "name": "mass", "type": "uint256"},{"internalType": "uint256", "name": "numberOfFilms", "type": "uint256"},{"internalType": "uint256", "name": "uid", "type": "uint256"}],"name": "task","type": "tuple"}',    url: 'https://swapi.info/api/people/3',    httpMethod: 'GET',    headers: '{}',    queryParams: '{}',    body: '{}',    postProcessJq: '{name: .name, height: .height, mass: .mass, numberOfFilms: .films | length, uid: (.url | split("/") | .[-1] | tonumber)}',    abiSignature: '{"components": [{"internalType": "string", "name": "name", "type": "string"},{"internalType": "uint256", "name": "height", "type": "uint256"},{"internalType": "uint256", "name": "mass", "type": "uint256"},{"internalType": "uint256", "name": "numberOfFilms", "type": "uint256"},{"internalType": "uint256", "name": "uid", "type": "uint256"}],"name": "task","type": "tuple"}'  ],  responseBody: [    '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000552322d4432000000000000000000000000000000000000000000000000000000',    abiEncodedData: '0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000552322d4432000000000000000000000000000000000000000000000000000000'  ]]
```

</details>

## Use the data[​](#use-the-data "Direct link to Use the data")

We will now define a simple contract, that will demonstrate how the data can be used onchain. The contract will receive character data from the [Star Wars API](https://swapi.dev/), and store it in a `StarWarsCharacter` struct. It will do so only if the proof is valid.

src/fdcExample/Web2Json.sol

```
struct StarWarsCharacter {    string name;    uint256 numberOfMovies;    uint256 apiUid;    uint256 bmi;}
```

We will also need a `DataTransportObject` struct, that will allow us to decode the data.

src/fdcExample/Web2Json.sol

```
struct DataTransportObject {    string name;    uint256 height;    uint256 mass;    uint256 numberOfMovies;    uint256 apiUid;}
```

The code of the contract is as follows.

src/fdcExample/Web2Json.sol

```
contract StarWarsCharacterList {    mapping(uint256 => StarWarsCharacter) public characters;    uint256[] public characterIds;    function isWeb2JsonProofValid(        IWeb2Json.Proof calldata _proof    ) private view returns (bool) {        // Inline the check for now until we have an official contract deployed        return            ContractRegistry.getFdcVerification().verifyWeb2Json(                _proof            );    }    function addCharacter(IWeb2Json.Proof calldata data) public {        require(isWeb2JsonProofValid(data), "Invalid proof");        DataTransportObject memory dto = abi.decode(            data.data.responseBody.abiEncodedData,            (DataTransportObject)        );        require(characters[dto.apiUid].apiUid == 0, "Character already exists");        StarWarsCharacter memory character = StarWarsCharacter({            name: dto.name,            numberOfMovies: dto.numberOfMovies,            apiUid: dto.apiUid,            bmi: (dto.mass * 100 * 100) / (dto.height * dto.height)        });        characters[dto.apiUid] = character;        characterIds.push(dto.apiUid);    }    function getAllCharacters()        public        view        returns (StarWarsCharacter[] memory)    {        StarWarsCharacter[] memory result = new StarWarsCharacter[](            characterIds.length        );        for (uint256 i = 0; i < characterIds.length; i++) {            result[i] = characters[characterIds[i]];        }        return result;    }}
```

### Verify proof[​](#verify-proof "Direct link to Verify proof")

FDC optimizes onchain storage costs by implementing a hybrid data verification system. Instead of storing complete datasets onchain, it stores only Merkle proofs, while maintaining the actual data through trusted offchain providers. This approach significantly reduces gas costs while preserving data integrity.

When requested, data providers supply the original data along with its corresponding Merkle proof. The protocol verifies data authenticity by comparing the provided Merkle proof against the onchain Merkle root. A successful match confirms the data's integrity and authenticity within the FDC system.

While data verification is optional if you trust your data provider, FDC ensures transparency by making verification possible at any time. This capability is crucial for maintaining system integrity and allowing users to independently verify data when needed, particularly in production environments.

FDC provides verification functionality through the `FdcVerification` contract. If the proof is valid, the function `verifyWeb2Json` will return `true`, otherwise `false`.

We deploy and verify this contract with the `deployAndVerifyContract` function in the `scripts/fdcExample/Web2Json.ts` file.

scripts/fdcExample/Web2Json.ts

```
async function deployAndVerifyContract() {  const args: any[] = [];  const characterList: StarWarsCharacterListInstance =    await StarWarsCharacterList.new(...args);  try {    await run("verify:verify", {      address: characterList.address,      constructorArguments: args,    });  } catch (e: any) {    console.log(e);  }  console.log("StarWarsCharacterList deployed to", characterList.address, "\n");  return characterList;}
```

## Interact with contract[​](#interact-with-contract "Direct link to Interact with contract")

We define an additional function that allows us to interact with the just deployed contract. The `interactWithContract` function also takes the proof retrieved in the previous step as an argument. It abi decodes the `response_hex` value to an `IWeb2Json.Response` struct. From that and the array of proofs, it constructs an `IWeb2Json.Proof` object, on which it call the `registerAddress` function of the `AddressRegistry` contract deployed above. The contract verifies the address, and the script prints it to the console.

scripts/fdcExample/Web2Json.ts

```
async function interactWithContract(  characterList: StarWarsCharacterListInstance,  proof: any,) {  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");  const transaction = await characterList.addCharacter({    merkleProof: proof.proof,    data: decodedResponse,  });  console.log("Transaction:", transaction.tx, "\n");  console.log(    "Star Wars Characters:\n",    await characterList.getAllCharacters(),    "\n",  );}
```

We can run the whole script by calling the following console command.

```
yarn hardhat run scripts/fdcExample/Web2Json.ts --network coston2
```
