# Getting Started

> Learn how to consume Scaling feeds on Flare.

> 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/ftso/scaling/getting-started

Scaling enables offchain access to anchor feeds by leveraging Flare's network of 100 independent data providers and a robust commit-reveal process every 90 seconds.

To read anchor feeds on Flare, follow these key steps:

1.  **Fetch anchor feed data offchain:**
    
    Use the Data Availability (DA) Layer API to retrieve anchor feeds and their associated cryptographic proofs.
    
2.  **Verify the proof onchain:**
    
    Validate the provided proof onchain to ensure the data matches the finalized version committed by [Scaling](/ftso/scaling/overview).
    
3.  **Use the feed data onchain:**
    
    After verification, integrate the feed data into your onchain application logic.
    

Scaling only stores **commitments to feed data** onchain. Complete feed data resides offchain but can be verified against an onchain Merkle root, ensuring data integrity and tamper resistance.

## DA Layer API URLs[​](#da-layer-api-urls "Direct link to DA Layer API URLs")

Rate limits

The public DA Layer endpoints are rate-limited. To request an API key for higher limits, create an [API Key Request Issue](https://github.com/flare-foundation/developer-hub/issues/new/choose).

The DA Layer provides API endpoints for querying offchain data from Flare protocols.

Network

Base URL

Flare Mainnet

`https://flr-data-availability.flare.network/`

Songbird Canary-Network

`https://sgb-data-availability.flare.network/`

Flare Testnet Coston2

`https://ctn2-data-availability.flare.network/`

Songbird Testnet Coston

`https://ctn-data-availability.flare.network/`

All networks have the same API structure. For a full list of endpoints see [Data Availability API Reference](/ftso/scaling/solidity-reference/data-availability-api).

## Fetching anchor feed data[​](#fetching-anchor-feed-data "Direct link to Fetching anchor feed data")

The DA Layer API allows querying values and proofs for multiple feeds from the same voting epoch in a single request. Use the `anchor-feeds-with-proof` POST endpoint to retrieve pricing data.

To fetch the feed values for FLR/USD, BTC/USD, and ETH/USD at the latest voting round, use the following command:

```
curl -X 'POST' \  'https://flr-data-availability.flare.network/api/v0/ftso/anchor-feeds-with-proof' \  -H 'accept: application/json' \  -H 'Content-Type: application/json' \  -d '{  "feed_ids": [    "0x01464c522f55534400000000000000000000000000",    "0x014254432f55534400000000000000000000000000",    "0x014554482f55534400000000000000000000000000"  ]}'
```

fetch\_anchor\_feeds.js

```
// THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.const BASE_URL = "https://flr-data-availability.flare.network/";const API_KEY = "<your-api-key>";// Feed IDs, see https://dev.flare.network/ftso/scaling/anchor-feeds for full listconst FEED_IDS = [  "0x01464c522f55534400000000000000000000000000", // FLR/USD  "0x014254432f55534400000000000000000000000000", // BTC/USD  "0x014554482f55534400000000000000000000000000", // ETH/USD];async function fetchAnchorFeeds(feedIds, votingRoundId = null) {  const url = votingRoundId    ? `${BASE_URL}/api/v0/ftso/anchor-feeds-with-proof?voting_round_id=${votingRoundId}`    : `${BASE_URL}/api/v0/ftso/anchor-feeds-with-proof`;  return await (    await fetch(url, {      method: "POST",      headers: {        "X-API-KEY": API_KEY,        "Content-Type": "application/json",      },      body: JSON.stringify({        feed_ids: feedIds,      }),    })  ).json();}fetchAnchorFeeds(FEED_IDS).then((data) => {  console.log("Anchor feeds data:", data);});
```

fetch\_anchor\_feeds.py

```
# THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.import asyncioimport aiohttpBASE_URL = "https://flr-data-availability.flare.network/"API_KEY = "<your-api-key>"FEED_IDS = [    "0x01464c522f55534400000000000000000000000000",  # FLR/USD    "0x014254432f55534400000000000000000000000000",  # BTC/USD    "0x014554482f55534400000000000000000000000000",  # ETH/USD]async def fetch_anchor_feeds(    feed_ids: list[str], voting_round_id: int | None = None) -> list[dict]:    url = f"{BASE_URL}api/v0/ftso/anchor-feeds-with-proof"    if voting_round_id:        url += f"?voting_round_id={voting_round_id}"    async with (        aiohttp.ClientSession() as session,        session.post(            url,            headers={                "X-API-KEY": API_KEY,                "Content-Type": "application/json",            },            json={"feed_ids": feed_ids},        ) as response,    ):        return await response.json()async def main() -> None:    data = await fetch_anchor_feeds(FEED_IDS)    print("Anchor feeds data:", data)asyncio.run(main())
```

fetch\_anchor\_feeds.go

```
// THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.package flareimport (	"bytes"	"encoding/json"	"fmt"	"io"	"net/http")const (	BaseURL = "https://flr-data-availability.flare.network/"	ApiKey  = "<your-api-key>")var FeedIds = []string{	"0x01464c522f55534400000000000000000000000000", // FLR/USD	"0x014254432f55534400000000000000000000000000", // BTC/USD	"0x014554482f55534400000000000000000000000000", // ETH/USD}var VotingRoundId = ""type AnchorFeed struct {	Body struct {		VotingRoundID int    `json:"votingRoundId"`		ID            string `json:"id"`		Value         int    `json:"value"`		TurnoutBIPS   int    `json:"turnoutBIPS"`		Decimals      int    `json:"decimals"`	} `json:"body"`	Proof []string `json:"proof"`}func FetchAnchorFeeds() ([]AnchorFeed, error) {	url := BaseURL + "api/v0/ftso/anchor-feeds-with-proof"	if VotingRoundId != "" {		url += "?voting_round_id=" + VotingRoundId	}	payload, _ := json.Marshal(map[string]interface{}{"feed_ids": FeedIds})	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))	req.Header.Set("X-API-KEY", ApiKey)	req.Header.Set("Content-Type", "application/json")	resp, err := http.DefaultClient.Do(req)	if err != nil || resp.StatusCode != http.StatusOK {		return nil, fmt.Errorf("request failed: %v (status: %d)", err, resp.StatusCode)	}	defer resp.Body.Close()	body, err := io.ReadAll(resp.Body)	if err != nil {		return nil, fmt.Errorf("failed to read response: %w", err)	}	var feeds []AnchorFeed	return feeds, json.Unmarshal(body, &feeds)}func main() {	feeds, err := FetchAnchorFeeds()	if err != nil {		fmt.Printf("Error: %v\n", err)		return	}	fmt.Printf("Anchor feeds: %+v\n", feeds)}
```

fetch\_anchor\_feeds.rs

```
// THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.#![allow(dead_code)]#![allow(unused)]use reqwest::Client;use serde_json::json;const BASE_URL: &str = "https://flr-data-availability.flare.network/";const API_KEY: &str = "<your-api-key>"; // Optionalconst FEED_IDS: &[&str] = &[    "0x01464c522f55534400000000000000000000000000", // FLR/USD    "0x014254432f55534400000000000000000000000000", // BTC/USD    "0x014554482f55534400000000000000000000000000", // ETH/USD];pub async fn fetch_anchor_feed(    feed_ids: &[&str],    voting_round_id: Option<u32>,) -> Result<Vec<serde_json::Value>, reqwest::Error> {    let client = Client::new();    let mut url = format!("{BASE_URL}api/v0/ftso/anchor-feeds-with-proof");    if let Some(id) = voting_round_id {        url.push_str(&format!("?voting_round_id={id}"));    }    let response = client        .post(&url)        .header("X-API-KEY", API_KEY)        .header("Content-Type", "application/json")        .json(&json!({ "feed_ids": feed_ids }))        .send()        .await?;    let json: Vec<serde_json::Value> = response.json().await?;    Ok(json)}#[tokio::main]async fn main() {    match fetch_anchor_feed(FEED_IDS, None).await {        Ok(data) => println!("Anchor feeds data: {data:?}"),        Err(err) => eprintln!("Error fetching anchor feeds: {err}"),    }}
```

#### API response structure[​](#api-response-structure "Direct link to API response structure")

The response contains JSON objects for each feed, with the following fields:

-   `votingRoundId`: The voting round ID (each round lasts 90 seconds; see the [Flare Systems Explorer](https://flare-systems-explorer.flare.network/voting-round)).
-   `id`: The feed ID (refer to the [list of anchor feeds](/ftso/scaling/anchor-feeds)).
-   `value`: The integer value of the feed.
-   `turnoutBIPS`: The percentage of voting weight (in basis points) that contributed to the finalized value.
-   `decimals`: The number of decimal places for the feed.
-   `proof`: The Merkle proof array for data verification.

#### Example Response (for BTC/USD)[​](#example-response-for-btcusd "Direct link to Example Response (for BTC/USD)")

```
[  {    "body": {      "votingRoundId": 823386,      "id": "0x014254432f55534400000000000000000000000000",      "value": 9837867,      "turnoutBIPS": 9442,      "decimals": 2    },    "proof": [      "0x79b8a56bf66ae571ed4c0e3e1317825277c43f5ca3b5a85b834fb6407de03b63",      "...additional proof hashes..."    ]  }]
```

The floating point value of a feed can be calculated by dividing the `value` by 10^`decimals`. For example, if the feed value of BTC/USD is `6900420` and the decimal is `2`, the floating point feed value is `69004.20`.

### Fetching Timestamps[​](#fetching-timestamps "Direct link to Fetching Timestamps")

The `ftso/anchor-feeds-with-proof` endpoint returns a `votingRoundId`. Each voting round lasts for a fixed duration of **90 seconds**. To determine the **starting timestamp**, use the `fsp/status` GET endpoint:

```
curl -X 'GET' \  'https://flr-data-availability.flare.network/api/v0/fsp/status' \  -H 'accept: application/json'
```

warning

-   The timestamps returned correspond to the **start** of the voting round, which lasts for **90 seconds**.
-   Prices for a given voting round are finalized at the **end** of the round, calculated as `start_timestamp + 90s`.

#### Example Response[​](#example-response "Direct link to Example Response")

```
{  "active": {    "voting_round_id": 839641,    "start_timestamp": 1733997690  },  "latest_fdc": {    "voting_round_id": -1,    "start_timestamp": -1  },  "latest_ftso": {    "voting_round_id": 839640,    "start_timestamp": 1733997600  }}
```

The response indicates that:

-   The **currently active voting round** has `voting_round_id` [839641](https://flare-systems-explorer.flare.network/voting-round/839641), which **started at** `1733997690`.
-   The **most recently finalized FTSO voting round** has `voting_round_id` [839640](https://flare-systems-explorer.flare.network/voting-round/839640), which **started at** `1733997600`.

## Verifying proof onchain[​](#verifying-proof-onchain "Direct link to Verifying proof onchain")

To verify feed data onchain, use the [`FtsoV2Interface`](/ftso/solidity-reference/FtsoV2Interface). This interface offers the [`verifyFeedData`](/ftso/solidity-reference/FtsoV2Interface#verifyfeeddata) method to validate feed data and proof against the onchain Merkle root. The function requires a single input struct [`FeedDataWithProof`](/ftso/solidity-reference/FtsoV2Interface#feeddatawithproof), which includes the feed data and voting round ID within the [`FeedData`](/ftso/solidity-reference/FtsoV2Interface#feeddata) struct, and a Merkle proof.

An example contract verifying and consuming anchor feeds onchain

FtsoV2AnchorFeedConsumer.sol

```
// SPDX-License-Identifier: MITpragma solidity >=0.8.0 <0.9.0;import {FtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/coston2/FtsoV2Interface.sol";import {ContractRegistry} from "@flarenetwork/flare-periphery-contracts/coston2/ContractRegistry.sol";import {TestFtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/coston2/TestFtsoV2Interface.sol";/** * THIS IS AN EXAMPLE CONTRACT. * DO NOT USE THIS CODE IN PRODUCTION. */contract FtsoV2AnchorFeedConsumer {    mapping(uint32 => mapping(bytes21 => TestFtsoV2Interface.FeedData))        public provenFeeds;    function savePrice(        TestFtsoV2Interface.FeedDataWithProof calldata data    ) external {        /* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */        TestFtsoV2Interface ftsoV2 = ContractRegistry.getTestFtsoV2();        // Step 1: Verify the proof        require(ftsoV2.verifyFeedData(data), "Invalid proof");        // Step 2: Use the feed data with app specific logic        // Here the feeds are saved        provenFeeds[data.body.votingRoundId][data.body.id] = data.body;    }}
```

[Open in Remix](https://remix.ethereum.org/#url=https://github.com/flare-foundation/developer-hub/blob/main/examples/developer-hub-solidity/FtsoV2AnchorFeedConsumer.sol&version=builtin&evmVersion=cancun&optimize=true&runs=200)

## Fetching and verifying feeds[​](#fetching-and-verifying-feeds "Direct link to Fetching and verifying feeds")

The following example shows how to query feed and proof data from DA Layer and submit it to the onchain consumer:

fetch\_and\_verify\_anchor\_onchain.js

```
// THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.import { artifacts } from "hardhat";import { fetchAnchorFeeds } from "./fetch_anchor_feeds";const FtsoV2AnchorFeedConsumer = artifacts.require(  "FtsoV2AnchorFeedConsumer.sol",);// Feed IDs, see https://dev.flare.network/ftso/scaling/anchor-feeds for full listconst BTC_USD_FEED_ID = "0x014254432f55534400000000000000000000000000";const TARGET_VOTING_ROUND = 823402;async function main() {  // Deploy FtsoV2AnchorFeedConsumer contract  const feedConsumer = await FtsoV2AnchorFeedConsumer.new();  // Fetch price from DA Layer  const feedData = await fetchAnchorFeeds(    [BTC_USD_FEED_ID],    TARGET_VOTING_ROUND,  );  // Save fetched price to contract  await feedConsumer.savePrice({    proof: feedData[0].proof,    body: feedData[0].data,  });  // Query saved price from contract  const savedPrice = await feedConsumer.provenFeeds.call(    TARGET_VOTING_ROUND,    BTC_USD_FEED_ID,  );  const formattedPrice = savedPrice.value * Math.pow(10, -savedPrice.decimals);  console.log(    `Saved price: ${formattedPrice} USD at voting round: ${savedPrice.votingRoundId.toString()}`,  );}main();
```

fetch\_and\_verify\_anchor\_onchain.py

```
# THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.import asynciofrom web3 import AsyncHTTPProvider, AsyncWeb3from fetch_anchor_feeds import fetch_anchor_feedsRPC_URL = "https://coston2-api.flare.network/ext/C/rpc"contract_abi = [    {        "inputs": [            {"internalType": "uint32", "name": "", "type": "uint32"},            {"internalType": "bytes21", "name": "", "type": "bytes21"},        ],        "name": "provenFeeds",        "outputs": [            {"internalType": "uint32", "name": "votingRoundId", "type": "uint32"},            {"internalType": "bytes21", "name": "id", "type": "bytes21"},            {"internalType": "int32", "name": "value", "type": "int32"},            {"internalType": "uint16", "name": "turnoutBIPS", "type": "uint16"},            {"internalType": "int8", "name": "decimals", "type": "int8"},        ],        "stateMutability": "view",        "type": "function",    },    {        "inputs": [            {                "components": [                    {"internalType": "bytes32[]", "name": "proof", "type": "bytes32[]"},                    {                        "components": [                            {                                "internalType": "uint32",                                "name": "votingRoundId",                                "type": "uint32",                            },                            {                                "internalType": "bytes21",                                "name": "id",                                "type": "bytes21",                            },                            {"internalType": "int32", "name": "value", "type": "int32"},                            {                                "internalType": "uint16",                                "name": "turnoutBIPS",                                "type": "uint16",                            },                            {                                "internalType": "int8",                                "name": "decimals",                                "type": "int8",                            },                        ],                        "internalType": "struct FtsoV2Interface.FeedData",                        "name": "body",                        "type": "tuple",                    },                ],                "internalType": "struct FtsoV2Interface.FeedDataWithProof",                "name": "data",                "type": "tuple",            }        ],        "name": "savePrice",        "outputs": [],        "stateMutability": "nonpayable",        "type": "function",    },]contract_address = "0x069227C6A947d852c55655e41C6a382868627920"w3 = AsyncWeb3(    AsyncHTTPProvider(RPC_URL),)# Create contract instancecontract = w3.eth.contract(address=contract_address, abi=contract_abi)# Feed IDs and target voting roundBTC_USD_FEED_ID = "0x014254432f55534400000000000000000000000000"TARGET_VOTING_ROUND = 823402async def main() -> None:    try:        feed_data = await fetch_anchor_feeds([BTC_USD_FEED_ID], TARGET_VOTING_ROUND)        proof = feed_data[0]["proof"]        body = feed_data[0]["body"]        # Create transaction        txn_hash = await contract.functions.savePrice(            {"proof": proof, "body": body}        ).transact()        tx_receipt = await w3.eth.wait_for_transaction_receipt(txn_hash)        print(f"Transaction sent with hash: {tx_receipt}")        saved_price = await contract.functions.provenFeeds(            TARGET_VOTING_ROUND, BTC_USD_FEED_ID        ).call()        formatted_price = saved_price[2] * (10 ** -saved_price[4])        print(f"Saved price: {formatted_price} USD at voting round: {saved_price[0]}")    except Exception as err:        print(f"Error: {err}")# Run the main functionif __name__ == "__main__":    asyncio.run(main())
```

fetch\_and\_verify\_anchor\_onchain.go

```
// THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.package flareimport (	"context"	"crypto/ecdsa"	"encoding/hex"	"fmt"	"log"	"math/big"	"github.com/ethereum/go-ethereum/accounts/abi/bind"	"github.com/ethereum/go-ethereum/common"	"github.com/ethereum/go-ethereum/crypto"	"github.com/ethereum/go-ethereum/ethclient")func convertToByteArray(proof []string) ([][32]byte, error) {	var result [][32]byte	for _, str := range proof {		decoded, err := hex.DecodeString(str)		if err != nil {			return nil, fmt.Errorf("failed to decode string: %v", err)		}		if len(decoded) != 32 {			return nil, fmt.Errorf("decoded string is not 32 bytes: got %d bytes", len(decoded))		}		var byteArray [32]byte		copy(byteArray[:], decoded)		result = append(result, byteArray)	}	return result, nil}func VerifyAnchorFeedsOnchain() {	const (		PRIVATE_KEY               = "your_private_key_here"		RPC_URL                   = "https://coston2-api.flare.network/ext/C/rpc"		DEPLOYED_CONTRACT_ADDRESS = "0x069227C6A947d852c55655e41C6a382868627920"	)	var feed_Id [21]byte	var proof [][32]byte	feeds, err := FetchAnchorFeeds()	if err != nil {		log.Fatal(err)	}	copy(feed_Id[:], feeds[0].Body.ID)	votingRoundId := uint32(feeds[0].Body.VotingRoundID)	feedBody := FtsoV2InterfaceFeedData{		votingRoundId,		feed_Id,		int32(feeds[0].Body.Value),		uint16(feeds[0].Body.TurnoutBIPS),		int8(feeds[0].Body.Decimals),	}	proof, _ = convertToByteArray(feeds[0].Proof)	var feedWithProof = FtsoV2InterfaceFeedDataWithProof{		proof,		feedBody,	}	client, err := ethclient.Dial(RPC_URL)	if err != nil {		log.Fatal(err)	}	privateKey, err := crypto.HexToECDSA(PRIVATE_KEY)	if err != nil {		log.Fatalf("Failed to parse private key: %v", err)	}	publicKey := privateKey.Public()	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)	if !ok {		log.Fatal("Invalid public key type")	}	fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)	nonce, err := client.PendingNonceAt(context.Background(), fromAddress)	if err != nil {		log.Fatalf("Failed to fetch nonce: %v", err)	}	gasPrice, err := client.SuggestGasPrice(context.Background())	if err != nil {		log.Fatalf("Failed to fetch gas price: %v", err)	}	chainID, err := client.NetworkID(context.Background())	if err != nil {		log.Fatalf("Failed to fetch chain ID: %v", err)	}	auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)	if err != nil {		log.Fatalf("Failed to create transactor: %v", err)	}	auth.Nonce = big.NewInt(int64(nonce))	auth.Value = big.NewInt(0) //	auth.GasLimit = uint64(3000000)	auth.GasPrice = gasPrice	consumerAddr := common.HexToAddress(DEPLOYED_CONTRACT_ADDRESS)	anchorFeed, err := NewFtsoV2AnchorFeedConsumer(consumerAddr, client)	if err != nil {		log.Fatalf("Failed to initialize consumer contract: %v", err)	}	if err != nil {		log.Fatal(err)	}	tx, err := anchorFeed.SavePrice(auth, feedWithProof)	if err != nil {		log.Fatalf("Failed to save price: %v", err)	}	fmt.Printf("SavePrice transaction hash: %s\n", tx.Hash().Hex())	savedPrice, _ := anchorFeed.ProvenFeeds(&bind.CallOpts{}, votingRoundId, feed_Id)	fmt.Printf("%+v\n", savedPrice)}
```

fetch\_and\_verify\_anchor\_onchain.rs

```
// THIS IS EXAMPLE CODE. DO NOT USE THIS CODE IN PRODUCTION.use crate::FtsoV2Interface::{FeedData, FeedDataWithProof};use alloy::{    network::EthereumWallet,    primitives::{address, hex, FixedBytes},    providers::{Provider, ProviderBuilder},    signers::local::PrivateKeySigner,    sol,};use hex::FromHex;use serde_json::Value;// Import the fetch_anchor_feeds modulemod fetch_anchor_feeds;// ABI bindings for the Solidity contractsol!(    #[sol(rpc)]    FtsoV2AnchorFeedConsumer,    "FtsoV2AnchorFeedConsumer.json");const BTC_USD_FEED_ID: &str = "0x014254432f55534400000000000000000000000000";const TARGET_VOTING_ROUND: u32 = 823402;mod convert_type {    use super::*;    // Functions for type conversion    pub fn as_u16(value: &Value) -> Result<u16, Box<dyn std::error::Error>> {        value            .as_u64()            .and_then(|v| v.try_into().ok())            .ok_or("Invalid u16 value".into())    }    pub fn as_u32(value: &Value) -> Result<u32, Box<dyn std::error::Error>> {        value            .as_u64()            .and_then(|v| v.try_into().ok())            .ok_or("Invalid u32 value".into())    }    pub fn as_i8(value: &Value) -> Result<i8, Box<dyn std::error::Error>> {        value            .as_i64()            .and_then(|v| v.try_into().ok())            .ok_or("Invalid i8 value".into())    }    pub fn as_i32(value: &Value) -> Result<i32, Box<dyn std::error::Error>> {        value            .as_i64()            .and_then(|v| v.try_into().ok())            .ok_or("Invalid i32 value".into())    }    // Function to convert JSON proofs to FixedBytes    pub fn json_proofs_to_fixed_bytes(        proofs: &[Value],    ) -> Result<Vec<FixedBytes<32>>, Box<dyn std::error::Error>> {        let mut result = Vec::new();        for proof in proofs {            let proof_str = proof.as_str().ok_or("Proof value is not a string")?;            // Validate and convert hex string            let fixed_bytes = hex_to_fixed_bytes_32(proof_str)?;            result.push(fixed_bytes);        }        Ok(result)    }    // Function to convert hex string to FixedBytes<32>    pub fn hex_to_fixed_bytes_32(        hex_str: &str,    ) -> Result<FixedBytes<32>, Box<dyn std::error::Error>> {        // Handle both 0x-prefixed and unprefixed hex strings        let clean_hex = if hex_str.starts_with("0x") || hex_str.starts_with("0X") {            &hex_str[2..]        } else {            hex_str        };        // Validate length (64 characters = 32 bytes)        if clean_hex.len() != 64 {            return Err(format!(                "Hex string must be 64 characters (32 bytes), got {} characters",                clean_hex.len()            )            .into());        }        // Convert hex to bytes using the hex crate        let mut bytes = [0u8; 32];        hex::decode_to_slice(clean_hex, &mut bytes)            .map_err(|e| format!("Hex decoding failed: {e}"))?;        Ok(FixedBytes::<32>::from(bytes))    }}#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> {    let feed_data: Vec<Value> =        fetch_anchor_feeds::fetch_anchor_feed(&[BTC_USD_FEED_ID], Some(TARGET_VOTING_ROUND))            .await?;    let raw_proofs = feed_data[0]["proof"]        .as_array()        .ok_or("Missing or invalid proof array")?;    let raw_body = feed_data[0]["body"]        .as_object()        .ok_or("Missing or invalid body object")?;    let byte_proof = convert_type::json_proofs_to_fixed_bytes(raw_proofs)?;    let body_data = FeedData {        decimals: convert_type::as_i8(&raw_body["decimals"])?,        id: FixedBytes::<21>::from_slice(&hex::decode(            raw_body["id"].as_str().ok_or("Invalid id format")?,        )?),        turnoutBIPS: convert_type::as_u16(&raw_body["turnoutBIPS"])?,        value: convert_type::as_i32(&raw_body["value"])?,        votingRoundId: convert_type::as_u32(&raw_body["votingRoundId"])?,    };    let contract_address = address!("069227C6A947d852c55655e41C6a382868627920");    let private_key = " your_private_key_here";    let signer: PrivateKeySigner = private_key.parse()?;    let wallet = EthereumWallet::from(signer.clone());    let provider = ProviderBuilder::new()        .wallet(wallet)        .connect_http("https://coston2-api.flare.network/ext/C/rpc".parse()?);    let latest_block = provider.get_block_number().await?;    println!("Latest block number: {latest_block}");    let contract = FtsoV2AnchorFeedConsumer::new(contract_address, provider);    let feed_data_with_proof = FeedDataWithProof {        proof: byte_proof,        body: body_data,    };    let tx_hash = contract        .savePrice(feed_data_with_proof)        .send()        .await?        .watch()        .await?;    println!("Save Price transaction hash: {tx_hash}");    let feed_id: FixedBytes<21> = {        let hex_str = BTC_USD_FEED_ID            .strip_prefix("0x")            .unwrap_or(BTC_USD_FEED_ID);        let bytes: [u8; 21] = <[u8; 21]>::from_hex(hex_str)?;        FixedBytes::new(bytes)    };    let saved_price = contract        .provenFeeds(TARGET_VOTING_ROUND, feed_id)        .call()        .await?;    println!("Saved Price is {}", saved_price.value);    Ok(())}
```
