> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sei.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Chainlink Data Streams

> Complete guide to integrating Chainlink Data Streams with Sei Network

## What is Chainlink?

Chainlink is the world's most widely adopted decentralized oracle network, enabling smart contracts to securely access off-chain data, APIs, and traditional payment systems. Chainlink Data Streams is a pull-based oracle solution that delivers high-frequency, low-latency market data directly to smart contracts.

## What This Guide Teaches You

This tutorial will walk you through integrating Chainlink Data Streams on Sei Network to:

1. **Fetch real-time price data** using the Chainlink Data Streams SDK
2. **Deploy a smart contract** that can verify and decode price reports on-chain
3. **Combine off-chain and on-chain components** to create a complete oracle solution
4. **Verify SEI/USDT price feeds** as a practical example

By the end of this guide, you'll have a working implementation that fetches price data off-chain and verifies it on-chain using Chainlink's data streams SDK.

## Prerequisites

Before starting this tutorial, ensure you have:

### Technical Requirements

* **Node.js** (v18 or higher) and npm installed
* **Basic knowledge** of TypeScript/JavaScript
* **Solidity development experience** (basic to intermediate)
* **Familiarity with smart contract deployment** tools like Remix or Hardhat

### Sei Network Setup

**Testnet** :

* **Sei testnet RPC**: `https://evm-rpc-testnet.sei-apis.com`
* **Chain ID**: 1328

**Mainnet** :

* **SEI mainnet RPC**: `https://evm-rpc.sei-apis.com`
* **Chain ID**: 1329

### Chainlink Data Streams Access

<Info>You need to contact Chainlink for getting access to API credentials of Data Streams.</Info>

* **API credentials**: Contact Chainlink to get access to Data Streams on Sei Mainnet/testnet
* **API Key** and **User Secret** for testnet access
* **Feed IDs**: We'll use [SEI/USDT](https://docs.chain.link/data-streams/crypto-streams?page=1\&testnetPage=1\&testnetSearch=SEI#testnet-crypto-streams) feed ID `0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117`
* **VerifierProxy contract address**: `0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB` — the same address is used on [Sei mainnet](https://seiscan.io/address/0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB) and [Sei testnet](https://testnet.seiscan.io/address/0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB). See Chainlink's [Stream Addresses](https://docs.chain.link/data-streams/crypto-streams) page for the current list.

## Step-by-Step Tutorial

### Step 1: Project Setup

Create a new directory and initialize your project:

```bash theme={"dark"}
mkdir chainlink-data-streams-sei
cd chainlink-data-streams-sei
npm init -y
```

Install the required dependencies:

```bash theme={"dark"}
npm install @chainlink/data-streams-sdk dotenv tsx ethers
npm install -D typescript @types/node
```

Create a `.env` file in your project root:

```env theme={"dark"}
API_KEY=your_chainlink_api_key_here
USER_SECRET=your_chainlink_user_secret_here
```

### Step 2: Fetching Price Data with TypeScript

Create a file called `singleStream.ts` with the following code:

```typescript theme={"dark"}
import { createClient, decodeReport, LogLevel, getReportVersion, formatReport } from '@chainlink/data-streams-sdk';
import 'dotenv/config';

async function main() {
  if (process.argv.length < 3) {
    console.error('Please provide a feed ID as an argument');
    console.error('Example: npx tsx singleStream.ts 0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117');
    process.exit(1);
  }

  const feedId = process.argv[2];
  const version = getReportVersion(feedId);

  try {
    const config = {
      apiKey: process.env.API_KEY || 'YOUR_API_KEY',
      userSecret: process.env.USER_SECRET || 'YOUR_USER_SECRET',
      endpoint: 'https://api.testnet-dataengine.chain.link',
      wsEndpoint: 'wss://ws.testnet-dataengine.chain.link',
      // Comment to disable SDK logging:
      logging: {
        logger: console,
        logLevel: LogLevel.INFO
      }
    };

    const client = createClient(config);
    console.log(`\nFetching latest report for feed ${feedId} (${version})...\n`);

    // Get raw report data
    const report = await client.getLatestReport(feedId);
    console.log(`Raw Report Blob: ${report.fullReport}`);

    // Decode the report
    const decodedData = decodeReport(report.fullReport, report.feedID);

    // Combine decoded data with report metadata
    const decodedReport = {
      ...decodedData,
      feedID: report.feedID,
      validFromTimestamp: report.validFromTimestamp,
      observationsTimestamp: report.observationsTimestamp
    };
    console.log(formatReport(decodedReport, version));
  } catch (error) {
    if (error instanceof Error) {
      console.error('Error:', error.message);
    } else {
      console.error('Unknown error:', error);
    }
    process.exit(1);
  }
}

main();
```

This script:

* **Creates a Data Streams client** with your API credentials
* **Fetches the latest report** for a given feed ID
* **Decodes the raw report** into readable price data
* **Displays both raw and formatted data** for debugging

### Step 3: Smart Contract Deployment

Create and deploy the verification contract on Sei Network. Copy this Solidity code into Remix IDE:

```solidity theme={"dark"}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Common} from "@chainlink/contracts@1.5.0/src/v0.8/llo-feeds/libraries/Common.sol";
import {IVerifierFeeManager} from "@chainlink/contracts@1.5.0/src/v0.8/llo-feeds/v0.3.0/interfaces/IVerifierFeeManager.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

using SafeERC20 for IERC20;

/**
 * THIS IS AN EXAMPLE CONTRACT FOR DEMONSTRATION PURPOSES.
 * This contract verifies Chainlink Data Streams reports onchain
 * and handles fee payments automatically.
 */

interface IVerifierProxy {
  function verify(
    bytes calldata payload,
    bytes calldata parameterPayload
  ) external payable returns (bytes memory verifierResponse);

  function s_feeManager() external view returns (IVerifierFeeManager);
}

interface IFeeManager {
  function getFeeAndReward(
    address subscriber,
    bytes memory unverifiedReport,
    address quoteAddress
  ) external returns (Common.Asset memory, Common.Asset memory, uint256);

  function i_linkAddress() external view returns (address);
  function i_rewardManager() external view returns (address);
}

contract ClientReportsVerifier {
  // Errors
  error NothingToWithdraw();
  error NotOwner(address caller);
  error InvalidReportVersion(uint16 version);

  // Report schema v3 (crypto streams)
  struct ReportV3 {
    bytes32 feedId;
    uint32 validFromTimestamp;
    uint32 observationsTimestamp;
    uint192 nativeFee;
    uint192 linkFee;
    uint32 expiresAt;
    int192 price;
    int192 bid;
    int192 ask;
  }

  // Storage
  IVerifierProxy public immutable i_verifierProxy;
  address private immutable i_owner;
  int192 public lastDecodedPrice;

  // Events
  event DecodedPrice(int192 price);

  constructor(address _verifierProxy) {
    i_owner = msg.sender;
    i_verifierProxy = IVerifierProxy(_verifierProxy);
  }

  modifier onlyOwner() {
    if (msg.sender != i_owner) revert NotOwner(msg.sender);
    _;
  }

  /**
   * @notice Verify a Data Streams report and extract price
   * @param unverifiedReport Full payload from Data Streams API
   */
  function verifyReport(bytes memory unverifiedReport) external {
    // Extract report data and version
    (, bytes memory reportData) = abi.decode(unverifiedReport, (bytes32[3], bytes));

    uint16 reportVersion = (uint16(uint8(reportData[0])) << 8) | uint16(uint8(reportData[1]));
    if (reportVersion != 3) {
      revert InvalidReportVersion(reportVersion);
    }

    // Handle fees
    IFeeManager feeManager = IFeeManager(address(i_verifierProxy.s_feeManager()));
    bytes memory parameterPayload;

    if (address(feeManager) != address(0)) {
      address feeToken = feeManager.i_linkAddress();
      (Common.Asset memory fee,,) = feeManager.getFeeAndReward(
        address(this),
        reportData,
        feeToken
      );

      IERC20(feeToken).approve(feeManager.i_rewardManager(), fee.amount);
      parameterPayload = abi.encode(feeToken);
    } else {
      parameterPayload = bytes("");
    }

    // Verify through proxy
    bytes memory verified = i_verifierProxy.verify(unverifiedReport, parameterPayload);

    // Decode and store price
    ReportV3 memory report = abi.decode(verified, (ReportV3));
    lastDecodedPrice = report.price;
    emit DecodedPrice(report.price);
  }

  /**
   * @notice Withdraw ERC-20 tokens from contract
   */
  function withdrawToken(address _beneficiary, address _token) external onlyOwner {
    uint256 amount = IERC20(_token).balanceOf(address(this));
    if (amount == 0) revert NothingToWithdraw();
    IERC20(_token).safeTransfer(_beneficiary, amount);
  }

  /**
   * @notice Get the last decoded price
   */
  function getLastPrice() external view returns (int192) {
    return lastDecodedPrice;
  }
}
```

**Deployment steps in Remix:**

1. Switch to Sei Testnet in MetaMask
2. Deploy with Sei testnet VerifierProxy address: `0x60fAa7faC949aF392DFc858F5d97E3EEfa07E9EB`
3. Save the contract address for testing

### Step 4: Complete Integration Script

Create `verifyOnChain.ts` to combine off-chain fetching with on-chain verification:

```typescript theme={"dark"}
import { createClient, LogLevel } from '@chainlink/data-streams-sdk';
import 'dotenv/config';

// You'll need to install ethers: npm install ethers
import { ethers } from 'ethers';

async function main() {
  const feedId = '0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117';

  // 1. Fetch the latest report
  const config = {
    apiKey: process.env.API_KEY!,
    userSecret: process.env.USER_SECRET!,
    endpoint: 'https://api.testnet-dataengine.chain.link',
    wsEndpoint: 'wss://ws.testnet-dataengine.chain.link',
    logging: {
      logger: console,
      logLevel: LogLevel.INFO
    }
  };

  const client = createClient(config);
  console.log('Fetching latest report...');

  const report = await client.getLatestReport(feedId);
  console.log(`Got report: ${report.fullReport}`);

  // 2. Connect to Sei testnet and your deployed contract
  const provider = new ethers.JsonRpcProvider('https://evm-rpc-testnet.sei-apis.com');
  const wallet = new ethers.Wallet(process.env.PRIVATE_KEY!, provider);

  const contractAddress = 'YOUR_DEPLOYED_CONTRACT_ADDRESS';
  const abi = ['function verifyReport(bytes memory unverifiedReport) external', 'function getLastPrice() external view returns (int192)', 'event DecodedPrice(int192 price)'];

  const contract = new ethers.Contract(contractAddress, abi, wallet);

  // 3. Verify the report on-chain
  console.log('Verifying report on-chain...');
  const tx = await contract.verifyReport(report.fullReport);
  await tx.wait();

  // 4. Get the decoded price
  const price = await contract.getLastPrice();
  console.log(`Price verified on-chain: ${ethers.formatUnits(price.toString(), 18)} SEI/USDT`);
}

main().catch(console.error);
```

## How to Run and Test

### 1. Test Off-chain Data Fetching

Run the single stream script to verify off-chain fetching and decoding:

```bash theme={"dark"}
npx tsx singleStream.ts 0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117
```

**Expected output:**

```bash theme={"dark"}
npx tsx singleStream.ts 0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117
[DataStreams] Data Streams client initialized

Fetching latest report for feed 0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117 (V3)...

[DataStreams] Request successful: GET https://api.testnet-dataengine.chain.link/api/v1/reports/latest?feedID=0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117 - 200
Raw Report Blob: 0x00090d9e8d96765a0c49e03a6ae05c82e8f8de70cf179baa632f18313e54bd690000000000000000000000000000000000000000000000000000000002ad82fe000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117000000000000000000000000000000000000000000000000000000006911a843000000000000000000000000000000000000000000000000000000006911a843000000000000000000000000000000000000000000000000000050fde585ef340000000000000000000000000000000000000000000000000045e76ab072da670000000000000000000000000000000000000000000000000000000069393543000000000000000000000000000000000000000000000000027f85b14842c1b4000000000000000000000000000000000000000000000000027f47776cef65d0000000000000000000000000000000000000000000000000027fb6ed8fc487e400000000000000000000000000000000000000000000000000000000000000027d2c18d803d4e1818fff6521a21a336a03e334ae5af3dccee1ee241239c1661d8a3d0384d5ae6159d4ac1151ffbc9d717304359ce7a904faeb1c5f23375c1e69000000000000000000000000000000000000000000000000000000000000000218da9942080bfb1d679e0185216226c52fd6eaf34fb4c9ee139dd1c87f235a3e18d4822b9454d61d010584f27ccdc6812df13dfe50409252d6cbc774f26b53d2

Report Metadata:
Feed ID: 0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117
Valid From: 1762764867
Observations: 1762764867

Decoded Data:
Native Fee: 89051407707956
LINK Fee: 19676218805901927
Expires At: 1765356867
Price: 180009506586149300
Bid Price: 179941088372418000
Ask Price: 180063641553635300
```

### 2. Test Smart Contract Deployment

1. Copy the raw report blob from Step 1 output (the long hex string starting with `0x00090d9e...`)
2. Call `verifyReport()` and paste the entire blob as the `unverifiedReport` parameter
3. Call `getLastPrice()` - you should see the decoded price (e.g., `180009506586149300`)

### 3. Test Complete Integration

**Step 3a: Setup Integration Script**

Add your private key to `.env`:

```env theme={"dark"}
API_KEY=your_chainlink_api_key_here
USER_SECRET=your_chainlink_user_secret_here
PRIVATE_KEY=your_sei_testnet_private_key_here
```

Update `verifyOnChain.ts` with your deployed contract address:

Replace `YOUR_DEPLOYED_CONTRACT_ADDRESS` in the script with your actual contract address.

**Step 3b: Run Integration Test**

```bash theme={"dark"}
npx tsx verifyOnChain.ts
```

**Expected Output:**

```bash theme={"dark"}
npx tsx verifyOnChain.ts
[DataStreams] Data Streams client initialized
Fetching latest report...
[DataStreams] Request successful: GET https://api.testnet-dataengine.chain.link/api/v1/reports/latest?feedID=0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117 - 200
Got report: 0x00090d9e8d96765a0c49e03a6ae05c82e8f8de70cf179baa632f18313e54bd690000000000000000000000000000000000000000000000000000000002ad80fd000000000000000000000000000000000000000000000000000000030000000100000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000280000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001200003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117000000000000000000000000000000000000000000000000000000006911a794000000000000000000000000000000000000000000000000000000006911a794000000000000000000000000000000000000000000000000000051070460a3df0000000000000000000000000000000000000000000000000045e8f0bff5bcd30000000000000000000000000000000000000000000000000000000069393494000000000000000000000000000000000000000000000000027fe207ce1890f4000000000000000000000000000000000000000000000000027fb2bbee1e2038000000000000000000000000000000000000000000000000028013671aa35a0400000000000000000000000000000000000000000000000000000000000000024ef8f65551057e6a89745ffd306a5202162d6b280fc9754360175b54a95f11598da55d6d7d965384479c9d4ff248cd00ffd89078d05de048299c61c48d0361c8000000000000000000000000000000000000000000000000000000000000000278bc17f79bf5d6483310b28fe004e884a64e603cbc212044492098af36b4d50047bf5dc8af1e50abfc5f38060fb7f4b6dc9089589915144211fa6e6ff049dd1e
Verifying report on-chain...
Price verified on-chain: 0.180009506586149300 SEI/USDT
```

## Resources

* [Chainlink Data Streams Documentation](https://docs.chain.link/data-streams/)
