NEW

CCIP is now live for all developers. See what's new.

Back

Fetch and decode reports via a REST API

In this tutorial, you'll learn how to use Chainlink Data Streams with the Streams Direct implementation and the REST API to fetch reports from the Data Streams Aggregation Network. You'll set up your Go project, retrieve reports from the API, decode them, and log their attributes to your terminal.

Before you begin

  • Go Version: Make sure you have Go version 1.21 or higher. You can check your current version by running go version in your terminal and download the latest version from the official Go website if necessary.
  • API Credentials: Access to the Streams Direct implementation requires API credentials. If you haven't already, contact us to talk to an expert about integrating Chainlink Data Streams with your applications.

Tutorial

You'll start with the set up of your Go project. Next, you'll fetch and decode reports for both single and multiple feeds, and log their attributes to your terminal.

Set up your Go project

  1. Create and navigate to your project directory:

    mkdir data-streams-direct && cd data-streams-direct
    
  2. Initialize a Go module:

    go mod init data-streams-direct
    
  3. Open the go.mod file at the root of your project directory and include the necessary module and package information:

    module data-streams-direct
    
    go 1.21
    
    require (
    	github.com/ethereum/go-ethereum v1.12.2 // Ethereum blockchain interaction library
    	github.com/pkg/errors v0.9.1 // Library for handling errors
    	github.com/smartcontractkit/chainlink/v2 v2.2.1-0.20230823171354-1ead9ee6f6bb // Chainlink core components library
    )
    
    replace (
    	// Resolves version mismatch between cosmosSDK and hdevalence/ed25519consensus
    	filippo.io/edwards25519 => filippo.io/edwards25519 v1.0.0-rc.1
    
    	// Adds ARM support by updating CosmWasm to v1.2.4
    	github.com/CosmWasm/wasmvm => github.com/CosmWasm/wasmvm v1.2.4
    
    	//// Fix go mod tidy issue for ambiguous imports from go-ethereum
    	//// See https://github.com/ugorji/go/issues/279
    	github.com/btcsuite/btcd => github.com/btcsuite/btcd v0.22.1
    
    	// Aligns protobuf version with cosmos SDK requirements
    	github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
    )
  4. Create client and internal subfolders within your project directory:

    mkdir client internal
    
  5. Create a client.go file in the client subfolder and a decoder.go file in the internal subfolder:

    touch client/client.go
    touch internal/decoder.go
    
  6. Insert the provided code into client.go and decoder.go to enable your application to fetch and decode reports:

    // client.go
    
    package client
    
    import (
    	"crypto/hmac"
    	"crypto/sha256"
    	"encoding/hex"
    	"encoding/json"
    	"fmt"
    	"io"
    	"net/http"
    	"net/url"
    	"os"
    	"strconv"
    	"strings"
    	"time"
    
    	"github.com/ethereum/go-ethereum/common/hexutil"
    )
    
    type SingleReport struct {
    	FeedID                hexutil.Bytes `json:"feedID"`
    	ValidFromTimestamp    uint32        `json:"validFromTimestamp"`
    	ObservationsTimestamp uint32        `json:"observationsTimestamp"`
    	FullReport            hexutil.Bytes `json:"fullReport"`
    }
    
    type SingleReportResponse struct {
    	Report SingleReport `json:"report"`
    }
    
    type BulkReportResponse struct {
    	Reports []SingleReport `json:"reports"`
    }
    
    const (
    	path     = "/api/v1/reports"
    	bulkPath = "/api/v1/reports/bulk"
    )
    
    func GenerateHMAC(method string, path string, body []byte, clientId string, timestamp int64, userSecret string) string {
    	serverBodyHash := sha256.New()
    	serverBodyHash.Write(body)
    	serverBodyHashString := fmt.Sprintf("%s %s %s %s %d",
    		method,
    		path,
    		hex.EncodeToString(serverBodyHash.Sum(nil)),
    		clientId,
    		timestamp)
    	fmt.Println("Generating HMAC with the following: ", serverBodyHashString)
    	signedMessage := hmac.New(sha256.New, []byte(userSecret))
    	signedMessage.Write([]byte(serverBodyHashString))
    	userHmac := hex.EncodeToString(signedMessage.Sum(nil))
    	return userHmac
    }
    
    func GenerateAuthHeaders(method string, pathAndParams string, clientId string, userSecret string) http.Header {
    	header := http.Header{}
    	timestamp := time.Now().UTC().UnixMilli()
    	hmacString := GenerateHMAC(method, pathAndParams, []byte(""), clientId, timestamp, userSecret)
    
    	header.Add("Authorization", clientId)
    	header.Add("X-Authorization-Timestamp", strconv.FormatInt(timestamp, 10))
    	header.Add("X-Authorization-Signature-SHA256", hmacString)
    	return header
    }
    
    func FetchSingleReportSingleFeed(feedId string) (SingleReport, error) {
    	baseUrl := os.Getenv("BASE_URL") // Example: api.testnet-dataengine.chain.link
    	clientId := os.Getenv("CLIENT_ID") // Example: "00000000-0000-0000-0000-000000000000"
    	userSecret := os.Getenv("CLIENT_SECRET") // Example: "your-secret"
    	
    	timestamp := time.Now().UTC().UnixMilli() - 500
    
    	params := url.Values{
    		"feedID":    {feedId},
    		"timestamp": {fmt.Sprintf("%d", timestamp/1000)},
    	}
    
    	req := &http.Request{
    		Method: http.MethodGet,
    		URL: &url.URL{
    			Scheme:   "https",
    			Host:     baseUrl,
    			Path:     path,
    			RawQuery: params.Encode(),
    		},
    	}
    	req.Header = GenerateAuthHeaders(req.Method, req.URL.RequestURI(), clientId, userSecret)
    	fmt.Println("base: ", baseUrl)
    	fmt.Println("header: ", req.Header)
    	fmt.Println("params: ", params)
    
    	rawRes, err := http.DefaultClient.Do(req)
    	if err != nil {
    		return SingleReport{}, err
    	}
    	defer rawRes.Body.Close()
    
    	body, err := io.ReadAll(rawRes.Body)
    	if err != nil {
    		return SingleReport{}, err
    	}
    
    	if rawRes.StatusCode != http.StatusOK {
    		// Error messages are typically descriptive
    		return SingleReport{}, fmt.Errorf("unexpected status code %d: %v", rawRes.StatusCode, string(body))
    	}
    
    	var res SingleReportResponse
    	err = json.Unmarshal(body, &res)
    	if err != nil {
    		return SingleReport{}, err
    	}
    
    	return res.Report, nil
    }
    
    func FetchSingleReportManyFeeds(feedIds []string) ([]SingleReport, error) {
    	baseUrl := os.Getenv("BASE_URL") //Example: api.testnet-dataengine.chain.link
    	clientId := os.Getenv("CLIENT_ID") // Example: "00000000-0000-0000-0000-000000000000"
    	userSecret := os.Getenv("CLIENT_SECRET") // Example: "your-secret"
    
    	timestamp := time.Now().UTC().UnixMilli() - 500
    
    	params := url.Values{
    		"feedIDs":   {strings.Join(feedIds, ",")},
    		"timestamp": {fmt.Sprintf("%d", timestamp/1000)},
    	}
    
    	req := &http.Request{
    		Method: http.MethodGet,
    		URL: &url.URL{
    			Scheme:   "https",
    			Host:     baseUrl,
    			Path:     bulkPath,
    			RawQuery: params.Encode(),
    		},
    	}
    
    	req.Header = GenerateAuthHeaders(req.Method, req.URL.RequestURI(), clientId, userSecret)
    	fmt.Println("base: ", baseUrl)
    	fmt.Println("header: ", req.Header)
    	fmt.Println("params: ", params)
    
    	rawRes, err := http.DefaultClient.Do(req)
    	if err != nil {
    		return []SingleReport{}, err
    	}
    	defer rawRes.Body.Close()
    
    	body, err := io.ReadAll(rawRes.Body)
    	if err != nil {
    		return []SingleReport{}, err
    	}
    
    	if rawRes.StatusCode != http.StatusOK {
    		// Error messages are typically descriptive
    		return []SingleReport{}, fmt.Errorf("unexpected status code %d: %v", rawRes.StatusCode, string(body))
    	}
    
    	var res BulkReportResponse
    	err = json.Unmarshal(body, &res)
    	if err != nil {
    		return []SingleReport{}, err
    	}
    
    	return res.Reports, nil
    }
  7. Execute the command below to download dependencies and generate the go.sum file:

    go mod tidy
    

    Your project directory should now have the following structure:

    data-streams-direct/
    ā”œā”€ā”€ client/
    ā”‚   ā””ā”€ā”€ client.go
    ā”œā”€ā”€ go.mod
    ā”œā”€ā”€ go.sum
    ā”œā”€ā”€ internal/
    ā”‚   ā””ā”€ā”€ decoder.go
    

Set environment variables

Set the required environment variables in your terminal session to authenticate with the Data Streams Aggregation Network API:

export BASE_URL="api.testnet-dataengine.chain.link"
export CLIENT_ID="YOUR_CLIENT_ID"
export CLIENT_SECRET="YOUR_CLIENT_SECRET"
  • BASE_URL is the REST endpoint to poll for specific reports. See the Streams Direct Interface guide for more information.
  • Replace CLIENT_ID and CLIENT_SECRET with your API credentials.

Fetch and decode a report with a single feed

  1. Create main.go at the root of your project directory:

    touch main.go
    
  2. Open main.go and insert the following code to fetch and decode a single feed:

    // main.go for a single feed
    
    package main
    
    import (
    	"data-streams-direct/client"
    	"data-streams-direct/internal"
    	"fmt"
    	"log"
    	"os"
    )
    
    func main() {
        // Check if a feed ID has been provided as an argument
        if len(os.Args) < 2 {
            log.Fatalf("Usage: %s <feedId>", os.Args[0])
        }
    
        // Retrieve the feedId from the CL arguments
        feedId := os.Args[1]
    
        // Fetch the report for the specified feedId
        report, err := client.FetchSingleReportSingleFeed(feedId)
        if err != nil {
            log.Fatalf("Failed to fetch report: %v", err)
        }
    
        // Decode the full report data
        decodedReport, err := internal.DecodeFullReportAndReportData(report.FullReport)
        if err != nil {
            log.Fatalf("Failed to decode report: %v", err)
        }
    
        // Print details of the decoded report
        printReportDetails(decodedReport)
    
        // Print the full report in hex format as the payload
        printPayload(report.FullReport)
    }
    
    // Helper function to print the full report (payload) in hex format
    func printPayload(payload []byte) {
        fmt.Println("")
        fmt.Println("Payload for onchain verification")
        fmt.Println("=========================================")
        fmt.Printf("Payload (hexadecimal): 0x%x\n", payload)  // Adding '0x' prefix for hexadecimal representation
        fmt.Println("------------------------------------------------")
        fmt.Println()
    }
    
    
    // Helper function to print details of the decoded report
    func printReportDetails(report *internal.ReportWithContext) {
        fmt.Println("")
        fmt.Println("Report Details")
        fmt.Println("==============")
        fmt.Printf("Feed ID: %s\n", report.FeedId)
        fmt.Println()
    
        if report.V3Report != nil {
            fmt.Println("Decoded V3 Report Details:")
            fmt.Println("-------------------------")
            fmt.Printf("Valid From Timestamp: %d\n", report.V3Report.ValidFromTimestamp)
            fmt.Printf("Observations Timestamp: %d\n", report.V3Report.ObservationsTimestamp)
            fmt.Printf("Native Fee: %s\n", report.V3Report.NativeFee.String())
            fmt.Printf("Link Fee: %s\n", report.V3Report.LinkFee.String())
            fmt.Printf("Expires At: %d\n", report.V3Report.ExpiresAt)
            fmt.Printf("Benchmark Price: %s\n", report.V3Report.BenchmarkPrice.String())
            fmt.Printf("Bid: %s\n", report.V3Report.Bid.String())
            fmt.Printf("Ask: %s\n", report.V3Report.Ask.String())
            fmt.Println()
        }
    }
    
  3. For this example, you will read from the ETH/USD Data Streams feed on Arbitrum Sepolia. This feed ID is 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782. See the Data Streams Feed IDs page for a complete list of available assets.

    Execute your application:

    go run main.go 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
    

    Expect output similar to the following in your terminal:

    Generating HMAC with the following:  GET /api/v1/reports?feedID=0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782&timestamp=1713947821 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 <YOUR_CLIENT_ID> 1713947821602
    base:  api.testnet-dataengine.chain.link
    header:  map[Authorization:[<YOUR_CLIENT_ID>] X-Authorization-Signature-Sha256:[7ba6d89a7fd5af48a51e54859fe61bfe5205385ea59d7fc2ad1636e9ed1dba92] X-Authorization-Timestamp:[1713947821602]]
    params:  map[feedID:[0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782] timestamp:[1713947821]]
    
    Report Details
    ==============
    Feed ID: 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
    
    Decoded V3 Report Details:
    -------------------------
    Valid From Timestamp: 1713947821
    Observations Timestamp: 1713947821
    Native Fee: 30697637229100
    Link Fee: 6468225201489100
    Expires At: 1714034221
    Benchmark Price: 3257579704051546000000
    Bid: 3257546265080495200000
    Ask: 3257633285580761400000
    
    
    Payload for onchain verification
    =========================================
    Payload (hexadecimal): 0x0006f9b553e393ced311551efd30d1decedb63d76ad41737462e2cdbbdff15780000000000000000000000000000000000000000000000000000000020a8f908000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782000000000000000000000000000000000000000000000000000000006628c4ad000000000000000000000000000000000000000000000000000000006628c4ad00000000000000000000000000000000000000000000000000001beb59c3322c0000000000000000000000000000000000000000000000000016fad1097644cc00000000000000000000000000000000000000000000000000000000662a162d0000000000000000000000000000000000000000000000b097fff9701a850a800000000000000000000000000000000000000000000000b097892cdef361d7000000000000000000000000000000000000000000000000b098be558e09ed02c0000000000000000000000000000000000000000000000000000000000000000262810ea0ddf1d0883c6bab3cc10215ad7babd96fee6abd62b66f1cf8d8ef88c12dbae561312990e0a03945df9baf01d599354232d422772bb4fecc563baa96a500000000000000000000000000000000000000000000000000000000000000023a8e6710120b441f06d7475c2e207867f53cb4d0fb7387880109b3a2192b1b4027cce218afeeb5b2b2110a9bfac8e4a976f5e2c5e11e08afceafda9a8e13aa99
    ------------------------------------------------
    

Decoded report details

The decoded report details include:

AttributeValueDescription
Feed ID0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782The unique identifier for the Data Streams feed. In this example, the feed is ETH/USD.
Valid From Timestamp1713947821The start validity timestamp for the report, indicating when the data becomes relevant.
Observations Timestamp1713947821The timestamp indicating when the data was captured.
Native Fee30697637229100The fee to pay in the native blockchain token (e.g., testnet ETH on Arbitrum Sepolia) for the onchain verification of the report data. With 18 decimals. Note: This example fee is not indicative of actual fees.
Link Fee6468225201489100The fee to pay in LINK tokens for the onchain verification of the report data. With 18 decimals. For readability: 0.0064682252014891 LINK. Note: This example fee is not indicative of actual fees.
Expires At1714034221The expiration timestamp of the report, indicating the point at which the data becomes outdated.
Benchmark Price3257579704051546000000The observed price in the report, with 18 decimals. For readability: 3,257.579704051546 ETH per USD.
Bid3257546265080495200000The simulated price impact of a buy order up to the X% depth of liquidity usage. For readability: 3,257.5462650804952 ETH per USD.
Ask3257633285580761400000The simulated price impact of a sell order up to the X% depth of liquidity usage. For readability: 3,257.6332855807614 ETH per USD.

Payload for onchain verification

In this tutorial, you log and decode the FullReport payload to extract the report data. In a production environment, you should verify the data onchain to ensure its integrity and authenticity. Refer to the Verify report data onchain guide.

Fetch and decode a report with multiple feeds

  1. Open your main.go file at the root of your project directory.

  2. Replace the main.go file content with the following code to fetch and decode multiple feeds:

    package main
    
    import (
    	"data-streams-direct/client"
    	"data-streams-direct/internal"
    	"fmt"
    	"log"
    	"os"
    )
    
    func main() {
        // Ensure the correct number of arguments are provided
        if len(os.Args) < 2 {
            log.Fatalf("Usage: %s <feedID1> <feedID2> ...", os.Args[0])
        }
    
        // Retrieve the feed IDs from command line arguments
        feedIds := os.Args[1:]
    
        // Fetch reports for all provided feed IDs
        reports, err := client.FetchSingleReportManyFeeds(feedIds)
        if err != nil {
            log.Fatalf("Failed to fetch reports: %v", err)
        }
    
        // Slice to store decoded reports
        var decodedReports []*internal.ReportWithContext
    
        // Process each report fetched
        for index, report := range reports {
            // Decode the full report data
            decodedReport, err := internal.DecodeFullReportAndReportData(report.FullReport)
            if err != nil {
                log.Fatalf("Failed to decode report: %v", err)
            }
            decodedReports = append(decodedReports, decodedReport)
    
            // Print the full report in hex format as the payload
            printPayload(report.FullReport, feedIds[index])
        }
    
        // Print details of all decoded reports
        printReportDetails(decodedReports)
    }
    
    // Helper function to print the full report (payload) in hex format
    func printPayload(payload []byte, feedId string) {
        fmt.Println("")
        fmt.Printf("Payload for onchain verification for Feed ID %s\n", feedId)
        fmt.Println("=============================================================")
        fmt.Printf("Payload (hexadecimal): 0x%x\n", payload)
        fmt.Println("-------------------------------------------------------------")
        fmt.Println()
    }
    
    // Helper function to print details of the decoded reports
    func printReportDetails(reports []*internal.ReportWithContext) {
        fmt.Println("")
        fmt.Println("Report Details")
        fmt.Println("==============")
        for _, report := range reports {
            fmt.Printf("Feed ID: %s\n", report.FeedId)
            if report.V3Report != nil {
                fmt.Println("Decoded V3 Report Details:")
                fmt.Println("-------------------------")
                fmt.Printf("Valid From Timestamp: %d\n", report.V3Report.ValidFromTimestamp)
                fmt.Printf("Observations Timestamp: %d\n", report.V3Report.ObservationsTimestamp)
                fmt.Printf("Native Fee: %s\n", report.V3Report.NativeFee.String())
                fmt.Printf("Link Fee: %s\n", report.V3Report.LinkFee.String())
                fmt.Printf("Expires At: %d\n", report.V3Report.ExpiresAt)
                fmt.Printf("Benchmark Price: %s\n", report.V3Report.BenchmarkPrice.String())
                fmt.Printf("Bid: %s\n", report.V3Report.Bid.String())
                fmt.Printf("Ask: %s\n", report.V3Report.Ask.String())
                fmt.Println("------------------------------------------------")
            }
        }
    }
    
  3. Ensure the required environment variables are still defined in your terminal session.

    echo $BASE_URL
    echo $CLIENT_ID
    echo $CLIENT_SECRET
    

    Otherwise, set them again.

  4. For this example, you will read from the ETH/USD and LINK/USD Data Streams feeds on Arbitrum Sepolia. Run your application:

    go run main.go 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265
    

    Expect to see the output below in your terminal:

    Generating HMAC with the following:  GET /api/v1/reports/bulk?feedIDs=0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782%2C0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265&timestamp=1713947384 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 YOUR_CLIENT_ID 1713947385462
    base:  api.testnet-dataengine.chain.link
    header:  map[Authorization:[<YOUR_CLIENT_ID>] X-Authorization-Signature-Sha256:[833a935aa45887b412ad7be1a88e8845c79333f04a6c777d9bd9cc245892e36a] X-Authorization-Timestamp:[1713947385462]]
    params:  map[feedIDs:[0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782,0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265] timestamp:[1713947384]]
    
    Payload for onchain verification for Feed ID 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
    =============================================================
    Payload (hexadecimal): 0x0006f9b553e393ced311551efd30d1decedb63d76ad41737462e2cdbbdff15780000000000000000000000000000000000000000000000000000000020a8b312000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782000000000000000000000000000000000000000000000000000000006628c2f8000000000000000000000000000000000000000000000000000000006628c2f800000000000000000000000000000000000000000000000000001beedfc3915c0000000000000000000000000000000000000000000000000016ef912bff40e800000000000000000000000000000000000000000000000000000000662a14780000000000000000000000000000000000000000000000b081b97fcea69480000000000000000000000000000000000000000000000000b0805ec11fd48080000000000000000000000000000000000000000000000000b083d9972145f8bae000000000000000000000000000000000000000000000000000000000000000023e4f63204f352f3a0d39a734c65fc535838226e7963d7180154b97d47b7684202bba4519d029ccb2458dac09ff546d5ddf6387ab00011001698ae9368723e83c0000000000000000000000000000000000000000000000000000000000000002604ab9834eaf5fd3c0ea835aa70716a167f07faf55018310b1cf9bd90e3c2a881a3753e271c19dc9ef6481ef52a2df64dbb2992aa640926de3d8d42cb4866348
    -------------------------------------------------------------
    
    
    Payload for onchain verification for Feed ID 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265
    =============================================================
    Payload (hexadecimal): 0x00060a2676459d14176b64106fcf3246631d3a03734171737eb082fe79c956e000000000000000000000000000000000000000000000000000000000254fb30f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265000000000000000000000000000000000000000000000000000000006628c2f8000000000000000000000000000000000000000000000000000000006628c2f800000000000000000000000000000000000000000000000000001beedea3b4640000000000000000000000000000000000000000000000000016ef462a08af7c00000000000000000000000000000000000000000000000000000000662a1478000000000000000000000000000000000000000000000000d6f99cfc1ffe9000000000000000000000000000000000000000000000000000d6f63cf703753000000000000000000000000000000000000000000000000000d6fcfd013c87f00000000000000000000000000000000000000000000000000000000000000000029e96ae3827e09874b89a13b19b1bc0fa8797b233ad25b45bd7866899c55ad463b3c3d3cfbb51c9fcbd42c86e4b0fc808c829753931c8586919257dbd87afdd5500000000000000000000000000000000000000000000000000000000000000026eab928bf633bd0edb6a517f275363c52f25a073f371e1288a290cbd2a9f51693528559572c2974a80462672c35e5e9ac44409c2e883da6bf19fe32e56166900
    -------------------------------------------------------------
    
    
    Report Details
    ==============
    Feed ID: 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
    Decoded V3 Report Details:
    -------------------------
    Valid From Timestamp: 1713947384
    Observations Timestamp: 1713947384
    Native Fee: 30712770302300
    Link Fee: 6455856275079400
    Expires At: 1714033784
    Benchmark Price: 3255974600000000000000
    Bid: 3255877000000000000000
    Ask: 3256127748030959500000
    ------------------------------------------------
    Feed ID: 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265
    Decoded V3 Report Details:
    -------------------------
    Valid From Timestamp: 1713947384
    Observations Timestamp: 1713947384
    Native Fee: 30712751436900
    Link Fee: 6455534119595900
    Expires At: 1714033784
    Benchmark Price: 15490585000000000000
    Bid: 15489635000000000000
    Ask: 15491535000000000000
    ------------------------------------------------
    

    Your application has successfully fetched and decoded data for both feeds.

Payload for onchain verification

In this tutorial, you log and decode the FullReport payloads to extract the report data. In a production environment, you should verify the data onchain to ensure its integrity and authenticity. Refer to the Verify report data onchain guide.

Explanation

Fetching a report

The application fetches a report from the Data Streams Aggregation Network using either the FetchSingleReportSingleFeed function for a single feed or the FetchSingleReportMultipleFeeds function for multiple feeds, found in the client package. This step involves sending an API request and receiving a response with the report data in either a SingleReport struct (single feed) or a BulkReportResponse struct (multiple feeds).

The response includes a FullReport blob containing the encoded report's context and observations.

Decoding a report

When the application successfully fetches the report, it decodes the report with the DecodeFullReportAndReportData function from the internal package. The process includes:

  1. FullReport blob decoding: It first decodes the FullReport blob into a FullReport struct, extracting the report context (including the feed ID(s) and the report version) and the report data. In this example, the feed uses the V3 report version.

  2. Report data decoding: Based on the feed ID(s) version(s), it transforms the raw data into a structured format. In this example, the application decodes the report data into a V3Report struct, which contains the report's attributes.

Handling the decoded data

The application logs the structured report data to the terminal. However, this data can be used for further processing, analysis, or display in your own application.

What's next

Stay updated on the latest Chainlink news