ऑन-चेन प्रोग्राम सीधे ऑफ-चेन डेटा को एक्सेस नहीं कर सकते हैं। वे एसेट की कीमतों, इवेंट के परिणामों या API प्रतिक्रियाओं जैसी जानकारी लाने के लिए ओरेकल (oracles) पर निर्भर करते हैं। इन ओरेकल्स के बिना, प्रोग्राम केवल ऑन-चेन पहले से स्टोर किए गए स्टेट तक ही सीमित रहते हैं।
Switchboard एक मल्टी-चेन डिसेंट्रलाइज्ड ओरेकल नेटवर्क है, जिसे मूल रूप से स्मार्ट कॉन्ट्रैक्ट्स को कीमत, मौसम और इवेंट डेटा जैसे विश्वसनीय ऑफ-चेन डेटा प्रदान करने के लिए Solana पर बनाया गया था। इस ट्यूटोरियल में, हम एक Solana प्रोग्राम बनाएंगे जो Switchboard से वर्तमान SOL/USD कीमत को पढ़ेगा।
इस ट्यूटोरियल में हम तीन चीजें करेंगे:
- एक Solana प्रोग्राम बनाएं और डिप्लॉय करें जो Switchboard से डेटा पढ़ता हो।
- हमारे प्रोग्राम के लिए एक नए Switchboard प्राइस फीड को इनिशियलाइज़ और कॉन्फ़िगर करें।
- कीमत प्रदर्शित करने के लिए एक क्लाइंट स्क्रिप्ट लिखें जो Devnet पर हमारे प्रोग्राम के साथ इंटरैक्ट करे।
Solana प्रोग्राम बनाने से पहले, आइए सबसे पहले समझते हैं कि Switchboard कैसे काम करता है।
Switchboard कैसे काम करता है
Solana प्रोग्राम्स को ऑफ-चेन डेटा पढ़ने की अनुमति देने के लिए Switchboard निम्नलिखित 4 प्रमुख कंपोनेंट्स का उपयोग करता है:
- Jobs: Switchboard में एक जॉब अनुक्रमिक (sequential) टास्क का एक संग्रह है। प्रत्येक टास्क एक विशिष्ट ऑपरेशन करता है। उदाहरण के लिए, एक टास्क किसी बाहरी API एंडपॉइंट से डेटा प्राप्त कर सकता है जबकि दूसरा टास्क उस रिस्पॉन्स को पार्स करता है।
- Data Sources: ये वो जगहें हैं जहां से ऑफ-चेन डेटा आता है, जैसे Binance, Coinbase, और Pyth। Switchboard जॉब्स इन स्रोतों से डेटा प्राप्त करते हैं।
- Oracles: ये Switchboard नेटवर्क नोड्स हैं जो आपके जॉब्स को निष्पादित (execute) करते हैं। प्रत्येक ओरेकल सभी कॉन्फ़िगर किए गए डेटा स्रोतों से डेटा प्राप्त करता है, उन्हें एग्रीगेट (aggregate) करता है, और ऑन-चेन फीड में एक सिंगल
i128वैल्यू सबमिट करता है। - Feed: फीड वह ऑन-चेन अकाउंट है जो ओरेकल सबमिशन को स्टोर करता है। जब आपका प्रोग्राम फीड को पढ़ता है, तो उसे व्यक्तिगत ओरेकल परिणामों से उत्पन्न एग्रीगेटेड वैल्यू प्राप्त होती है, जिसे Switchboard SDK दशमलव अंकगणित (decimal arithmetic) के लिए
Decimalटाइप में बदल देता है।
संक्षेप में, ऊपर दिए गए कंपोनेंट एक साथ इस प्रकार काम करते हैं:
- आप जॉब्स को परिभाषित करते हैं जो यह निर्दिष्ट करते हैं कि डेटा कहां से प्राप्त करना है (डेटा स्रोत)
- ओरेकल नोड्स इन जॉब्स को निष्पादित करते हैं और अपने परिणामों को ऑन-चेन फीड अकाउंट में सबमिट करते हैं
- फीड ओरेकल सबमिशन को एक सिंगल
i128वैल्यू में एग्रीगेट करता है - आपका प्रोग्राम फीड से डेटा पढ़ता है
हम इस ट्यूटोरियल के दौरान सीखेंगे कि ये प्रक्रियाएं कैसे काम करती हैं। आइए अपने प्राइस फीड्स इम्प्लीमेंटेशन के साथ शुरुआत करें।
पूर्वापेक्षाएँ (Prerequisites)
साथ-साथ चलने के लिए, आपको एक काम करने वाले Solana डेवलपमेंट एनवायरनमेंट की आवश्यकता होगी जिसमें निम्नलिखित टूल इंस्टॉल हों:
- Solana CLI and Anchor: Solana प्रोग्राम्स को लिखने, बिल्ड करने और डिप्लॉय करने के लिए आवश्यक हैं। यदि आपने उन्हें पहले से सेट अप नहीं किया है, तो इस सीरीज का पहला लेख पढ़ें।
- Bun: क्लाइंट स्क्रिप्ट चलाने के लिए एक पैकेज मैनेजर के रूप में। bun इंस्टॉल करने के लिए अपने टर्मिनल पर यह कमांड चलाएँ:
curl -fsSL https://bun.sh/install | bash।
हम यूनिट टेस्ट के बजाय स्टैंडअलोन स्क्रिप्ट्स का उपयोग करके अपने डिप्लॉयड प्रोग्राम के साथ इंटरैक्ट करेंगे। ऐसा इसलिए है क्योंकि Devnet पर प्राइस फीड्स अपडेट होते हैं, और हम यह दिखाना चाहते हैं कि रीयल-टाइम ओरेकल डेटा ऑन-चेन लॉजिक में कैसे फ्लो करता है।
अपने टर्मिनल पर नीचे दी गई कमांड चलाकर अपने Solana क्लस्टर को Devnet पर सेट करें:
solana config set --url [https://api.devnet.solana.com](https://api.devnet.solana.com/)
ट्रांजेक्शन के भुगतान के लिए आपको Devnet पर कुछ SOL की भी आवश्यकता होगी। आप solana airdrop का उपयोग करके फॉसेट (faucet) से टेस्ट SOL का अनुरोध कर सकते हैं:
solana airdrop 2 # for 2 devnet SOL.
आप Devnet फॉसेट पर एक बार में केवल 2 SOL का अनुरोध कर सकते हैं। आप 2 का अनुरोध कर सकते हैं, थोड़ी देर प्रतीक्षा करें और बाद में एक और अनुरोध करें क्योंकि यह रेट-लिमिटेड (rate-limited) है।
Solana slots
Solana में, समय को स्लॉट्स (slots) में मापा जाता है, जो नेटवर्क समय के अनुक्रमिक अंतराल (sequential intervals) हैं जिसके दौरान चेन आगे बढ़ती है; नेटवर्क के प्रगति के साथ एक स्लॉट नंबर बढ़ता है और इसका उपयोग ऑन-चेन घटनाओं को क्रमबद्ध करने के लिए एक साधारण घड़ी के रूप में किया जाता है। यह Ethereum के ब्लॉक नंबर (block.number) के समान है क्योंकि वे दोनों समय की प्रगति और घटनाओं के क्रम का प्रतिनिधित्व करते हैं।
स्लॉट्स के इस विचार को ध्यान में रखें, Switchboard डेटा के बासीपन (staleness) को मापने के लिए इसका उपयोग करता है, हम देखेंगे कि इस लेख में बाद में इसका उपयोग कैसे किया जाता है।
प्रोग्राम सेटअप (Program setup)
हम एक Anchor प्रोजेक्ट बनाकर शुरुआत करेंगे जो Solana प्रोग्राम को परिभाषित करता है जो Switchboard से प्राइस डेटा खींचेगा (pull करेगा)।
anchor init switchboard-demo
cd switchboard-demo
अपने Anchor.toml प्रोवाइडर में cluster फील्ड को अपडेट करें ताकि Devnet का उपयोग किया जा सके, क्योंकि हम Devnet पर काम कर रहे होंगे:
[provider]
cluster = "Devnet"
wallet = "~/.config/solana/id.json"
इसके बाद, हम अपनी programs/switchboard-demon/src/Cargo.toml फ़ाइल के डिपेंडेंसी सेक्शन में switchboard-on-demand क्रेट (crate) जोड़ेंगे। यह वह Switchboard क्रेट है जिसके साथ हम काम करेंगे।
[dependencies]
anchor-lang = "0.31.1"
switchboard-on-demand = "0.5.3"
programs/switchboard-demo/src/lib.rs के अंदर, हम एक प्रोग्राम लिखते हैं जो:
- Switchboard ऑन-डिमांड फीड अकाउंट डेटा को रॉ बाइट्स (raw bytes) के रूप में पढ़ता है।
switchboard-on-demandक्रेट से फीड के रॉ बाइट्स कोPullFeedAccountDataस्ट्रक्चर में पार्स करता है।- नवीनतम कीमत को मान्य (validate) करने और निकालने के लिए निम्नलिखित मापदंडों (parameters) के साथ पार्स किए गए फीड पर
get_valueमेथड को कॉल करता है:max_stale_slots: यह फीड अकाउंट के अंतिम बार अपडेट होने के बाद से Solana स्लॉट की अधिकतम संख्या निर्धारित करता है। यदि फीड इससे पुराना है, तोfeed.get_valueविफल हो जाता है।min_samples: एक वैध कीमत के लिए आवश्यक न्यूनतम ओरेकल सबमिशन की संख्या निर्धारित करता है।only_positive: जब यह true होता है, तो गैर-सकारात्मक (non-positive) मानों (≤ 0) को अस्वीकार कर देता है। उन कीमतों या मात्राओं के लिए उपयोगी है जो हमेशा सकारात्मक होनी चाहिए।
msg!के साथ SOL/USD कीमत को लॉग करता है।
use anchor_lang::prelude::*;
use switchboard_on_demand::{
on_demand::accounts::pull_feed::PullFeedAccountData,
prelude::rust_decimal,
};
use rust_decimal::Decimal;
declare_id!("iSYBH57FJPsqKnVxz8pyqPvCLEBH63y95Vgk346utR2");
#[program]
pub mod switch_on_demand_price_feed {
use super::*;
pub fn read_price(ctx: Context<ReadPrice>) -> Result<()> {
// Step 1: READS THE FEED ACCOUNT's RAW BINARY (bytes)
let data_slice = ctx.accounts.feed.data.borrow();
// Step 2: PARSE FEED ACCOUNT DATA
let feed = PullFeedAccountData::parse(data_slice).unwrap();
// Step 3: RETRIEVE FEED VALUE WITH SLOT, SAMPLING CONSTRAIN,
// AND THE PARAMETER THAT DETERMINES WHETHER THE RECEIVED VALUE
// IS POSITIVE OR NEGATIVE
let price: Decimal = feed.get_value(
&Clock::get()?,
/*max_stale_slots=*/ 100,
/*min_samples=*/ 3,
/*only_positive=*/ true,
).unwrap();
// Step 4: LOGS THE SOL/USD PRICE WITH `msg!`.
msg!("SOL/USD price: {}", price);
Ok(())
}
}
#[derive(Accounts)]
pub struct ReadPrice<'info> {
/// CHECK: This is a Switchboard on-chain feed (PullFeedAccount)
pub feed: AccountInfo<'info>,
}
चरण 3 में स्लॉट के बासीपन (staleness) और सैंपलिंग बाधाओं (sampling constraints) के बारे में टिप्पणी (comment) पर ध्यान दें। हर बार जब Switchboard ऑन-चेन एक नया मान लिखता है, तो यह स्लॉट नंबर रिकॉर्ड करता है। जब आप feed.get_value(&Clock::get()?, max_stale_slots, ...) कॉल करते हैं, तो Switchboard वर्तमान स्लॉट की तुलना फीड के अंतिम अपडेट स्लॉट से करता है।
यदि अंतर max_stale_slots (हमारे कोड में 100) से अधिक हो जाता है, तो get_value एक त्रुटि (error) देता है। इंस्ट्रक्शन विफल हो जाता है और ट्रांजेक्शन को अस्वीकार कर दिया जाता है।
इसके अलावा, min_samples पैरामीटर यह सुनिश्चित करता है कि हम सटीकता बढ़ाने के लिए पर्याप्त ओरेकल से प्रतिक्रियाओं को एग्रीगेट करें। हमारे उदाहरण में, हमने इसे 3 पर सेट किया है, जिसका अर्थ है कि परिणाम में कम से कम 3 ओरेकल प्रतिक्रियाओं का डेटा शामिल होना चाहिए। हम देखेंगे कि इन ओरेकल्स को कैसे कॉन्फ़िगर किया जाए इस लेख में बाद में जब हम ऑफ-चेन इनिशियलाइज़ेशन पर चर्चा करेंगे।
इसके बाद, नीचे दिए गए कमांड को चलाकर प्रोग्राम को बिल्ड और डिप्लॉय करें:
anchor build && anchor deploy
एक सफल डिप्लॉयमेंट नीचे दिखाए गए अनुसार प्रोग्राम Id और सिग्नेचर वापस करेगा:

हमारा ऑन-चेन प्रोग्राम अब डिप्लॉय हो गया है और SOL/USD की कीमत को लॉग कर सकता है, लेकिन हम अभी तक इसका उपयोग नहीं कर सकते हैं क्योंकि हालांकि प्रोग्राम feed अकाउंट से कीमत पढ़ने के लिए तैयार है, लेकिन इसके पास पढ़ने के लिए अभी तक कोई विशिष्ट फीड नहीं है। हम आगे ऑफ-चेन फीड सेट करके इस पर काम करेंगे।
ऑफ-चेन फीड सेटअप (Off-chain feed setup)
याद करें कि फीड वह ऑन-चेन अकाउंट है जो ओरेकल सबमिशन को स्टोर करता है।
फीड सेट अप करने में दो चरण शामिल हैं, जिन्हें हम आगे कवर करेंगे:
- फीड कॉन्फ़िगरेशन और इनिशियलाइज़ेशन
- फीड अपडेट
1/2 फीड कॉन्फ़िगरेशन और इनिशियलाइज़ेशन
Feed configuration आपके ओरेकल के लिए डेटा स्रोत और एग्रीगेशन नियमों को परिभाषित करता है (जैसे ऑन-चेन वाले जैसे max_stale_slots)। आप निर्दिष्ट करते हैं कि किन बाहरी API या ऑन-चेन ओरेकल से क्वेरी करनी है, आपको कितनी प्रतिक्रियाओं की आवश्यकता है, और स्रोतों के बीच स्वीकार्य भिन्नता (variance) क्या है। ऑन-चेन पर कुछ भी जाने से पहले यह कॉन्फ़िगरेशन एक JavaScript ऑब्जेक्ट के रूप में मौजूद होता है।
Feed initialization कॉन्फ़िगरेशन लेता है और ऑन-चेन वास्तविक अकाउंट बनाता है। इनिशियलाइज़ेशन ट्रांजेक्शन आपके फीड के मेटाडेटा को स्टोर करता है, इसे ओरेकल नोड्स के एक पूल (जिसे Switchboard में “oracle queue” कहा जाता है) से बांधता है, और वह पब्लिक कुंजी (public key) उत्पन्न करता है जिसका संदर्भ आपके प्रोग्राम को प्राइस डेटा का अनुरोध करते समय देना चाहिए।
Chainlink ओरेकल के विपरीत, Switchboard फीड स्वचालित रूप से अपडेट नहीं होते हैं। यह एक पुल मॉडल (pull model) का उपयोग करता है। नए डेटा को प्राप्त करने और ऑन-चेन फीड को अपडेट करने के लिए आपके प्रोग्राम को एक ऑफ-चेन स्क्रिप्ट के माध्यम से जॉब ट्रिगर करना होगा।
प्रत्येक जॉब में निम्न शामिल होते हैं:
- Tasks array: क्रमिक रूप से निष्पादित करने के लिए ऑपरेशन्स वाले टास्क की एक सूची
- Task types: Tasks array में विभिन्न टास्क जैसे:
httpTask: जो किसी URL से फीड डेटा प्राप्त करता हैjsonParseTask: एक पाथ क्वेरी का उपयोग करकेhttpTaskसे प्राप्त JSON रिस्पॉन्स से विशिष्ट वैल्यू निकालता है
नीचे दिया गया उदाहरण दो-टास्क वाला एक जॉब दिखाता है: एक जो Coinbase API से एक्सचेंज रेट डेटा प्राप्त करता है और दूसरा जो रिस्पॉन्स को पार्स करता है।

आमतौर पर, आप प्रति जॉब एक फ़ंक्शन का उपयोग करके इन जॉब्स को स्वयं लागू करेंगे। हालाँकि, सरलता के लिए हम इस उदाहरण में फ़ेचिंग (fetching) लॉजिक को मैन्युअल रूप से लागू नहीं करेंगे।
Switchboard टीम ने पहले से ही एक सार्वजनिक utils.ts फ़ाइल प्रदान की है जिसमें सामान्य जॉब-फ़ेचिंग इम्प्लीमेंटेशन शामिल हैं। हम उनके बजाय इसका उपयोग करेंगे। GitHub पर utils फ़ाइल खोलें, कंटेंट को कॉपी करें, और इसे /scripts/utils.ts में पेस्ट करें।
एकाधिक डेटा स्रोत सेट अप करना (Setting up multiple data sources)
ऊपर दिया गया आरेख केवल एक स्रोत के साथ एक टास्क दिखाता है। एक वास्तविक प्रोग्राम में, आपको एकाधिक स्रोतों (multiple sources) की आवश्यकता होगी, जिसका अर्थ है, आपके पास एकाधिक जॉब होंगे। एकाधिक स्रोत सिंगल-पॉइंट विफलता को रोकते हैं और फीड को वैरियंस (variance) चेक के माध्यम से आउटलेर्स (outliers) को फ़िल्टर करने देते हैं।
इनिशियलाइज़ेशन स्क्रिप्ट बनाना
/scripts फ़ोल्डर में एक initializeFeeds.ts फ़ाइल बनाएँ और Switchboard नेटवर्क के साथ इंटरैक्ट करने के लिए आवश्यक डिपेंडेंसी इंस्टॉल करने के लिए नीचे दिया गया कमांड चलाएँ। हम @solana/web3.js का भी उपयोग करेंगे, जो हमारे Anchor इंस्टॉलेशन के साथ शामिल है।
yarn add @switchboard-xyz/on-demand @switchboard-xyz/common
हमारी स्क्रिप्ट 5 चरणों को निष्पादित करती है:
SOL/USDकीमत पढ़ने के लिए चार जॉब्स को परिभाषित करती है: एक Pyth के ऑन-चेन ओरेकल से पढ़ता है, तीन REST API से पढ़ते हैं।- एग्रीगेशन नियमों (
maxStaleness,minimumSamples, आदि) के साथ फीड को कॉन्फ़िगर करती है। - फीड के प्राइस डेटा को स्टोर करने के लिए एक अद्वितीय (unique) ऑन-चेन एड्रेस बनाने के लिए एक नया फीड अकाउंट कीपेयर (keypair) जेनरेट करती है।
- जॉब डेफ़िनिशन को ऑफ-चेन स्टोर करने के लिए Crossbar (जो IPFS पर अपलोड करने के लिए Switchboard की सर्विस है) का उपयोग करती है। यह ऑन-चेन स्टोरेज लागत से बचाता है। Crossbar IPFS कंटेंट हैश लौटाता है जिसका उपयोग ओरेकल स्टोर किए गए जॉब डेफ़िनिशन को प्राप्त करने के लिए करेंगे। हम देखेंगे कि ओरेकल इस हैश का उपयोग फीड अपडेट (feed updates) सेक्शन में कैसे करते हैं।
- ऑन-चेन फीड बनाने के लिए फीड इनिशियलाइज़ेशन ट्रांजेक्शन को बिल्ड करती है और भेजती है।
import { PublicKey } from "@solana/web3.js";
import * as sb from "@switchboard-xyz/on-demand";
import { AnchorUtils, PullFeed } from "@switchboard-xyz/on-demand";
import { CrossbarClient, decodeString } from "@switchboard-xyz/common";
import {
buildCoinbaseJob,
buildBinanceJob,
buildPythJob,
buildBybitJob,
TX_CONFIG,
} from "./utils";
const crossbarClient = new CrossbarClient(
"https://crossbar.switchboard.xyz",
/* verbose= */ true
);
// 1. DEFINE FOUR FEED JOBs to READ SOL/USD PRICE
const FEED_JOBS = [
// Pyth oracle passing the SOL/USD price feed public key
buildPythJob("H6ARHf6YXhGYeQfUzQNGk6rDNnLBQKrenN712K4AQJEG"),
// Web API Endpoints
buildCoinbaseJob("SOL-USD"),
buildBinanceJob("SOLUSDT"),
buildBybitJob("SOLUSDT"),
];
(async function main() {
// Load wallet and RPC connection from Solana CLI config (~/.config/solana/id.json)
// Then fetch the network's default oracle queue
const { keypair, connection, program } = await AnchorUtils.loadEnv();
const queueAccount = await sb.getDefaultQueue(connection.rpcEndpoint);
const queue = queueAccount.pubkey;
// 2. FEED CONFIGURATION
const conf: any = {
name: "SOL-USD Price Feed", // the feed name (max 32 bytes)
queue: new PublicKey(queue), // the queue of oracles to bind to
maxVariance: 1.0, // allow 1% variance between submissions and jobs
minResponses: 3, // minimum number of responses of jobs to allow
numSignatures: 3, // number of signatures to fetch per update
minSampleSize: 3, // minimum number of responses to sample for a result
maxStaleness: 100, // maximum stale slots of responses to sample
};
// 3. GENERATE FEED KEYPAIR
console.log("Initializing new data feed");
const [pullFeed, feedKp] = PullFeed.generate(program!);
// 4. STORE JOB DEFINITIONS ON IPFS
conf.feedHash = decodeString(
(await crossbarClient.store(queue.toString(), FEED_JOBS)).feedHash
);
// 5. BUILD AND SEND THE INITIALIZATION TRANSACTION
// WITH THE FEED CONFIGURATION
const initTx = await sb.asV0Tx({
connection,
ixs: [await pullFeed.initIx(conf)],
payer: keypair.publicKey,
signers: [keypair, feedKp],
computeUnitPrice: 75_000,
computeUnitLimitMultiple: 1.3,
});
console.log("Sending initialize transaction");
const sig = await connection.sendTransaction(initTx, TX_CONFIG);
await connection.confirmTransaction(sig, "confirmed");
console.log(`Feed ${feedKp.publicKey} initialized: ${sig}`);
})();
आइए उपरोक्त कोड के मुख्य Switchboard विशिष्ट भागों की व्याख्या करें:
Oracle queues
स्क्रिप्ट sb.getDefaultQueue() का उपयोग करके आपके नेटवर्क के लिए डिफ़ॉल्ट ओरेकल कतार (queue) प्राप्त करती है। यह कतार ओरेकल नोड्स के लिए Switchboard का समन्वय तंत्र (coordination mechanism) है। जब आप किसी फीड को किसी कतार से बांधते हैं, तो आप Switchboard नेटवर्क को बता रहे होते हैं कि ओरेकल्स का कौन सा पूल आपके फीड के लिए अपडेट अनुरोधों को पूरा कर सकता है। प्रत्येक कतार में पंजीकृत ओरेकल्स, रिवॉर्ड मापदंडों और परिचालन नियमों का अपना सेट होता है। Devnet पर, यह पब्लिक टेस्ट कतार लौटाता है जिसे सभी डेवलपर्स साझा करते हैं।
फीड कॉन्फ़िगरेशन (The feed configuration)
उपरोक्त कोड में, फीड कॉन्फ़िगरेशन नियमों का वह सेट है जो हमारे जॉब्स के परिणामों को एक सिंगल, विश्वसनीय मान (trusted value) में एग्रीगेट करता है। यह विश्वास हमारे कॉन्फ़िगरेशन से वेलिडेशन की कई परतों से आता है:
minResponses: हमें प्रति अपडेट कम से कम 3 सफल प्रतिक्रियाओं की आवश्यकता है, और हम परिणाम की गणना करते समय कम से कम 3 सबमिशन का नमूना (sample) लेते हैं। यहget_valueके लिए हमारे ऑन-चेन कॉल मेंmin_samples = 3के साथ मेल खाता है।maxVariance: हमने इसे1.0पर सेट किया है, जिसका अर्थ है कि यदि एक स्रोत ऐसी कीमत रिपोर्ट करता है जो दूसरों से 1% से अधिक भिन्न है, तो इसे असंगत (inconsistent) मानकर त्याग दिया जा सकता है।maxStaleness: यह सुनिश्चित करता है कि डेटा हाल का (recent) है, जो हमारे ऑन-चेन प्रोग्राम मेंmax_stale_slotsवैल्यू के अनुरूप है।
bun का उपयोग करके स्क्रिप्ट चलाएं, वह JavaScript रनटाइम जिसे हमने पहले इंस्टॉल किया था:
bun run scripts/initializeFeeds.ts
परिणाम में प्रोग्राम में फीड अकाउंट से फीड पब्लिक की (public key) शामिल होगी जैसा कि नीचे दिखाया गया है:

यह फीड अब Devnet पर लाइव है, लेकिन यह अभी भी खाली है। अगले सेक्शन में, हम इस पब्लिक कुंजी का उपयोग करके इसमें डेटा भरना शुरू करेंगे।
2/2 फीड अपडेट (Feed update)
अब जबकि हमारा फीड अकाउंट इनिशियलाइज़ हो गया है, हमें इसे लगातार ताज़ा डेटा के साथ भरने के लिए एक प्रक्रिया की आवश्यकता है। फीड अपडेट प्रक्रिया का वह भाग है जो यह सुनिश्चित करता है कि फीड अकाउंट में हमेशा अद्यतित (up-to-date) डेटा हो।
हम एक स्क्रिप्ट, runfeeds.ts बनाएंगे, जो एक इनफिनिट लूप (infinite loop) चलाती है। प्रत्येक इटरेशन (iteration) में, यह फीड पब्लिक कुंजी का उपयोग करके Switchboard नेटवर्क से हमारे जॉब्स के लिए नवीनतम कीमतों का अनुरोध करती है। सभी ओरेकल्स के डेटा को एग्रीगेट किया जाता है, और फिर स्क्रिप्ट हमारे ऑन-चेन फीड अकाउंट में मान्य परिणाम को स्टोर करने के लिए एक ट्रांजेक्शन भेजती है।
import * as sb from "@switchboard-xyz/on-demand";
import { CrossbarClient } from "@switchboard-xyz/common";
import yargs from "yargs";
import { TX_CONFIG, sleep } from "./utils";
import { PublicKey } from "@solana/web3.js";
/// Parse command-line arguments - requires the feed account public key '--feed'
const argv = yargs(process.argv).options({ feed: { required: true } })
.argv as any;
console.log(`Using feed: ${argv.feed}`);
// The main function is wrapped in an immediately-invoked async function expression (IIFE).
(async function main() {
// Load wallet keypair, RPC connection, and program
const { keypair, connection, program } = await sb.AnchorUtils.loadEnv();
// Load the default Switchboard queue account for the specified network (devnet or mainnet).
const queue = await sb.Queue.loadDefault(program!);
// Initialize a 'PullFeed' object to interact with the
// on-chain data feed public key specified in the terminal.
const feedAccount = new sb.PullFeed(program!, argv.feed!);
// Connect to Switchboard's off-chain infrastructure
const crossbar = new CrossbarClient("https://crossbar.switchboard.xyz");
const gateway = await queue.fetchGatewayFromCrossbar(crossbar as any);
// Cache address lookup tables to reduce transaction size
await feedAccount.preHeatLuts();
let runCount = 0;
console.log("Starting feed updater.");
// Start an infinite loop to continuously update the data feed.
while (true) {
try {
console.log(`\n--- Update #${++runCount} ---`);
// Request fresh oracle data. The gateway reads the feedHash from the feed account,
// retrieves job definitions from IPFS, distributes them to oracles, and returns
// their aggregated responses
const [pullIx, responses, _ok, luts] = await feedAccount.fetchUpdateIx({
gateway: gateway.gatewayUrl,
crossbarClient: crossbar as any,
});
// Check for oracle errors
let hasError = false;
for (const response of responses) {
const shortErr = response.shortError();
if (shortErr) {
console.log(`Oracle response error: ${shortErr}`);
hasError = true;
}
}
// Skip update if errors occurred or no instructions returned
if (hasError || !pullIx || pullIx.length === 0) {
console.log("Skipping update due to oracle errors or no instructions generated.");
await sleep(5000); // Wait longer before retrying if there are errors.
continue;
}
// Assemble the update instructions into a versioned transaction (v0).
const tx = await sb.asV0Tx({
connection,
ixs: [...pullIx!],
signers: [keypair], // The payer's keypair must sign the transaction.
computeUnitPrice: 200_000, // Set a priority fee to get the transaction processed faster.
computeUnitLimitMultiple: 1.3, // Add a buffer to the compute unit limit to prevent failure.
lookupTables: luts, // Include the pre-heated LUTs.
});
// Send transaction to update the feed on-chain
const sig = await connection.sendTransaction(tx, TX_CONFIG);
console.log(`✅ Transaction sent: https://explorer.solana.com/tx/${sig}?cluster=devnet`);
console.log("Waiting for confirmation...");
await connection.confirmTransaction(sig, "confirmed");
console.log("✅ Transaction confirmed!");
} catch (e) {
console.error("❌ An error occurred in the main loop:", e);
} finally {
// Pause execution for a few seconds before starting the next update cycle.
await sleep(5000);
}
}
})();
IPFS कंटेंट हैश का उपयोग कैसे किया जाता है
जब feedAccount.fetchUpdateIx() को कॉल किया जाता है:
- यह Solana से फीड अकाउंट डेटा पढ़ता है, जिसमें
feedHash(इनिशियलाइज़ेशन के दौरान स्टोर किया गया IPFS कंटेंट हैश) शामिल होता है - यह इस
feedHashके साथ गेटवे (gateway) को एक अनुरोध भेजता है - गेटवे (और ओरेकल) IPFS से जॉब डेफ़िनिशन प्राप्त करने के लिए
feedHashका उपयोग करते हैं - ओरेकल उन जॉब्स को निष्पादित करते हैं (Pyth, Coinbase, Binance, Bybit से प्राप्त करते हैं)
- ओरेकल अपने परिणाम switchboard गेटवे को लौटाते हैं, जो उन्हें एग्रीगेट करता है
fetchUpdateIx()ऐसे इंस्ट्रक्शन (pullIx) लौटाता है जो ट्रांजेक्शन सफल होने पर एग्रीगेटेड ओरेकल डेटा को फीड अकाउंट में लिखते हैं।
हम कमांड लाइन आर्गुमेंट के रूप में फीड पब्लिक की (public key) के साथ स्क्रिप्ट चलाएंगे जैसा कि नीचे दिखाया गया है:
bun scripts/runfeeds.ts --feed GgGVgSLWAyL9Xf4fGaAQQCkmWetBjX7PCNz8kTK97DKB
और परिणाम कुछ इस तरह दिखेगा:

हम यह सुनिश्चित करने के लिए इस स्क्रिप्ट को लगातार चलाते रहेंगे कि हमें हमेशा ताज़ा डेटा प्राप्त हो।
SOL/USD की कीमत पढ़ना
अब जब हमारे पास प्रोग्राम डिप्लॉय हो गया है और फीड लगातार अपडेट हो रहा है, तो आइए SOL/USD की कीमत पढ़ने के लिए एक स्क्रिप्ट लिखें।
नीचे दी गई स्क्रिप्ट यह करती है:
- Define the feed account: आप हमारी फीड इनिशियलाइज़ेशन स्क्रिप्ट (
GgGVgSLWAyL9Xf4fGaAQQCkmWetBjX7PCNz8kTK97DKB) के माध्यम से पहले बनाई गई फीड अकाउंट पब्लिक कुंजी प्रदान करते हैं। - Build the instruction: Switchboard का
.readPrice()मेथड ऑन-चेनread_priceमेथड को संदर्भित करता है।.accounts({ feed })चरण फीड अकाउंट को इंस्ट्रक्शन से बांधता है। - Build the transaction: सिग्नेचर और कंप्यूट लिमिट्स के साथ इंस्ट्रक्शन को एक वर्शन्ड ट्रांजेक्शन (versioned transaction) में रैप (wrap) करता है।
- Simulate transaction: ट्रांजेक्शन फीस खर्च करने से पहले लॉग का पूर्वावलोकन (preview) करने और त्रुटियों को पकड़ने के लिए पहले ट्रांजेक्शन को सिमुलेट (simulate) करता है। आपके ऑन-चेन प्रोग्राम से
msg!आउटपुट यहां दिखाई देता है, जिसे स्क्रिप्ट कैप्चर करती है और प्रदर्शित करती है। - Send the transaction to the Solana network: अंत में, स्क्रिप्ट ऑन-चेन ट्रांजेक्शन भेजती है और इसे Solana एक्सप्लोरर (Solana Explorer) में देखने के लिए एक लिंक प्रिंट करती है।
import { PublicKey } from "@solana/web3.js";
import * as sb from "@switchboard-xyz/on-demand";
import * as anchor from "@coral-xyz/anchor";
import { TX_CONFIG } from "./utils";
(async function main() {
try {
console.log("Getting SOL/USD price from Switchboard...");
// Load keypair and connection from local environment
const { keypair, connection } = await sb.AnchorUtils.loadEnv();
// Create Anchor provider and attach it
const provider = new anchor.AnchorProvider(
connection,
new anchor.Wallet(keypair),
{ commitment: "confirmed" }
);
anchor.setProvider(provider);
// Load the deployed program using Anchor's workspace
const program = anchor.workspace.switchOnDemandPriceFeed;
// ====== 1. DEFINE FEED ACCOUNT ======
// Replace with YOUR feed account address from initializeFeeds.ts output
const feedAccount = new PublicKey("GgGVgSLWAyL9Xf4fGaAQQCkmWetBjX7PCNz8kTK97DKB");
// 2. **BUILD THE INSTRUCTION**
// Build the instruction to call the 'read_price' method on-chain
const ix = await program.methods
.readPrice()
.accounts({
feed: feedAccount,
})
.instruction();
// ====== 3. BUILDING THE TRANSACTION ======
// Wrap in a versioned transaction, with the payer signing
const tx = await sb.asV0Tx({
connection,
ixs: [ix],
payer: keypair.publicKey,
signers: [keypair],
computeUnitPrice: 200_000,
computeUnitLimitMultiple: 1.3,
});
// ====== 4. TRANSACTION SIMULATION ======
// Simulate the transaction to capture logs (including our price output)
const sim = await connection.simulateTransaction(tx, TX_CONFIG);
if (sim.value.logs) {
const priceLog = sim.value.logs.find(log =>
log.includes("SOL/USD price:"));
if (priceLog) {
console.log(`✅ ${priceLog}`);
} else {
console.log("All logs:", sim.value.logs);
}
}
// ====== 5. SENDING THE TRANSACTION TO THE SOLANA NETWORK ======
// Send the actual transaction and log its signature link
const sig = await connection.sendTransaction(tx, TX_CONFIG);
console.log(`📝 Transaction: https://explorer.solana.com/tx/${sig}?cluster=devnet`);
} catch (error) {
console.error("❌ Error getting price:", error);
}
})();
अब, एक टर्मिनल में runfeeds.ts के सक्रिय होने पर, एक दूसरा टर्मिनल खोलें और रीडर (reader) स्क्रिप्ट चलाएँ:
bun run scripts/showPrice.ts
आपको अपने ऑन-चेन प्रोग्राम से सीधे लॉग की गई SOL/USD कीमत देखनी चाहिए, जो इस बात की पुष्टि करता है कि संपूर्ण डेटा पाइपलाइन काम कर रही है।

हमारे उदाहरण में, हम केवल कीमत प्राप्त कर रहे हैं और इसे प्रदर्शित कर रहे हैं। आप ऑन-चेन Switchboard के डेटा का उपयोग करके कुछ भी बना सकते हैं।
निष्कर्ष (Conclusion)
हमने सीखा है कि कई स्रोतों से विश्वसनीय डेटा प्राप्त करने और ऑन-चेन इसका उपयोग करने के लिए Switchboard ओरेकल वर्कफ़्लो के साथ कैसे इंटरैक्ट किया जाए। इस प्रक्रिया में शामिल चरण हैं:
- अपना ऑन-चेन प्रोग्राम लिखना जिसे Switchboard फीड अकाउंट के डेटा के साथ इंटरैक्ट करना चाहिए।
- Switchboard फीड्स को ऑफ-चेन इनिशियलाइज़ और कॉन्फ़िगर करना, जो ऑन-चेन एक फीड अकाउंट पब्लिक की (public key) जेनरेट करता है जिसका उपयोग आपका ऐप फीड रनर डेटा को लगातार अपडेट करने के लिए कर सकता है।
- पब्लिक कुंजी का उपयोग करके फीड से पढ़ने के लिए क्लाइंट स्क्रिप्ट चलाना।
फ़ॉलो-अप अभ्यास (Follow-Up Exercise)
एक प्रोग्राम बनाएं जो प्राइस इम्पैक्ट (price impact) की गणना करता है, यह आकलन करता है कि ट्रेडिंग स्लिपेज (trading slippage) ट्रेडों को कैसे प्रभावित करेगा। फ़ंक्शन को Switchboard प्राइस फीड डेटा का उपयोग करना चाहिए।
- Switchboard से वर्तमान SOL/USD कीमत प्राप्त करें
- ट्रेड के आकार के आधार पर एक टियर्ड स्लिपेज मॉडल (tiered slippage model) लागू करें
- स्लिपेज के बाद वास्तविक निष्पादन (execution) कीमत की गणना करें
- मूल ट्रेड आकार, वर्तमान बाजार मूल्य, परिकलित (calculated) प्राइस इम्पैक्ट और स्लिपेज के बाद प्रभावी निष्पादन कीमत की रिपोर्ट करें।