Fetch and decode reports using the SDK (Crypto Streams)

In this guide, you'll learn how to use Chainlink Data Streams with the Streams Direct implementation and the Data Streams SDK for Go to fetch and decode V3 reports for Crypto streams from the Data Streams Aggregation Network. You'll set up your Go project, retrieve reports, decode them, and log their attributes.

Requirements

  • Git: Make sure you have Git installed. You can check your current version by running git --version in your terminal and download the latest version from the official Git website if necessary.
  • 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 request mainnet or testnet early access.

Guide

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

Set up your Go project

  1. Create a new directory for your project and navigate to it:

    mkdir my-data-streams-project
    cd my-data-streams-project
    
  2. Initialize a new Go module:

    go mod init my-data-streams-project
    
  3. Install the Data Streams SDK:

    go get github.com/smartcontractkit/data-streams-sdk/go
    

Fetch and decode a report with a single stream

  1. Create a new Go file, single-stream.go, in your project directory:

    touch single-stream.go
    
  2. Insert the following code example and save your single-stream.go file:

     package main
    
     import (
         "context"
         "fmt"
         "os"
         "time"
    
         streams "github.com/smartcontractkit/data-streams-sdk/go"
         feed "github.com/smartcontractkit/data-streams-sdk/go/feed"
         report "github.com/smartcontractkit/data-streams-sdk/go/report"
         v3 "github.com/smartcontractkit/data-streams-sdk/go/report/v3" // Import the v3 report schema for Crypto streams. For RWA streams, use the v4 schema.
     )
    
     func main() {
         if len(os.Args) < 2 {
             fmt.Printf("Usage: go run main.go [FeedID]\n")
             os.Exit(1)
         }
         feedIDInput := os.Args[1]
    
         // Define the configuration for the SDK client.
         cfg := streams.Config{
             ApiKey:    os.Getenv("API_KEY"),
             ApiSecret: os.Getenv("API_SECRET"),
             RestURL: "https://api.testnet-dataengine.chain.link",
             Logger: streams.LogPrintf,
         }
    
         // Initialize the SDK client.
         client, err := streams.New(cfg)
         if err != nil {
             cfg.Logger("Failed to create client: %v\n", err)
             os.Exit(1)
         }
    
         // Create context for timeout.
         ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
         defer cancel()
    
         // Define the feedID from user input.
         var FeedID feed.ID
         if err := FeedID.FromString(feedIDInput); err != nil {
             cfg.Logger("Failed to parse stream ID: %v\n", err)
             os.Exit(1)
         }
    
         // Fetch the latest report for the given FeedID.
         reportResponse, err := client.GetLatestReport(ctx, FeedID)
         if err != nil {
             cfg.Logger("Failed to get latest report: %v\n", err)
             os.Exit(1)
         }
    
         // Log the contents of the report before decoding
         cfg.Logger("Raw report data: %+v\n", reportResponse)
    
         // Decode the report.
         decodedReport, err := report.Decode[v3.Data](reportResponse.FullReport)
         if err != nil {
             cfg.Logger("Failed to decode report: %v\n", err)
             os.Exit(1)
         }
    
         // Log the decoded report
         cfg.Logger("\nDecoded Report for Stream ID %s:\n------------------------------------------\n"+
             "Observations Timestamp: %d\n"+
             "Benchmark Price       : %s\n"+
             "Bid                   : %s\n"+
             "Ask                   : %s\n"+
             "Valid From Timestamp  : %d\n"+
             "Expires At            : %d\n"+
             "Link Fee              : %s\n"+
             "Native Fee            : %s\n"+
             "------------------------------------------\n",
             feedIDInput,
             decodedReport.Data.ObservationsTimestamp,
             decodedReport.Data.BenchmarkPrice.String(),
             decodedReport.Data.Bid.String(),
             decodedReport.Data.Ask.String(),
             decodedReport.Data.ValidFromTimestamp,
             decodedReport.Data.ExpiresAt,
             decodedReport.Data.LinkFee.String(),
             decodedReport.Data.NativeFee.String(),
         )
     }
    
  3. Download the required dependencies and update the go.mod and go.sum files:

    go mod tidy
    
  4. Set up the SDK client configuration within single-stream.go with your API credentials and the REST endpoint:

    cfg := streams.Config{
        ApiKey:    os.Getenv("API_KEY"),
        ApiSecret: os.Getenv("API_SECRET"),
        RestURL:   "https://api.testnet-dataengine.chain.link",
        Logger: streams.LogPrintf,
    }
    
    • Set your API credentials as environment variables:

      export API_KEY="<YOUR_API_KEY>"
      export API_SECRET="<YOUR_API_SECRET>"
      

      Replace <YOUR_API_KEY> and <YOUR_API_SECRET> with your API credentials.

    • RestURL is the REST endpoint to poll for specific reports. See the Streams Direct Interface page for more information.

    See the SDK Reference page for more configuration options.

  5. For this example, you will read from the ETH/USD crypto stream. This stream ID is 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782. See the Streams Addresses page for a complete list of available crypto assets.

    Execute your application:

    go run single-stream.go 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782
    

    Expect output similar to the following in your terminal:

    Raw report data: {"fullReport":"0x0006f9b553e393ced311551efd30d1decedb63d76ad41737462e2cdbbdff157800000000000000000000000000000000000000000000000000000000351f200b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba7820000000000000000000000000000000000000000000000000000000066aa78ab0000000000000000000000000000000000000000000000000000000066aa78ab00000000000000000000000000000000000000000000000000001b6732178a04000000000000000000000000000000000000000000000000001b1e8f8f0dc6880000000000000000000000000000000000000000000000000000000066abca2b0000000000000000000000000000000000000000000000b3eba5491849628aa00000000000000000000000000000000000000000000000b3eaf356fc42b6f6c00000000000000000000000000000000000000000000000b3ecd20810b9d1c000000000000000000000000000000000000000000000000000000000000000000203f40222dca8f8531b2a9db61f105630118383062c0efdb04e5b42c5dd9db79b3e845b0b930cc8a11c94b0cc81917863687ff846cd7f841f70c284cf5e9d089b000000000000000000000000000000000000000000000000000000000000000266cdaf55fce51c53781597978e732de7f81991de4aaf703486fd65e48daf625477abe65ad40ae4a408b78edb9de621094a86517911d9fa188b95fc622ed727d1","feedID":"0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782","validFromTimestamp":1722448043,"observationsTimestamp":1722448043}
    
    Decoded Report for Stream ID 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782:
    ------------------------------------------
    Observations Timestamp: 1722448043
    Benchmark Price       : 3318947247532739300000
    Bid                   : 3318897160259676600000
    Ask                   : 3319031900000000000000
    Valid From Timestamp  : 1722448043
    Expires At            : 1722534443
    Link Fee              : 7633426300389000
    Native Fee            : 30130035984900
    ------------------------------------------
    

Decoded report details

The decoded report details include:

AttributeValueDescription
Stream ID0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782The unique identifier for the stream. In this example, the stream is for ETH/USD.
Observations Timestamp1722448043The timestamp indicating when the data was captured.
Benchmark Price3318947247532739300000The observed price in the report, with 18 decimals. For readability: 3,318.9472475327393 USD per ETH.
Bid3318897160259676600000The highest price a buyer is willing to pay for an asset, with 18 decimals. For readability: 3,318.8971602596766 USD per ETH. Learn more about the Bid price.
Ask3319031900000000000000The lowest price a seller is willing to accept for an asset, with 18 decimals. For readability: 3,319.0319000000000 USD per ETH. Learn more about the Ask price.
Valid From Timestamp1722448043The start validity timestamp for the report, indicating when the data becomes relevant.
Expires At1722534443The expiration timestamp of the report, indicating the point at which the data becomes outdated.
Link Fee7633426300389000The fee to pay in LINK tokens for the onchain verification of the report data. With 18 decimals. For readability: 0.007633426300389 LINK.
Native Fee30130035984900The fee to pay in the native blockchain token (e.g., ETH on Ethereum) for the onchain verification of the report data. With 18 decimals. Note: This example fee is not indicative of actual fees.

Payload for onchain verification

In this guide, 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 reports for multiple streams

  1. Create a new Go file, multiple-streams.go, in your project directory:

    touch multiple-streams.go
    
  2. Insert the following code example in your multiple-streams.go file:

    package main
    
    import (
        "context"
        "fmt"
        "os"
        "time"
    
        streams "github.com/smartcontractkit/data-streams-sdk/go"
        feed "github.com/smartcontractkit/data-streams-sdk/go/feed"
        report "github.com/smartcontractkit/data-streams-sdk/go/report"
        v3 "github.com/smartcontractkit/data-streams-sdk/go/report/v3" // Import the v3 report schema for Crypto streams. For RWA streams, use the v4 schema.
    )
    
    func main() {
        if len(os.Args) < 3 { // Ensure there are at least two Stream IDs provided
            fmt.Printf("Usage: go run multiple-streams.go [StreamID1] [StreamID2] ...\n")
            os.Exit(1)
        }
    
        // Define the configuration for the SDK client
        cfg := streams.Config{
            ApiKey: "YOUR_API_KEY",
            ApiSecret: "YOUR_API_SECRET",
            RestURL: "https://api.testnet-dataengine.chain.link",
            Logger: streams.LogPrintf,
        }
    
        // Initialize the SDK client
        client, err := streams.New(cfg)
        if err != nil {
            cfg.Logger("Failed to create client: %v\n", err)
            os.Exit(1)
        }
    
        // Create context for timeout
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        defer cancel()
    
        // Parse Feed IDs from command line arguments
        var ids []feed.ID
        for _, arg := range os.Args[1:] {
            var fid feed.ID
            if err := fid.FromString(arg); err != nil {
                cfg.Logger("Failed to parse stream ID %s: %v\n", arg, err)
                os.Exit(1)
            }
            ids = append(ids, fid)
        }
    
        // Fetch reports for the given stream IDs
        reportResponses, err := client.GetReports(ctx, ids, uint64(time.Now().Unix())) // Using current time as an example timestamp
        if err != nil {
            cfg.Logger("Failed to get reports: %v\n", err)
            os.Exit(1)
        }
    
        for _, reportResponse := range reportResponses {
            // Log the contents of the report before decoding
            cfg.Logger("Raw report data for Stream ID %s: %+v\n", reportResponse.FeedID.String(), reportResponse)
    
            // Decode the report
            decodedReport, err := report.Decode[v3.Data](reportResponse.FullReport)
            if err != nil {
                cfg.Logger("Failed to decode report for Stream ID %s: %v\n", reportResponse.FeedID, err)
                continue // Skip to next report if decoding fails
            }
    
            // Log the decoded report
            cfg.Logger("\nDecoded Report for Stream ID %s:\n------------------------------------------\n"+
                "Observations Timestamp: %d\n"+
                "Benchmark Price       : %s\n"+
                "Bid                   : %s\n"+
                "Ask                   : %s\n"+
                "Valid From Timestamp  : %d\n"+
                "Expires At            : %d\n"+
                "Link Fee              : %s\n"+
                "Native Fee            : %s\n"+
                "------------------------------------------\n",
                reportResponse.FeedID.String(),
                decodedReport.Data.ObservationsTimestamp,
                decodedReport.Data.BenchmarkPrice.String(),
                decodedReport.Data.Bid.String(),
                decodedReport.Data.Ask.String(),
                decodedReport.Data.ValidFromTimestamp,
                decodedReport.Data.ExpiresAt,
                decodedReport.Data.LinkFee.String(),
                decodedReport.Data.NativeFee.String(),
            )
        }
    }
    
  3. Replace ApiKey and ApiSecret with your API credentials in the SDK client configuration:

    cfg := streams.Config{
        ApiKey:    "YOUR_API_KEY",
        ApiSecret: "YOUR_API_SECRET",
        RestURL:   "https://api.testnet-dataengine.chain.link",
        Logger: streams.LogPrintf,
    }
    
  4. For this example, you will read from the ETH/USD and LINK/USD Data Streams crypto streams. Run your application:

    go run multiple-streams.go 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265
    

    Expect to see the output below in your terminal:

    Raw report data for Stream ID 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782: {"fullReport":"0x0006f9b553e393ced311551efd30d1decedb63d76ad41737462e2cdbbdff157800000000000000000000000000000000000000000000000000000000351fae0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000220000000000000000000000000000000000000000000000000000000000000028001010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000120000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba7820000000000000000000000000000000000000000000000000000000066aa7c460000000000000000000000000000000000000000000000000000000066aa7c4600000000000000000000000000000000000000000000000000001b792816df70000000000000000000000000000000000000000000000000001b3ce720f89cb00000000000000000000000000000000000000000000000000000000066abcdc60000000000000000000000000000000000000000000000b37605534e2f2b24600000000000000000000000000000000000000000000000b3757e1b6805f45da00000000000000000000000000000000000000000000000b3768c8b345860648000000000000000000000000000000000000000000000000000000000000000021bf16c540e0eed976ae7cbfb1bc3d81e1f9bdba6ea2491c93b4abe16c7139325ab8a49f61ebe2231210dbcdd3cd4b42f303635fff1f2e325d21de9eec7875a9200000000000000000000000000000000000000000000000000000000000000026786234d10e05d5d42e3aca8fe96becd84cf8f8c46e658d578f09e3aa5165b394e13a87a7f48aeed6e13d9a7de90ef764a961494a6192c935fde0cad9c2fa660","feedID":"0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782","validFromTimestamp":1722448966,"observationsTimestamp":1722448966}
    
    Decoded Report for Stream ID 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782:
    ------------------------------------------
    Observations Timestamp: 1722448966
    Benchmark Price       : 3310471484260632700000
    Bid                   : 3310433423677103300000
    Ask                   : 3310509544844162000000
    Valid From Timestamp  : 1722448966
    Expires At            : 1722535366
    Link Fee              : 7666787759463600
    Native Fee            : 30207177580400
    ------------------------------------------
    
    Raw report data for Stream ID 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265: {"fullReport":"0x00060a2676459d14176b64106fcf3246631d3a03734171737eb082fe79c956e00000000000000000000000000000000000000000000000000000000039c6a415000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000022000000000000000000000000000000000000000000000000000000000000002800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012000036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d02650000000000000000000000000000000000000000000000000000000066aa7c460000000000000000000000000000000000000000000000000000000066aa7c4600000000000000000000000000000000000000000000000000001b791a5f6bcc000000000000000000000000000000000000000000000000001b3a3d53e3d5600000000000000000000000000000000000000000000000000000000066abcdc6000000000000000000000000000000000000000000000000b514b7b30a5c0000000000000000000000000000000000000000000000000000b506137dd1137388000000000000000000000000000000000000000000000000b51d9c2a243b700000000000000000000000000000000000000000000000000000000000000000028ebed5590f7967d24acbdc3efbbd0089f2b58a4664312f7a824556c2fa72a94f1c2acf3d5932cfbfa09671c23c652bbb787ba85bb9e64c68b70d0d542772912e000000000000000000000000000000000000000000000000000000000000000262e5902da042903df35a5f726f468e123883c6622f216fb0f313fc84c2a0de4c755f7eb4a4a57cd91a23b1ac76bfb39b533e3cbc3b39b90b239c792906639ed8","feedID":"0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265","validFromTimestamp":1722448966,"observationsTimestamp":1722448966}
    
    Decoded Report for Stream ID 0x00036fe43f87884450b4c7e093cd5ed99cac6640d8c2000e6afc02c8838d0265:
    ------------------------------------------
    Observations Timestamp: 1722448966
    Benchmark Price       : 13048256000000000000
    Bid                   : 13044134801824773000
    Ask                   : 13050759000000000000
    Valid From Timestamp  : 1722448966
    Expires At            : 1722535366
    Link Fee              : 7663859446044000
    Native Fee            : 30206947453900
    ------------------------------------------
    

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

Payload for onchain verification

In this guide, 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 reports

Your application uses the Data Streams SDK to fetch reports from the Data Streams Aggregation Network. Different functions from the SDK are invoked based on whether a single stream or multiple streams are queried:

  • Single Stream: The application calls the GetLatestReport function from the SDK's streams package to fetch a single stream report. This function sends an API request to retrieve the latest available report for a specified stream ID. The response includes the report's data in a structured format suitable for further processing and analysis.

  • Multiple Streams: The GetReports function is used for multiple streams. This function allows you to fetch reports for multiple stream IDs simultaneously, specifying a timestamp to retrieve reports valid at a particular point in time.

The responses include a fullReport blob containing the encoded report's context and observations.

Decoding reports

After retrieving the report data, the application decodes it using the Decode function available in the report package of the SDK. This function is designed to handle different data formats by specifying the version of the data structure as a type parameter (e.g., v3.Data for crypto streams).

The Decode function unpacks the encoded report blob into a usable data structure (Report[v3.Data] for crypto streams). It extracts information such as observation timestamps, benchmark prices, bid and ask prices from the report data.

Handling the decoded data

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

What's next

Get the latest Chainlink content straight to your inbox.