Thanks to visit codestin.com
Credit goes to docs.sei.io

Skip to Content
EVMOraclesChainlink Data Streams

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 (v16 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
You need to contact Chainlink for getting access to API credentials of Data Streams.

Step-by-Step Tutorial

Step 1: Project Setup

Create a new directory and initialize your project:

mkdir chainlink-data-streams-sei cd chainlink-data-streams-sei npm init -y

Install the required dependencies:

npm install @chainlink/data-streams-sdk dotenv tsx ethers npm install -D typescript @types/node

Create a .env file in your project root:

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:

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:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Common} from "@chainlink/[email protected]/src/v0.8/llo-feeds/libraries/Common.sol"; import {IVerifierFeeManager} from "@chainlink/[email protected]/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:

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:

npx tsx singleStream.ts 0x0003dba2d8553dfd7afe35c2bfe217ef5106d7805e5272c04a08940ddb868117

Expected output:

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:

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

npx tsx verifyOnChain.ts

Expected Output:

npx tsx singleStream.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

Last updated on