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/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:

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