Skip to main content

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.

  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

Rate limits

The public DA Layer endpoints are rate-limited. To request an API key for higher limits, create an API Key Request Issue.

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

NetworkBase URL
Flare Mainnethttps://flr-data-availability.flare.network/
Songbird Canary-Networkhttps://sgb-data-availability.flare.network/

All networks have the same API structure. For a full list of endpoints see Data Availability API Reference.

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"
]
}'

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).
  • id: The feed ID (refer to the list of 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)

[
{
"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

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

{
"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, which started at 1733997690.
  • The most recently finalized FTSO voting round has voting_round_id 839640, which started at 1733997600.

Verifying proof onchain

To verify feed data onchain, use the FtsoV2Interface. This interface offers the verifyFeedData method to validate feed data and proof against the onchain Merkle root. The function requires a single input struct FeedDataWithProof, which includes the feed data and voting round ID within the FeedData struct, and a Merkle proof.

An example contract verifying and consuming anchor feeds onchain

FtsoV2AnchorFeedConsumer.sol
// SPDX-License-Identifier: MIT
pragma 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 {
TestFtsoV2Interface internal ftsoV2;
mapping(uint32 => mapping(bytes21 => TestFtsoV2Interface.FeedData))
public provenFeeds;

function savePrice(
TestFtsoV2Interface.FeedDataWithProof calldata data
) public {
/* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */
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;
}
}

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 list
const 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();