# Access Data Streams Using Automation
Source: https://docs.chain.link/chainlink-automation/guides/streams-lookup

> For the complete documentation index, see [llms.txt](/llms.txt).

> **NOTE: Talk to an expert**
>
> [Contact us](https://chain.link/contact?ref_id=datastreams) to talk to an expert about integrating Chainlink Data
> Streams with your applications.This guide shows you how to read data from a Data Streams stream, verify the answer onchain, and store it.This example uses the *[Streams Trade](/data-streams/streams-trade) implementation*, with a [Chainlink Automation Log Trigger](/chainlink-automation/guides/log-trigger) to check for events that require data. For this example, the log trigger comes from a simple emitter contract. Chainlink Automation then uses `StreamsLookup` to retrieve a signed report from the Data Streams Aggregation Network, return the data in a callback, and run the [`performUpkeep` function](/chainlink-automation/reference/automation-interfaces#performupkeep-function-for-log-triggers) on your registered upkeep contract. The `performUpkeep` function calls the `verify` function on the verifier contract.Note: To learn how to use Data Streams with the REST API or WebSocket, see the [Fetch and decode reports via a REST API](/data-streams/tutorials/go-sdk-fetch) guide or the [Stream and decode reports via WebSocket](/data-streams/tutorials/go-sdk-stream) guide.> **CAUTION: Disclaimer**
>
> This guide represents an example of using a Chainlink product or service and is provided to help you understand how to
> interact with Chainlink's systems and services so that you can integrate them into your own. This template is provided
> "AS IS" and "AS AVAILABLE" without warranties of any kind, has not been audited, and may be missing key checks or
> error handling to make the usage of the product more clear. Do not use the code in this example in a production
> environment without completing your own audits and application of best practices. Neither Chainlink Labs, the
> Chainlink Foundation, nor Chainlink node operators are responsible for unintended outputs that are generated due to
> errors in code.## Before you begin* [Contact
  us](https://chainlinkcommunity.typeform.com/datastreams?typeform-source=docs.chain.link#ref_id=docs) to request
  mainnet or testnet access.
* If you are new to smart contract development, learn how to [Deploy Your First Smart Contract](/quickstarts/deploy-your-first-contract) so you are familiar with the tools that are necessary for this guide:
  - The [Solidity](https://soliditylang.org/) programming language
  - The [MetaMask](https://metamask.io) wallet
  - The [Remix](https://remix.ethereum.org/) development environment
* This guide requires testnet ETH and LINK on *Arbitrum Sepolia*. Both are available at [faucets.chain.link](https://faucets.chain.link/arbitrum-sepolia).
* Learn how to [Fund your contract with LINK](/resources/fund-your-contract).## Tutorial### Deploy the Chainlink Automation upkeep contractDeploy an upkeep contract that is enabled to retrieve data from Data Streams. For this example, you will read from the ETH/USD stream on Arbitrum Sepolia. This stream ID is 0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782. See the [Data Streams Crypto streams](/data-streams/crypto-streams) page for a complete list of available crypto assets.1) [Open the StreamsUpkeep.sol](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/StreamsUpkeep.sol) contract in Remix.

   [Open StreamsUpkeep.sol in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/StreamsUpkeep.sol)

2) Select the `StreamsUpkeep.sol` contract in the **Solidity Compiler** tab.

   ![Chainlink Data Streams Solidity Compiler](/images/data-streams/getting-started/solidity-compiler.webp)

3) Compile the contract. You can ignore the warning messages for this example.

4) Open MetaMask and set the network to *Arbitrum Sepolia*. If you need to add Arbitrum Sepolia to your wallet, you can find the chain ID and the LINK token contract address on the [LINK Token Contracts](/resources/link-token-contracts#arbitrum-sepolia-testnet) page.
   -

5) On the **Deploy & Run Transactions** tab in Remix, select *Injected Provider - MetaMask* in the **Environment** list. Remix will use the MetaMask wallet to communicate with *Arbitrum Sepolia*.

   ![Chainlink Data Streams Injected Provider MetaMask](/images/data-streams/getting-started/injected-provider.webp)

6) In the **Contract** section, select the `StreamsUpkeep` contract and fill in the Arbitrum Sepolia **verifier proxy address**: 0x2ff010DEbC1297f19579B4246cad07bd24F2488A. You can find the verifier proxy addresses on the [Stream Addresses](/data-streams/crypto-streams) page.

   ![Chainlink Data Streams Remix Deploy Upkeep Contract](/images/data-streams/getting-started/deploy-upkeep.webp)

7) Click the **Deploy** button to deploy the contract. MetaMask prompts you to confirm the transaction. Check the transaction details to ensure you deploy the contract to *Arbitrum Sepolia*.

8) After you confirm the transaction, the contract address appears under the **Deployed Contracts** list in Remix. Save this contract address for later.

   ![Chainlink Data Streams Remix Deployed Upkeep Contract](/images/data-streams/getting-started/deployed-upkeep-1.webp)### Deploy the emitter contractThis contract emits logs that trigger the upkeep. This code can be part of your dApp. For example, you might emit log triggers when your users initiate a trade or other action requiring data retrieval. For this Getting Started guide, use a very simple emitter so you can test the upkeep and data retrieval.1) [Open the LogEmitter.sol](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/LogEmitter.sol) contract in Remix.

   [Open LogEmitter.sol in Remix](https://remix.ethereum.org/#url=https://docs.chain.link/samples/DataStreams/LogEmitter.sol)

2) Under the **Solidity Compiler** tab, select the `0.8.19` Solidity compiler and click the **Compile LogEmitter.sol** button to compile the contract.

   ![Chainlink Data Streams Remix Compile Log Emitter Contract](/images/data-streams/getting-started/compile-logemitter.webp)

3) Open MetaMask and make sure the network is still set to *Arbitrum Sepolia*.

4) On the **Deploy & Run Transactions** tab in Remix, ensure the **Environment** is still set to *Injected Provider - MetaMask*.

   ![Chainlink Data Streams Injected Provider MetaMask](/images/data-streams/getting-started/injected-provider.webp)

5) Click the **Deploy** button to deploy the contract. MetaMask prompts you to confirm the transaction. Check the transaction details to ensure you deploy the contract to *Arbitrum Sepolia*.

   ![Chainlink Data Streams Deploy Emitter Contract](/images/data-streams/getting-started/deploy-logemitter.webp)

6) After you confirm the transaction, the contract address appears in the **Deployed Contracts** list. Save this contract address for later.

   ![Chainlink Data Streams Deployed Emitter Contract](/images/data-streams/getting-started/deployed-logemitter.webp)### Register the upkeepRegister a new **Log trigger** upkeep. See [Automation Log Triggers](/chainlink-automation/guides/log-trigger) to learn more about how to register Log Trigger upkeeps.1) Go to the [Chainlink Automation UI](https://automation.chain.link/arbitrum-sepolia) for *Arbitrum Sepolia* and connect your browser wallet.

2) Click **Register new Upkeep**.

3) Select the **Log trigger** upkeep type and click **Next**.

4) Specify the upkeep contract address you saved earlier as the **Contract to automate**. In this example, you can ignore the warning about the Automation compatible contract verification. Click **Next**.

5) Specify the emitter contract address that you saved earlier. This tells Chainlink Automation what contracts to watch for log triggers. Then click **Next**.

6) Provide the ABI if the contract is not validated. To find the ABI of your contract in Remix, navigate to the **Solidity Compiler** tab. Then, copy the ABI to your clipboard using the button at the bottom of the panel.

   ![Chainlink Data Streams Remix Log Emitter ABI](/images/data-streams/getting-started/remix-copy-abi.webp)

7) Select the `Log` event as the triggering event in the **Emitted log** dropdown. **Log index topic filters** are optional filters to narrow the logs you want to trigger your upkeep. For this example, leave the field blank. Click **Next**.

8) Specify a name for the upkeep.

9) Specify a **Starting balance** of 1 testnet LINK for this example. You can retrieve unused LINK later.

10) Leave the **Check data** value and other fields blank for now, and click **Register Upkeep**. MetaMask prompts you to confirm the transaction. Wait for the transaction to complete.### Fund the upkeep contractIn this example, the upkeep contract pays for onchain verification of reports from Data Streams. The Automation subscription does not cover the cost.Open MetaMask and send 1 testnet LINK on *Arbitrum Sepolia* to the upkeep contract address you saved earlier.![Chainlink Data Streams Fund Deployed Upkeep](/images/data-streams/getting-started/fund-deployed-upkeep.webp)### Emit a logYou can use your emitter contract to emit a log and initiate the upkeep, which retrieves data for the specified stream ID.1) In Remix, on the **Deploy & Run Transactions** tab, expand your emitter contract under the **Deployed Contracts** section.

   ![Chainlink Data Streams Emit Log](/images/data-streams/getting-started/emitter-emitlog.webp)

2) Click the `emitLog` button to call the function and emit a log. MetaMask prompts you to accept the transaction.After the transaction is complete, the log is emitted, and the upkeep is triggered. You can find the upkeep transaction hash in the [Chainlink Automation UI](https://automation.chain.link/arbitrum-sepolia). Check to make sure the transaction is successful.![Image](/images/data-streams/getting-started/request-fulfilled.webp)### View the retrieved priceThe retrieved price is stored in the `lastDecodedPrice` storage variable.1) On the **Deploy & Run Transactions** tab in Remix, expand the details of your upkeep contract in the **Deployed Contracts** section.

2) Click the `lastDecodedPrice` getter function to view the retrieved price. The answer on the ETH/USD stream uses 18 decimal places, so an answer of `248412100000000000` indicates an ETH/USD price of 2,484.121. Some streams may use a different number of decimal places for answers. See the [Data Streams Crypto streams](/data-streams/crypto-streams) page for more information.

   ![Chainlink Data Streams Deployed Upkeep](/images/data-streams/getting-started/deployed-upkeep-2.webp)## Examine the codeThe example code you deployed has all the interfaces and functions required to work with Chainlink Automation as an upkeep contract. It follows a similar flow to the trading flow in the [Architecture](/data-streams/architecture#example-trading-flow-using-streams-trade) documentation but uses a basic log emitter to simulate the client contract that would initiate a `StreamsLookup`. After the contract receives and verifies the report, `performUpkeep` stores the price from the report in the `lastDecodedPrice` variable. You could modify this to use the data in a way that works for your specific use case and application.The code example uses `revert` with `StreamsLookup` to convey call information about what streams to retrieve. See the [EIP-3668 rationale](https://eips.ethereum.org/EIPS/eip-3668#rationale) for more information about how to use `revert` in this way.```sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ILogAutomation, Log} from "@chainlink/contracts/src/v0.8/automation/interfaces/ILogAutomation.sol";
import {
  StreamsLookupCompatibleInterface
} from "@chainlink/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol";

/**
 * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE FOR DEMONSTRATION PURPOSES.
 * DO NOT USE THIS CODE IN PRODUCTION.
 */

// Custom interface for IVerifierProxy
interface IVerifierProxy {
  /**
   * @notice Verifies that the data encoded has been signed.
   * correctly by routing to the correct verifier.
   * @param payload The encoded data to be verified, including the signed
   * report.
   * @param parameterPayload Empty bytes for Data Streams subscription billing.
   * @return verifierResponse The encoded report from the verifier.
   */
  function verify(
    bytes calldata payload,
    bytes calldata parameterPayload
  ) external payable returns (bytes memory verifierResponse);
}

contract StreamsUpkeep is ILogAutomation, StreamsLookupCompatibleInterface {
  error InvalidReportVersion(uint16 version); // Thrown when an unsupported report version is provided to verifyReport.

  /**
   * @dev Represents a data report from a Data Streams stream for v3 schema (used for crypto and DEX State Price
   * streams).
   * The `price`, `bid`, and `ask` values are carried to either 8 or 18 decimal places, depending on the stream.
   * `bid`, and `ask` values are not available for DEX State Price streams.
   * For more information, see https://docs.chain.link/data-streams/crypto-streams and
   * https://docs.chain.link/data-streams/reference/report-schema
   */
  struct ReportV3 {
    bytes32 feedId; // The stream ID the report has data for.
    uint32 validFromTimestamp; // Earliest timestamp for which price is applicable.
    uint32 observationsTimestamp; // Latest timestamp for which price is applicable.
    uint192 nativeFee; // Legacy onchain verification fee field.
    uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing.
    uint32 expiresAt; // Latest timestamp where the report can be verified onchain.
    int192 price; // DON consensus median price (8 or 18 decimals).
    int192 bid; // Simulated price impact of a buy order up to the X% depth of liquidity utilisation (8 or 18 decimals).
    // Note: not available for DEX State Price streams.
    int192 ask; // Simulated price impact of a sell order up to the X% depth of liquidity utilisation (8 or 18
    // decimals). Note: not available for DEX State Price streams.
  }

  /**
   * @dev Represents a data report from a Data Streams stream for v4 schema (RWA streams).
   * The `price` value is carried to either 8 or 18 decimal places, depending on the stream.
   * The `marketStatus` indicates whether the market is currently open. Possible values: `0` (`Unknown`), `1`
   * (`Closed`), `2` (`Open`).
   * For more information, see https://docs.chain.link/data-streams/rwa-streams and
   * https://docs.chain.link/data-streams/reference/report-schema-v4
   */
  struct ReportV4 {
    bytes32 feedId; // The stream ID the report has data for.
    uint32 validFromTimestamp; // Earliest timestamp for which price is applicable.
    uint32 observationsTimestamp; // Latest timestamp for which price is applicable.
    uint192 nativeFee; // Legacy onchain verification fee field.
    uint192 linkFee; // Legacy onchain verification fee field. Not used for subscription billing.
    uint32 expiresAt; // Latest timestamp where the report can be verified onchain.
    int192 price; // DON consensus median benchmark price (8 or 18 decimals).
    uint32 marketStatus; // The DON's consensus on whether the market is currently open.
  }

  struct Quote {
    address quoteAddress;
  }

  IVerifierProxy public verifier;

  string public constant DATASTREAMS_FEEDLABEL = "feedIDs";
  string public constant DATASTREAMS_QUERYLABEL = "timestamp";
  int192 public lastDecodedPrice;

  // This example reads the ID for the ETH/USD report.
  // Find a complete list of IDs at https://docs.chain.link/data-streams/crypto-streams.
  string[] public feedIds = ["0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782"];

  constructor(
    address _verifier
  ) {
    verifier = IVerifierProxy(_verifier);
  }

  // This function uses revert to convey call information.
  // See https://eips.ethereum.org/EIPS/eip-3668#rationale for details.
  function checkLog(
    Log calldata log,
    bytes memory
  ) external returns (bool upkeepNeeded, bytes memory performData) {
    revert StreamsLookup(DATASTREAMS_FEEDLABEL, feedIds, DATASTREAMS_QUERYLABEL, log.timestamp, "");
  }

  /**
   * @notice this is a new, optional function in streams lookup. It is meant to surface streams lookup errors.
   * @return upkeepNeeded boolean to indicate whether the keeper should call performUpkeep or not.
   * @return performData bytes that the keeper should call performUpkeep with, if
   * upkeep is needed. If you would like to encode data to decode later, try `abi.encode`.
   */
  function checkErrorHandler(
    uint256, /*errCode*/
    bytes memory /*extraData*/
  ) external pure returns (bool upkeepNeeded, bytes memory performData) {
    return (true, "0");
    // Hardcoded to always perform upkeep.
    // Read the StreamsLookup error handler guide for more information.
    // https://docs.chain.link/chainlink-automation/guides/streams-lookup-error-handler
  }

  // The Data Streams report bytes is passed here.
  // extraData is context data from stream lookup process.
  // Your contract may include logic to further process this data.
  // This method is intended only to be simulated offchain by Automation.
  // The data returned will then be passed by Automation into performUpkeep
  function checkCallback(
    bytes[] calldata values,
    bytes calldata extraData
  ) external pure returns (bool, bytes memory) {
    return (true, abi.encode(values, extraData));
  }

  // function will be performed onchain
  function performUpkeep(
    bytes calldata performData
  ) external {
    // Decode the performData bytes passed in by CL Automation.
    // This contains the data returned by your implementation in checkCallback().
    (bytes[] memory signedReports, bytes memory extraData) = abi.decode(performData, (bytes[], bytes));

    bytes memory unverifiedReport = signedReports[0];

    (, /* bytes32[3] reportContextData */ bytes memory reportData) = abi.decode(unverifiedReport, (bytes32[3], bytes));

    // Extract report version from reportData
    uint16 reportVersion = (uint16(uint8(reportData[0])) << 8) | uint16(uint8(reportData[1]));

    // Validate report version
    if (reportVersion != 3 && reportVersion != 4) {
      revert InvalidReportVersion(uint8(reportVersion));
    }

    // Verify the report. Data Streams uses subscription billing, so no fee metadata is required.
    bytes memory verifiedReportData = verifier.verify(unverifiedReport, bytes(""));

    // Decode verified report data into the appropriate Report struct based on reportVersion
    if (reportVersion == 3) {
      // v3 report schema
      ReportV3 memory verifiedReport = abi.decode(verifiedReportData, (ReportV3));

      // Store the price from the report
      lastDecodedPrice = verifiedReport.price;
    } else if (reportVersion == 4) {
      // v4 report schema
      ReportV4 memory verifiedReport = abi.decode(verifiedReportData, (ReportV4));

      // Store the price from the report
      lastDecodedPrice = verifiedReport.price;
    }
  }
}
```### Initializing the upkeep contractWhen you deploy the contract, you define the verifier proxy address. You can find this address on the [Stream Addresses](/data-streams/crypto-streams) page. The `IVerifierProxy` interface provides the following functions:* The `verify` function to verify the report onchain.### Emitting a log, retrieving, and verifying the reportAfter registering your upkeep contract with Chainlink Automation with a log trigger, you can emit a log with the `emitLog` function from your emitter contract.1) The emitted log triggers the Chainlink Automation upkeep.
2) Chainlink Automation then uses `StreamsLookup` to retrieve a signed report from the Data Streams Aggregation Network, returns the data in a callback (`checkCallback`), and runs the `performUpkeep` function on your registered upkeep contract.
3) The `performUpkeep` function calls the `verify` function on the verifier contract to verify the report onchain.
4) In this example, the `performUpkeep` function also stores the price from the report in the `lastDecodedPrice` state variable.### Viewing the retrieved priceThe `lastDecodedPrice` getter function of your upkeep contract retrieves the last price stored by the `performUpkeep` function in the `lastDecodedPrice` state variable of the `StreamsUpkeep` contract.### Feed ID types and conversionChainlink Data Streams uses different data types for feed IDs at different stages of the process:* The [`StreamsLookup` error](https://github.com/smartcontractkit/chainlink/blob/contracts-v1.3.0/contracts/src/v0.8/automation/interfaces/StreamsLookupCompatibleInterface.sol#L6) requires feed IDs to be provided as an array of `string`,
* The decoded reports within the contract use `bytes32` types for feed IDs (see the [Report Schemas](/data-streams/reference/report-schema-v3) reference).If your application needs to compare the feed ID(s) sent in the `StreamsLookup` with those received in the report(s), you must convert between `string` and `bytes32` types.### Optional: Handle Data Streams fetching errors offchain with `checkErrorHandler`When Automation detects the triggering event, it runs the `checkLog` function of your upkeep contract, which includes a `StreamsLookup` revert custom error. The `StreamsLookup` revert enables your upkeep to fetch a report from the Data Streams Aggregation Network. If the report is fetched successfully, the `checkCallback` function is evaluated offchain. Otherwise, the `checkErrorHandler` function is evaluated offchain to determine what Automation should do next.In this example, the `checkErrorHandler` is set to always return `true` for `upkeepNeeded`. This implies that the upkeep is always triggered, even if the report fetching fails. You can modify the `checkErrorHandler` function to handle errors offchain in a way that works for your specific use case. Read more about [using the StreamsLookup error handler](/chainlink-automation/guides/streams-lookup-error-handler).## Debugging StreamsLookupRead our [debugging section](/chainlink-automation/reference/debugging-errors) to learn how to identify and resolve common errors when using `StreamsLookup`.