Documentation

Report schema

Every DataLink report carries report data: nine fields, ABI-encoded in a fixed order. This page describes those fields, how they're encoded, and how to decode them.

The nine fields

#FieldABI typeMeaning
1feedIdbytes32The feed this report belongs to
2observationsTimestampuint32When the values were observed (Unix seconds)
3benchmarkPriceint192The main price, as an integer — see Decimals
4bidint192Bid price, same decimals
5askint192Ask price, same decimals
6currentBlockNumuint64XDC block number recorded with the observation
7currentBlockHashbytes32Hash of that block
8validFromBlockNumuint64First block number this report covers
9currentBlockTimestampuint64Timestamp of that block (Unix seconds)

The order is fixed: decoding with a different order or different types produces wrong values without any error.

For sources without an order book (for example a median sale price), bid and ask carry the same value as benchmarkPrice.

Decimals

Prices are integers. Divide by 10^decimals to get the real value. The number of decimals is set per feed by its report schema:

  • the decoded API returns it in schema.fields[].decimals;
  • the Consumer portal shows it on the feed's Feed Access & Integration page.

With 8 decimals, 8157000000 means 81.57.

Example: decoded report

GET /api/v1/reports/decoded?feedID=… returns the report's fields as JSON. A real report (feed "DataLink SOL Test Provider", 8 decimals):

JSON

{
  "createdAt": 1794519775,
  "decoded": {
    "feedId": "0001d1fb86f34da1314b728dfd840d31cf3a25bae5a53bcd5251ce7e0e909918",
    "observationsTimestamp": 1794519770,
    "benchmarkPrice": "8157000000",
    "bid": "8157000000",
    "ask": "8158000000",
    "currentBlockNum": 109335603,
    "currentBlockHash": "9534790fceb7a7338d052818573dbff63546c0bd98969d709d1907cf1a5ae2e4",
    "validFromBlockNum": 109335574,
    "currentBlockTimestamp": 1794519768
  }
}
FieldRawValue (8 decimals)
benchmarkPrice815700000081.57
bid815700000081.57
ask815800000081.58
observationsTimestamp1794519770Unix seconds

The decoded endpoint also returns a schema block with each field's label, type, decimals and the decoded key that carries it.

createdAt is not part of the report

createdAt is API metadata: when the report was stored by the DataLink service. It is not encoded in the report data and is not covered by the signatures. The time the values were observed is observationsTimestamp — use that for freshness checks. (In the example, the values were observed at 1794519770 and the report stored five seconds later.)

The signed payload

The payload from GET /api/v1/reports/latest wraps the report data together with the signatures:

Solidity

(bytes32[3] reportContext, bytes reportData, bytes32[] rs, bytes32[] ss, bytes32 rawVs)
PartMeaning
reportContextreportContext[0] is the configuration digest; the VerifierProxy uses it to find the right Verifier
reportDataThe nine fields above (288 bytes: nine 32-byte words)
rs, ss, rawVsThe oracle signatures

You don't decode the payload yourself to verify it: pass it unchanged to the VerifierProxy, which returns reportData once the signatures are valid.

Decoding with abi.decode

ABI encoding stores each of these nine fields in its own 32-byte word, in order. abi.decode reads them back when you give it the same types in the same order.

In Solidity, after verification:

Solidity

struct Report {
    bytes32 feedId;
    uint32 observationsTimestamp;
    int192 benchmarkPrice;
    int192 bid;
    int192 ask;
    uint64 currentBlockNum;
    bytes32 currentBlockHash;
    uint64 validFromBlockNum;
    uint64 currentBlockTimestamp;
}

bytes memory reportData = verifierProxy.verify(payload, "");
Report memory report = abi.decode(reportData, (Report));

In JavaScript (with viem), straight from a signed payload — useful for inspecting a report off-chain (this does not check the signatures):

JavaScript

import { decodeAbiParameters } from "viem";

const [, reportData] = decodeAbiParameters(
  [{ type: "bytes32[3]" }, { type: "bytes" }, { type: "bytes32[]" }, { type: "bytes32[]" }, { type: "bytes32" }],
  payload // "0x…" from /api/v1/reports/latest
);

const [feedId, observationsTimestamp, benchmarkPrice, bid, ask, currentBlockNum, currentBlockHash, validFromBlockNum, currentBlockTimestamp] =
  decodeAbiParameters(
    [
      { name: "feedId", type: "bytes32" },
      { name: "observationsTimestamp", type: "uint32" },
      { name: "benchmarkPrice", type: "int192" },
      { name: "bid", type: "int192" },
      { name: "ask", type: "int192" },
      { name: "currentBlockNum", type: "uint64" },
      { name: "currentBlockHash", type: "bytes32" },
      { name: "validFromBlockNum", type: "uint64" },
      { name: "currentBlockTimestamp", type: "uint64" },
    ],
    reportData
  );
// benchmarkPrice is a BigInt, e.g. 8157000000n → 81.57 with 8 decimals