इस पूरी सीरीज़ में, हमने Solana प्रोग्राम बनाने के लिए Anchor फ्रेमवर्क का उपयोग किया है। यह ट्यूटोरियल आपको सिखाता है कि उन्हें Anchor के बिना नेटिव Rust में कैसे लिखा जाए।
ऐसा करने के कई कारण हो सकते हैं, जैसे:
- लो-लेवल कंट्रोल: आपके पास इस बात का कंट्रोल होता है कि आप डेटा को कैसे सीरियलाइज़ (serialize) और डीसीरियलाइज़ (deserialize) करते हैं, अकाउंट्स को वैलिडेट करते हैं, और अपने प्रोग्राम लॉजिक को स्ट्रक्चर करते हैं
- परफॉरमेंस: नेटिव Rust में, जब आवश्यकता न हो तो आप साधारण ऑपरेशन्स के लिए Anchor के सीरियलाइज़ेशन, डीसीरियलाइज़ेशन और अकाउंट वैलिडेशन स्टेप्स को छोड़ सकते हैं, जिसके परिणामस्वरूप कम कंप्यूट यूनिट (compute unit) का उपयोग होगा
- छोटा बाइनरी साइज़: Anchor का कोई ओवरहेड नहीं (अतिरिक्त Rust मैक्रोज़ और डिपेंडेंसीज़) होने का मतलब है कि डिप्लॉय किए गए प्रोग्राम छोटे होंगे
- सीखना: इसके पीछे की कार्यप्रणाली को समझने से आप एक बेहतर Solana डेवलपर बनते हैं
अब तक, हम अपने प्रोग्राम बनाने के लिए Anchor का उपयोग करते आ रहे हैं और हमने अपने फंक्शन्स को कुछ इस तरह लिखा है:
#[program]
pub mod my_program {
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// function logic
Ok(())
}
pub fn update(ctx: Context<Update>, value: u64) -> Result<()> {
// function logic
Ok(())
}
}
#[program] एट्रिब्यूट मैक्रो पर्दे के पीछे अपने आप एक प्रोग्राम एंट्रीपॉइंट (entrypoint) जनरेट करता है। यह एंट्रीपॉइंट आने वाले सभी इंस्ट्रक्शन्स को प्राप्त करता है और क्लाइंट द्वारा पास किए गए इंस्ट्रक्शन डेटा के आधार पर उन्हें आपके अलग-अलग फंक्शन्स (initialize, update, आदि) में भेजता है। नेटिव Rust में, हम एंट्रीपॉइंट बनाने और डिस्पैचिंग (dispatching) को मैन्युअल रूप से संभालने के लिए Solana SDK से entrypoint! मैक्रो का उपयोग करेंगे।
Solana में Entrypoint क्या है?
एक एंट्रीपॉइंट को अपने Solana प्रोग्राम का “मुख्य दरवाज़ा (front door)” समझें। Ethereum में, हर पब्लिक फंक्शन का होना कई मुख्य दरवाज़े होने जैसा है: EVM पब्लिक फंक्शन्स जैसे ERC20 में transfer(), approve(), या किसी भी अन्य पब्लिक फंक्शन को सीधे कॉल कर सकता है। Solana अलग तरह से काम करता है। Solana प्रोग्राम्स में ठीक एक ही मुख्य दरवाज़ा (एंट्रीपॉइंट) होता है जो क्लाइंट्स से आने वाली सभी कॉल्स को हैंडल करता है।
एंट्रीपॉइंट फंक्शन को सीधे हमारे द्वारा नहीं लिखा जाता है। यह कंपाइल टाइम (compile time) पर Solana SDK द्वारा प्रदान किए गए एक entrypoint! मैक्रो द्वारा जनरेट किया जाता है (जैसा कि हम बाद में देखेंगे जब हम अपना नेटिव प्रोग्राम लिखेंगे)। जब कोई क्लाइंट किसी Solana प्रोग्राम को इनवोक (invoke) करता है, तो रनटाइम (runtime) एंट्रीपॉइंट को कॉल करता है, जो आने वाले इंस्ट्रक्शन को डीसीरियलाइज़ करता है और उसे एक इंस्ट्रक्शन प्रोसेसर (instruction processor) फंक्शन में पास कर देता है जिसे हम परिभाषित करते हैं (इस पर हम आगे चर्चा करेंगे)। इंस्ट्रक्शन प्रोसेसर इंस्ट्रक्शन्स को सही प्रोग्राम फंक्शन पर रूट (route) कर सकता है, अकाउंट वैलिडेशन कर सकता है, या सीधे बिज़नेस लॉजिक को हैंडल कर सकता है।
इसलिए, entrypoint! मैक्रो उस सभी लो-लेवल कोड को हैंडल करता है जिसकी रनटाइम को आपके प्रोग्राम को इनवोक करने, इंस्ट्रक्शन पैरामीटर्स को डीसीरियलाइज़ करने और उन्हें इंस्ट्रक्शन प्रोसेसर तक फॉरवर्ड करने के लिए आवश्यकता होती है। यह आपको सामान्य Rust फंक्शन्स और टाइप्स का उपयोग करके अपने प्रोग्राम का लॉजिक लिखने की सुविधा देता है, जबकि मैक्रो Solana के साथ इंटरफेस को मैनेज करता है।
इंस्ट्रक्शन प्रोसेसर
नेटिव Rust Solana प्रोग्राम्स में, हमें एक इंस्ट्रक्शन प्रोसेसर परिभाषित करने की आवश्यकता होती है: एक ऐसा फंक्शन जो आने वाले इंस्ट्रक्शन्स को प्रोसेस करता है। जब कोई क्लाइंट आपके प्रोग्राम को कोई इंस्ट्रक्शन भेजता है, तो Solana रनटाइम आपके प्रोग्राम के एंट्रीपॉइंट को इनवोक करता है, जो फिर टॉप-लेवल इंस्ट्रक्शन पैरामीटर्स को डीसीरियलाइज़ करता है और उन्हें आपके इंस्ट्रक्शन प्रोसेसर फंक्शन में पास कर देता है। इस तरह आपका प्रोग्राम प्रत्येक इंस्ट्रक्शन को प्राप्त करता है और प्रोसेस करता है।
इस इंस्ट्रक्शन प्रोसेसर का एक स्टैंडर्ड फंक्शन सिग्नेचर होता है:
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult
process_instruction फंक्शन के तीन पैरामीटर्स हैं:
program_id: आपके प्रोग्राम का एड्रेसaccounts: वे सभी अकाउंट्स जिन्हें आपके प्रोग्राम को पढ़ने या लिखने की आवश्यकता हैinstruction_data: रॉ बाइट्स (Raw bytes) जिनमें आपके प्रोग्राम के लिए सीरियलाइज़्ड इंस्ट्रक्शन डेटा होता है
ProgramResult रिटर्न टाइप Result<(), ProgramError> के लिए एक टाइप एलियास (type alias) है, जहाँ ProgramError एक ईनम (enum) है जो उन संभावित एरर्स जिन्हें एक Solana प्रोग्राम वापस कर सकता है, को परिभाषित करता है।
Anchor में, रॉ (raw) process_instruction पैरामीटर्स और रिटर्न टाइप छिपे होते हैं। इसके बजाय, आपके हैंडलर (handler) को पूरी तरह से डीसीरियलाइज़ किए गए अकाउंट्स और इंस्ट्रक्शन डेटा वाला एक Context<T> प्राप्त होता है, जिस पर ऑटोमैटिक वैलिडेशन लागू होता है, ताकि आप रॉ बाइट स्लाइस (raw byte slices) के बजाय सीधे टाइप्ड स्ट्रक्ट्स (typed structs) के साथ काम कर सकें।
आप इस इंस्ट्रक्शन प्रोसेसर फंक्शन का कोई भी नाम रख सकते हैं, लेकिन Solana इकोसिस्टम का कन्वेंशन process_instruction है। यही वह फंक्शन है जिसे आप entrypoint! मैक्रो में पास करते हैं (जैसा कि हमने पहले चर्चा की थी)।
अब चलिए entrypoint! मैक्रो के ज़रिए जुड़े हुए एक इंस्ट्रक्शन प्रोसेसर के साथ एक Solana प्रोग्राम लिखते हैं और उसे एग्जीक्यूट करते हैं। यह व्यवहार में कैसे काम करता है, यह देखने के लिए हम एक TypeScript क्लाइंट के साथ अपने प्रोग्राम का परीक्षण (test) करेंगे।
अपना Solana प्रोग्राम बनाना
प्रोजेक्ट सेटअप
हम एक इंस्ट्रक्शन प्रोसेसर के साथ एक साधारण Solana प्रोग्राम बनाएंगे जो बेसिक अर्थमैटिक (गणित) करता है और परिणाम को लॉग करता है। यह दर्शाएगा कि एंट्रीपॉइंट और इंस्ट्रक्शन प्रोसेसर व्यवहार में एक साथ कैसे काम करते हैं।
यदि आप पिछले ट्यूटोरियल्स का पालन कर रहे हैं तो आपके लोकल सिस्टम पर Rust और Solana इंस्टॉल होना चाहिए। यदि नहीं, तो कृपया Solana Hello World (Installation and Troubleshooting) देखें।
अब चलिए अपने प्रोग्राम के लिए एक नई डायरेक्टरी बनाते हैं और निम्नलिखित कमांड्स रन करके इसे इनिशियलाइज़ करते हैं:
mkdir solana-entrypoint-tutorial # Create a new directory for our program
cd solana-entrypoint-tutorial # Change into the directory
cargo init --lib # Initialize a new Rust library
इसके बाद, प्रोजेक्ट के Cargo.toml को इस तरह दिखने के लिए अपडेट करें:
[package]
name = "solana-entrypoint-tutorial"
version = "0.1.0"
edition = "2021" # added
## NEWLY ADDED ##
[lib]
crate-type = ["cdylib", "lib"]
[dependencies]
solana-program = "3.0.0"
हमने दो कॉन्फ़िगरेशन्स जोड़े हैं:
crate-type = ["cdylib", "lib"]: Rust को बताता है कि हमारी लाइब्रेरी को एक डायनामिक लाइब्रेरी के रूप में कंपाइल करे जिसे Solana लोड कर सकता हैsolana-program = "3.0.0": कोर Solana प्रोग्राम लाइब्रेरी जो ऑन-चेन प्रोग्राम्स के लिए आवश्यक सभी टाइप्स और फंक्शन्स प्रदान करती है
अब चलिए अपना प्रोग्राम बनाते हैं।
अपना प्रोग्राम कोड लिखना
हम एक ऐसे इंस्ट्रक्शन प्रोसेसर के साथ शुरुआत करेंगे जो बेसिक अर्थमैटिक करता है और परिणाम को लॉग करता है।
Anchor में, आप अपने #[program] मॉड्यूल के अंदर बेसिक मैथ करने वाले फंक्शन को कुछ इस तरह परिभाषित कर सकते हैं:
#[program]
pub mod some_program {
pub fn do_math(ctx: Context<DoMath>) -> Result<()> {
let result = 5 + 3;
msg!("Result: {}", result);
Ok(())
}
}
लेकिन नेटिव Rust Solana प्रोग्राम्स के लिए, हम एक इंस्ट्रक्शन प्रोसेसर परिभाषित करते हैं और इसे entrypoint! मैक्रो का उपयोग करके प्रोग्राम के एंट्रीपॉइंट से जोड़ते हैं। हालाँकि आप अन्य पब्लिक फंक्शन्स परिभाषित कर सकते हैं, लेकिन उन्हें इंस्ट्रक्शन प्रोसेसर से ही कॉल किया जाना चाहिए, क्योंकि सारा एग्जीक्यूशन वहीं से शुरू होता है।
entrypoint! मैक्रो भारी काम (heavy lifting) करता है: यह वास्तविक एंट्रीपॉइंट कोड जनरेट करता है जिसे Solana रनटाइम कॉल करता है, आने वाले डेटा को डीसीरियलाइज़ करता है, और इसे आपके इंस्ट्रक्शन प्रोसेसर फंक्शन को फॉरवर्ड करता है। इस तरह, आप अपना बिज़नेस लॉजिक अपने इंस्ट्रक्शन प्रोसेसर में लिखते हैं जबकि मैक्रो लो-लेवल एंट्रीपॉइंट सेटअप को संभालता है।
अब src/lib.rs के डिफ़ॉल्ट कोड को निम्नलिखित कोड से बदल दें। कोड में, हम:
- अपने प्रोग्राम के लिए आवश्यक Solana प्रोग्राम डिपेंडेंसीज़ को इम्पोर्ट करते हैं:
AccountInfo,entrypoint,ProgramResult,msg, औरPubkey। process_instructionको अपने प्रोग्राम के इंस्ट्रक्शन प्रोसेसर के रूप में जोड़ने के लिएentrypoint!मैक्रो का उपयोग करते हैं।- स्टैंडर्ड तीन पैरामीटर्स (सभी अंडरस्कोर के साथ क्योंकि हम अभी तक उनका उपयोग नहीं कर रहे हैं) के साथ
process_instructionनामक एक इंस्ट्रक्शन प्रोसेसर फंक्शन परिभाषित करते हैं। - दो हार्डकोड किए गए नंबर्स (5 + 3) को जोड़कर साधारण अर्थमैटिक करते हैं।
- कैलकुलेशन के परिणाम को ट्रांज़ैक्शन लॉग्स में लॉग करने के लिए
msg!मैक्रो का उपयोग करते हैं। - सफल एग्जीक्यूशन को इंगित करने के लिए
Ok(())रिटर्न करते हैं।
use solana_program::{
account_info::AccountInfo,
entrypoint,
entrypoint::ProgramResult,
msg,
pubkey::Pubkey,
};
entrypoint!(process_instruction); // Register process_instruction as our instruction processor
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
let a: u64 = 5;
let b: u64 = 3;
let result = a + b;
msg!("Calculating {} + {} = {}", a, b, result);
Ok(())
}
entrypoint! मैक्रो को समझना
पहले, हमने बताया था कि Solana आपके इंस्ट्रक्शन प्रोसेसर को प्रोग्राम के एंट्रीपॉइंट से जोड़ने के लिए एक entrypoint! मैक्रो प्रदान करता है।
ऊपर दिए गए कोड में, entrypoint! मैक्रो तीन काम करता है:
- वह वास्तविक एंट्रीपॉइंट फंक्शन जनरेट करता है जिसे Solana रनटाइम कॉल करता है
- रनटाइम इनपुट (जिसमें प्रोग्राम आईडी, अकाउंट्स और इंस्ट्रक्शन डेटा होता है) को डीसीरियलाइज़ करता है
- डीसीरियलाइज़ किए गए पैरामीटर्स के साथ आपके इंस्ट्रक्शन प्रोसेसर फंक्शन (
process_instruction) को कॉल करता है
जब आप entrypoint!(process_instruction); लिखते हैं, तो यह विस्तृत होकर ऐसे कोड में बदल जाता है जो कुछ इस तरह दिखता है:
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64 {
// Deserialize the raw input from the Solana runtime
// `input` is raw runtime memory (just bytes)
let (program_id, accounts, instruction_data) = unsafe { deserialize(input) };
// Call your instruction processor function with the deserialized data
// program_id: &Pubkey
// accounts: Vec<AccountInfo>
// instruction_data: &[u8]
match process_instruction(program_id, &accounts, instruction_data) {
Ok(()) => 0, // Return 0 for success
Err(error) => error.into(), // Return error code on failure
}
}
यह जनरेटेड फंक्शन Solana रनटाइम और आपके Rust कोड के बीच का पुल (bridge) है। रनटाइम इस एंट्रीपॉइंट को मेमोरी में इंस्ट्रक्शन डेटा के पॉइंटर (input: *mut u8) के साथ कॉल करता है। यह पॉइंटर एक मेमोरी लोकेशन की ओर इशारा करता है जिसमें सीरियलाइज़्ड इंस्ट्रक्शन पैरामीटर्स (प्रोग्राम आईडी, अकाउंट्स, और इंस्ट्रक्शन डेटा) रॉ बाइट्स के रूप में होते हैं। deserialize(input) फंक्शन इस मेमोरी लोकेशन से पढ़ता है और उन बाइट्स को तीन वैल्यूज़ में बदल देता है:
program_id(पहले से ही एक&Pubkey),accounts(एकVec<AccountInfo>), औरinstruction_data(पहले से ही एक&[u8])।
Solana SDK में, deserialize फंक्शन का सिग्नेचर इस प्रकार परिभाषित किया गया है:
pub unsafe fn deserialize<'a>(input: *mut u8) -> (&Pubkey, Vec<AccountInfo>, &[u8])
process_instruction(program_id, &accounts, instruction_data) कॉल में, केवल accounts को एक & की आवश्यकता होती है।
ऐसा इसलिए है क्योंकि deserialize पहले से ही program_id और instruction_data को रिफरेन्सेस के रूप में लौटाता है (&Pubkey और &[u8]—जैसा कि हम ऊपर सिग्नेचर में देखते हैं), लेकिन accounts को Vec<AccountInfo> के रूप में लौटाता है।
जनरेट किए गए कोड में, &accounts एक &Vec<AccountInfo> बनाता है, जिसे Rust स्वचालित रूप से &[AccountInfo] स्लाइस में बदल देता है जिसकी process_instruction अपेक्षा करता है।
entrypoint! मैक्रो आपको process_instruction को लागू करने पर ध्यान केंद्रित करने देता है जबकि मैक्रो Solana रनटाइम के साथ इंटरैक्शन को संभालता है। आप एंट्रीपॉइंट मैक्रो का पूरा इम्प्लीमेंटेशन यहाँ देख सकते हैं।
दूसरी ओर, Anchor में, #[program] एट्रिब्यूट स्वचालित रूप से एंट्रीपॉइंट जनरेट करता है, इंस्ट्रक्शन डेटा और अकाउंट्स को डीसीरियलाइज़ करता है, और उपयुक्त फंक्शन्स में इंस्ट्रक्शन्स को डिस्पैच करता है।
अब, चलिए वास्तव में अपने प्रोग्राम को कंपाइल और डिप्लॉय करते हैं ताकि हम इसका परीक्षण कर सकें।
प्रोग्राम को बिल्ड और डिप्लॉय करना
प्रोग्राम को बिल्ड और डिप्लॉय करने के लिए, निम्नलिखित कमांड्स रन करें:
cargo build-sbf --tools-version v1.52
solana-test-validator # in another terminal
solana program deploy target/deploy/solana_entrypoint_tutorial.so
यहाँ बताया गया है कि प्रत्येक कमांड क्या करता है:
cargo build-sbf: Solana रनटाइम के लिए हमारे Rust प्रोग्राम को बिल्ड करता है और एकtarget/deploy/फ़ोल्डर बनाता है जिसमें एक.soफ़ाइल (शेयर्ड ऑब्जेक्ट) होती है जो हमारा कंपाइल्ड प्रोग्राम है। Anchor के बिल्ड कमांड के विपरीत, यह IDL जनरेट नहीं करता है या Anchor के डिस्क्रिमिनेटर्स (discriminators) और ऑटोमैटिक वैलिडेशन कोड को शामिल नहीं करता है। इसके परिणामस्वरूप छोटी बाइनरीज़ बनती हैं क्योंकि इसमें कोई फ्रेमवर्क ओवरहेड नहीं होता है।--tools-version v1.52फ़्लैग बिल्ड के लिए Solana प्लेटफ़ॉर्म टूलचेन को पिन कर देता है। यह एक कम्पैटिबल Rust और Cargo वर्ज़न सुनिश्चित करता है और बेमेल या पुराने टूलिंग से होने वाली समस्याओं से बचाता है।solana-test-validator: पिछले ट्यूटोरियल्स की तरह, हम परीक्षण के लिए एक लोकल Solana वैलिडेटर शुरू करने के लिए इसका उपयोग करते हैं (इसे एक अलग टर्मिनल में रन करें)solana program deploy: बिल्ड कमांड द्वारा बनाई गई.soफ़ाइल को लेता है और इसे लोकल वैलिडेटर पर डिप्लॉय करता है
लोकल वैलिडेटर नोड पर डिप्लॉय करने के बाद, आपको कुछ इस तरह दिखाई देना चाहिए।

डिप्लॉय आउटपुट से Program ID को कॉपी करें, परीक्षण के लिए आपको इसकी आवश्यकता होगी।
यदि आपको बिल्ड एरर (build error) मिलता है, तो सुनिश्चित करें कि आपका Solana टूलचेन अप-टू-डेट है, इसके लिए यह कमांड रन करें: curl --proto '=https' --tlsv1.2 -sSfL https://solana-install.solana.workers.dev | bash
परीक्षण चरण 1: बेसिक अर्थमैटिक और लॉगिंग
अब अपने बेसिक एंट्रीपॉइंट प्रोग्राम का परीक्षण करने के लिए एक TypeScript क्लाइंट बनाते हैं। सबसे पहले, हमें क्लाइंट एनवायरनमेंट सेटअप करना होगा, फिर हम क्लाइंट कोड जोड़ेंगे।
निम्नलिखित कमांड्स के साथ अपने प्रोजेक्ट के अंदर TypeScript क्लाइंट सेटअप करें:
mkdir client && cd client
npm init -y
npm install @solana/web3.js typescript ts-node @types/node
client/package.json में डिफ़ॉल्ट test स्क्रिप्ट को निम्नलिखित से बदलें:
"scripts": {
"test": "ts-node client.ts"
},
यह हमें npm run test के साथ अपने TypeScript क्लाइंट को रन करने की सुविधा देता है, क्योंकि npm init द्वारा जनरेट की गई डिफ़ॉल्ट टेस्ट स्क्रिप्ट TypeScript कोड का समर्थन नहीं करती है।
एक client/tsconfig.json बनाएँ और यह जोड़ें:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"types": [
"node"
]
},
"include": [
"*.ts"
]
}
यह सेटअप हमें npm run test के साथ क्लाइंट को रन करने की अनुमति देगा।
अब एक client/client.ts फ़ाइल बनाएँ और निम्नलिखित कोड जोड़ें। इस कोड में, हम:
- कनेक्शन्स, ट्रांज़ैक्शन्स, और की-पेयर्स (keypairs) बनाने के लिए आवश्यक Solana web3.js डिपेंडेंसीज़ को इम्पोर्ट करते हैं।
- पोर्ट 8899 (
solana-test-validatorके लिए डिफ़ॉल्ट पोर्ट) पर चल रहे लोकल Solana वैलिडेटर के साथ एक कनेक्शन सेटअप करते हैं। - एक
testBasicEntrypointफंक्शन बनाते हैं जो ट्रांज़ैक्शन का भुगतान करने के लिए एक नया की-पेयर जनरेट करता है। - ट्रांज़ैक्शन फीस को फंड करने के लिए SOL के एयरड्रॉप (airdrop) का अनुरोध करते हैं।
- बिना किसी अकाउंट और बिना किसी इंस्ट्रक्शन डेटा के एक
TransactionInstructionबनाते हैं (चूँकि हमारा प्रोग्राम अभी तक उनका उपयोग नहीं करता है)। - ट्रांज़ैक्शन को बिल्ड करते हैं और अपने प्रोग्राम में भेजते हैं।
- वेरिफिकेशन के लिए ट्रांज़ैक्शन सिग्नेचर को लॉग करते हैं।
import {
Connection,
Keypair,
LAMPORTS_PER_SOL,
PublicKey,
Transaction,
TransactionInstruction,
sendAndConfirmTransaction,
} from '@solana/web3.js';
import { Buffer } from 'buffer';
// === REPLACE WITH YOUR ACTUAL PROGRAM ID === //
const PROGRAM_ID = new PublicKey('7x8574zHWf6cRQJrE5T3cfUdcgDi6Vt6q7HhLfHkHZQ4'); // Replace with your actual program ID
const connection = new Connection('http://localhost:8899', 'confirmed');
async function testBasicEntrypoint() {
const payer = Keypair.generate();
// Airdrop some SOL to pay for the transaction
await connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL);
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for airdrop
// Create a instruction that calls our program
const instruction = new TransactionInstruction({
keys: [], // keys is the account metadata array; no accounts needed for this simple example
programId: PROGRAM_ID,
data: Buffer.alloc(0), // No instruction data needed
});
const transaction = new Transaction().add(instruction);
console.log('Calling our program...');
const signature = await sendAndConfirmTransaction(connection, transaction, [payer]);
console.log(`Transaction confirmed: ${signature}`);
}
testBasicEntrypoint().catch(console.error);
PROGRAM_ID में दिए गए प्रोग्राम आईडी को अपने वास्तविक प्रोग्राम आईडी से बदलें।
परीक्षण रन करने से पहले, यह सुनिश्चित करें कि लोकल वैलिडेटर अभी भी चल रहा है और प्रोग्राम डिप्लॉय हो चुका है। फिर हमारे प्रोग्राम लॉग को देखने के लिए एक नए टर्मिनल में solana logs रन करें।
अब इसके साथ परीक्षण (test) रन करें:
cd client
npm run test
आपको कुछ इस तरह का प्रोग्राम लॉग दिखाई देना चाहिए:

हमारा प्रोग्राम सफलतापूर्वक एग्जीक्यूट हो गया। ध्यान दें कि Anchor की तुलना में यह कितना आसान है, इसमें कोई अकाउंट वैलिडेशन या इंस्ट्रक्शन पार्सिंग नहीं है।
यह लेख Solana development पर एक ट्यूटोरियल सीरीज़ का हिस्सा है।