Getting Started

Confused about HDP? Start here!

This document is a guidebook for utilizing Herodotus's Data Processor. The general documentation is here. It aims to provide a practical, hands-on approach to using the API effectively and to assist builders in understanding and leveraging its capabilities, particularly in hackathons.

Welcome All Builders :)

Welcome to the HDP Builder Guide! HDP stands for Herodotus Data Processor. This page will show you how to use HDP step by step. You'll learn what you can do with HDP and how to actually use its API. Our API allows you to generate proofs of on-chain data access and custom computations! If you want to know more about storage proofs, check out this article.

What Can You Build with HDP?

Are you interested in accessing massive historical data from one chain and using it in another? Would you like to run arbitrary computations within this data?

With the help of HDP, you can achieve this with just a simple API request or by defining a simple Cairo module. We leverage storage proofs to access data across different chains and even retrieve information from older blocks. By running a ZK VM, you can prove any computation with this verified data.

Currently, the HDP API supports the following environments. For more details on available API methods, refer to this documentation.

Getting Started with HDP

In this chapter, we will provide an example of a simple program that can sum up all the balances of a given address on Ethereum and return the validated result!

API Tutorial

  1. Design: Determine the data you want to read and how to compute it.

  2. Module (optional): Define a module that implements the logic for accessing the data and performing the computation.

  3. Request: Create an API request body based on the requirements you want to test.

  4. Access: You can call the FactRegistry contract directly or call the API task to get the result and use it.

1. Design

In this example, we want to prove the total balance of a given address over a range of blocks. Specifically, we need to sum up the balances of the address at different block numbers and return the validated result.

To achieve this, we need to:

  • Access the balance of the given address at specific block numbers.

  • Sum up these balances.

Since we are performing a custom computation (summing balances over blocks), we need to use a Module task to implement this logic. If you want to know what task options are available and how they differ, check out the task section.

2. Module

Now that we have determined the task should be of module type, it's time to create a Cairo program. Check out the template if you want to quickly start.

The goal for the module is to sum the balances of the given address at specified block numbers.

#[starknet::contract]
mod contract {
    use hdp_cairo::{HDP, memorizer::account_memorizer::{AccountKey, AccountMemorizerImpl}};

    #[storage]
    struct Storage {}

    #[external(v0)]
    pub fn main(
        ref self: ContractState, hdp: HDP, mut block_number_list: Array<u32>, address: felt252
    ) -> u256 {
        let mut sum: u256 = 0;
        loop {
            match block_number_list.pop_front() {
                Option::Some(block_number) => {
                    sum += hdp
                        .account_memorizer
                        .get_balance(
                            AccountKey {
                                chain_id: 11155111,
                                block_number: block_number.into(),
                                address: address
                            }
                        )
                },
                Option::None => { break; },
            }
        };
        sum
    }
}

After creating the module, we need to upload it to use it in the next request step. The file we are going to upload is the compiled Cairo program, which can be generated via:

scarb build

Now that you have compiled the program, let's upload it. Detailed API documentation for interacting with the program registry is available here.

curl --location 'https://sharp.api.herodotus.cloud/submit-program?apiKey={yourApiKey}' \
--form 'programFile=@"./custom_module_custom_module.compiled_contract_class.json"' \

You will receive a response like this:

0xaae117f9cdfa4fa4d004f84495c942adebf17f24aec8f86e5e6ea29956b47e

To ensure the program is uploaded successfully, you can check the program registry by:

curl --location 'http://program-registery.api.herodotus.cloud/get-metadata?program_hash=0xaae117f9cdfa4fa4d004f84495c942adebf17f24aec8f86e5e6ea29956b47e'

Congratulations! You have successfully defined a custom module and deployed it. Remember this program hash, and let's move to the next step.

3. Request (Off-Chain)

Finally, we can call the API to trigger this module. Here is what the request body looks like:

{
  "destinationChainId": "ETHEREUM_SEPOLIA",
  "tasks": [
    {
      "type": "Module",
      "programHash": "0xaae117f9cdfa4fa4d004f84495c942adebf17f24aec8f86e5e6ea29956b47e",
      "inputs": [
        {
          "visibility": "public",
          "value": "0x3"
        },
        {
          "visibility": "public",
          "value": "0x5222A4"
        },
        {
          "visibility": "public",
          "value": "0x5222A7"
        },
        {
          "visibility": "public",
          "value": "0x5222C4"
        },
        {
          "visibility": "public",
          "value": "0x13cb6ae34a13a0977f4d7101ebc24b87bb23f0d5"
        }
      ]
    }
  ]
}

Breaking Down the Request Body

  • destinationChainId: The destination chain ID where the task will be executed.

  • tasks: An array of tasks to be executed.

    • type: The type of the task. In this case, it's "Module".

    • programHash: The hash of the program to be executed, which you obtained from the previous step.

    • inputs: The input parameters for the module.

Recalling the main function's input parameters:

pub fn main(
    ref self: ContractState,
    hdp: HDP,
    mut block_number_list: Array<u32>,
    address: felt252
) -> u256

Note that the first two parameters are used internally and are not part of the inputs we need to provide. Therefore, every element in the input array sequentially matches from the third parameter onward. So, the request body above corresponds to:

  • block_number_list: [0x5222A4, 0x5222A7, 0x5222C4]. Since this list has a dynamic size, we need to add the size of the array at the front, resulting in [0x3, 0x5222A4, 0x5222A7, 0x5222C4].

  • address: 0x13cb6ae34a13a0977f4d7101ebc24b87bb23f0d5

So the total input array is:

[
  "0x3",
  "0x5222A4",
  "0x5222A7",
  "0x5222C4",
  "0x13cb6ae34a13a0977f4d7101ebc24b87bb23f0d5"
]

Each element needs to specify the visibility and value, like this:

{
  "visibility": "public",
  "value": "0x3"
}

Now let's submit this request body (check out the docs here).

curl --location 'https://hdp.api.herodotus.cloud/submit-batch-query?apiKey={yourApiKey}' \
--header 'Content-Type: application/json' \
--data '{
  "destinationChainId": "ETHEREUM_SEPOLIA",
  "tasks": [
    {
      "type": "Module",
      "programHash": "0xaae117f9cdfa4fa4d004f84495c942adebf17f24aec8f86e5e6ea29956b47e",
      "inputs": [
        {
          "visibility": "public",
          "value": "0x3"
        },
        {
          "visibility": "public",
          "value": "0x5222A4"
        },
        {
          "visibility": "public",
          "value": "0x5222A7"
        },
        {
          "visibility": "public",
          "value": "0x5222C4"
        },
        {
          "visibility": "public",
          "value": "0x13cb6ae34a13a0977f4d7101ebc24b87bb23f0d5"
        }
      ]
    }
  ]}'

Each request will return a batchId and an array of taskHashes, like:

{
  "taskHashes": [
    "0x785dce67cebd991614cb1212abc9f478f827fe08e82df53abc21045a70f9b9b1"
  ],
  "batchId": "bq_01J8KJ729KRGJNSGNJY4PWAAYZ"
}

From the given batchId, you can check the status of the task via the Explorer.

You can also check the status via a request (see the docs here).

4. Access (On-Chain)

After you confirm that the requested task has been finalized:

In the Explorer, at the last step, you can get the transaction hash. Then you can get the result via the HdpExecutionStore contract. You can access it via the getFinalizedTaskResult method.

Last updated