ویب 3 ایپس کے لیے سرور کے اجزاء اور ایجنٹس
تعارف
زیادہ تر معاملات میں، ایک ڈی سینٹرلائزڈ ایپ سافٹ ویئر تقسیم کرنے کے لیے سرور کا استعمال کرتی ہے، لیکن تمام اصل تعامل کلائنٹ (عام طور پر، ویب براؤزر) اور بلاک چین کے درمیان ہوتا ہے۔
تاہم، کچھ ایسے معاملات ہیں جہاں ایک ایپلی کیشن کو آزادانہ طور پر چلنے والے سرور کے جزو سے فائدہ ہوگا۔ ایسا سرور ایونٹس، اور دیگر ذرائع، جیسے کہ API سے آنے والی درخواستوں کا ٹرانزیکشنز جاری کر کے جواب دینے کے قابل ہوگا۔
ایسے سرور کے لیے کئی ممکنہ کام ہو سکتے ہیں جو وہ انجام دے سکتا ہے۔
-
خفیہ اسٹیٹ کا حامل۔ گیمنگ میں اکثر یہ مفید ہوتا ہے کہ گیم کی تمام معلومات کھلاڑیوں کو دستیاب نہ ہوں۔ تاہم، بلاک چین پر کوئی راز نہیں ہوتے، بلاک چین میں موجود کسی بھی معلومات کا پتہ لگانا کسی کے لیے بھی آسان ہے۔ لہذا، اگر گیم کی اسٹیٹ کے کسی حصے کو خفیہ رکھنا ہے، تو اسے کہیں اور اسٹور کرنا ہوگا (اور ممکنہ طور پر اس اسٹیٹ کے اثرات کی تصدیق زیرو نالج پروفز کا استعمال کرتے ہوئے کی جا سکتی ہے)۔
-
سینٹرلائزڈ اوریکل۔ اگر داؤ پر لگی رقم کافی کم ہے، تو ایک بیرونی سرور جو آن لائن کچھ معلومات پڑھتا ہے اور پھر اسے چین پر پوسٹ کرتا ہے، اسے ایک اوریکل کے طور پر استعمال کرنا کافی ہو سکتا ہے۔
-
ایجنٹ۔ بلاک چین پر اسے فعال کرنے کے لیے کسی ٹرانزیکشن کے بغیر کچھ نہیں ہوتا۔ ایک سرور صارف کی جانب سے کام کر سکتا ہے تاکہ موقع ملنے پر آربٹریج جیسے افعال انجام دے سکے۔
نمونہ پروگرام
آپ github پر (opens in a new tab) ایک نمونہ سرور دیکھ سکتے ہیں۔ یہ سرور اس کنٹریکٹ (opens in a new tab) سے آنے والے ایونٹس کو سنتا ہے، جو Hardhat کے Greeter کا ایک ترمیم شدہ ورژن ہے۔ جب گریٹنگ (greeting) کو تبدیل کیا جاتا ہے، تو یہ اسے واپس تبدیل کر دیتا ہے۔
اسے چلانے کے لیے:
-
ریپوزٹری کو کلون کریں۔
1git clone https://github.com/qbzzt/20240715-server-component.git2cd 20240715-server-component -
ضروری پیکجز انسٹال کریں۔ اگر آپ کے پاس یہ پہلے سے نہیں ہے، تو پہلے Node انسٹال کریں (opens in a new tab)۔
1npm install -
ایک ایسے اکاؤنٹ کی پرائیویٹ کی (private key) کی وضاحت کرنے کے لیے
.envمیں ترمیم کریں جس کے پاس Holesky ٹیسٹ نیٹ پر ETH ہو۔ اگر آپ کے پاس Holesky پر ETH نہیں ہے، تو آپ یہ فوسٹ استعمال کر سکتے ہیں (opens in a new tab)۔1PRIVATE_KEY=0x <private key goes here> -
سرور شروع کریں۔
1npm start -
ایک بلاک ایکسپلورر (opens in a new tab) پر جائیں، اور اس ایڈریس سے مختلف ایڈریس استعمال کرتے ہوئے جس کے پاس پرائیویٹ کی ہے، گریٹنگ میں ترمیم کریں۔ دیکھیں کہ گریٹنگ خود بخود واپس تبدیل ہو جاتی ہے۔
یہ کیسے کام کرتا ہے؟
سرور کا جزو لکھنے کا طریقہ سمجھنے کا سب سے آسان طریقہ یہ ہے کہ نمونے کا لائن بہ لائن جائزہ لیا جائے۔
src/app.ts
پروگرام کی اکثریت src/app.ts (opens in a new tab) میں موجود ہے۔
ضروری آبجیکٹس بنانا
1import {2 createPublicClient,3 createWalletClient,4 getContract,5 http,6 Address,7} from "viem"یہ وہ Viem (opens in a new tab) اینٹیٹیز ہیں جن کی ہمیں ضرورت ہے، فنکشنز اور Address ٹائپ (opens in a new tab)۔ یہ سرور TypeScript (opens in a new tab) میں لکھا گیا ہے، جو JavaScript کی ایک ایکسٹینشن ہے جو اسے اسٹرونگلی ٹائپڈ (strongly typed) (opens in a new tab) بناتی ہے۔
1import { privateKeyToAccount } from "viem/accounts"یہ فنکشن (opens in a new tab) ہمیں پرائیویٹ کی کے مطابق والیٹ کی معلومات، بشمول ایڈریس، تیار کرنے دیتا ہے۔
1import { holesky } from "viem/chains"Viem میں بلاک چین استعمال کرنے کے لیے آپ کو اس کی تعریف (definition) امپورٹ کرنے کی ضرورت ہے۔ اس صورت میں، ہم Holesky (opens in a new tab) ٹیسٹ بلاک چین سے جڑنا چاہتے ہیں۔
1// اس طرح ہم .env میں موجود تعریفات کو process.env میں شامل کرتے ہیں۔2import * as dotenv from "dotenv"3dotenv.config()اس طرح ہم .env کو انوائرنمنٹ میں پڑھتے ہیں۔ ہمیں پرائیویٹ کی کے لیے اس کی ضرورت ہے (بعد میں دیکھیں)۔
1const greeterAddress : Address = "0xB8f6460Dc30c44401Be26B0d6eD250873d8a50A6"2const greeterABI = [3 {4 "inputs": [5 {6 "internalType": "string",7 "name": "_greeting",8 "type": "string"9 }10 ],11 "stateMutability": "nonpayable",12 "type": "constructor"13 },14 .15 .16 .17 {18 "inputs": [19 {20 "internalType": "string",21 "name": "_greeting",22 "type": "string"23 }24 ],25 "name": "setGreeting",26 "outputs": [],27 "stateMutability": "nonpayable",28 "type": "function"29 }30] as constکسی کنٹریکٹ کو استعمال کرنے کے لیے ہمیں اس کے ایڈریس اور اس کے لیے کی ضرورت ہوتی ہے۔ ہم یہاں دونوں فراہم کرتے ہیں۔
JavaScript (اور اس وجہ سے TypeScript) میں آپ کسی کونسٹنٹ (constant) کو نئی ویلیو تفویض نہیں کر سکتے، لیکن آپ اس میں اسٹور کیے گئے آبجیکٹ میں ترمیم کر سکتے ہیں۔ as const لاحقہ استعمال کر کے ہم TypeScript کو بتا رہے ہیں کہ فہرست بذات خود کونسٹنٹ ہے اور اسے تبدیل نہیں کیا جا سکتا۔
1const publicClient = createPublicClient({2 chain: holesky,3 transport: http(),4})ایک Viem پبلک کلائنٹ (opens in a new tab) بنائیں۔ پبلک کلائنٹس کے ساتھ کوئی پرائیویٹ کی منسلک نہیں ہوتی، اور اس لیے وہ ٹرانزیکشنز نہیں بھیج سکتے۔ وہ view فنکشنز (opens in a new tab) کو کال کر سکتے ہیں، اکاؤنٹ بیلنس پڑھ سکتے ہیں، وغیرہ۔
1const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)انوائرنمنٹ ویری ایبلز process.env (opens in a new tab) میں دستیاب ہیں۔ تاہم، TypeScript اسٹرونگلی ٹائپڈ ہے۔ ایک انوائرنمنٹ ویری ایبل کوئی بھی اسٹرنگ (string)، یا خالی ہو سکتا ہے، لہذا انوائرنمنٹ ویری ایبل کی ٹائپ string | undefined ہے۔ تاہم، Viem میں ایک کی (key) کو 0x${string} (یعنی 0x کے بعد ایک اسٹرنگ) کے طور پر بیان کیا گیا ہے۔ یہاں ہم TypeScript کو بتاتے ہیں کہ PRIVATE_KEY انوائرنمنٹ ویری ایبل اس ٹائپ کا ہوگا۔ اگر ایسا نہیں ہے، تو ہمیں رن ٹائم ایرر ملے گا۔
پھر privateKeyToAccount (opens in a new tab) فنکشن اس پرائیویٹ کی کا استعمال کرتے ہوئے ایک مکمل اکاؤنٹ آبجیکٹ بناتا ہے۔
1const walletClient = createWalletClient({2 account,3 chain: holesky,4 transport: http(),5})اس کے بعد، ہم اکاؤنٹ آبجیکٹ کا استعمال کرتے ہوئے ایک والیٹ کلائنٹ (opens in a new tab) بناتے ہیں۔ اس کلائنٹ کے پاس ایک پرائیویٹ کی اور ایک ایڈریس ہوتا ہے، لہذا اسے ٹرانزیکشنز بھیجنے کے لیے استعمال کیا جا سکتا ہے۔
1const greeter = getContract({2 address: greeterAddress,3 abi: greeterABI,4 client: { public: publicClient, wallet: walletClient },5})اب چونکہ ہمارے پاس تمام ضروری چیزیں موجود ہیں، ہم آخر کار ایک کنٹریکٹ انسٹینس (contract instance) (opens in a new tab) بنا سکتے ہیں۔ ہم اس کنٹریکٹ انسٹینس کا استعمال آن چین کنٹریکٹ کے ساتھ بات چیت کرنے کے لیے کریں گے۔
بلاک چین سے پڑھنا
1console.log(`Current greeting:`, await greeter.read.greet())کنٹریکٹ کے وہ فنکشنز جو صرف پڑھنے کے لیے ہیں (view (opens in a new tab) اور pure (opens in a new tab)) وہ read کے تحت دستیاب ہیں۔ اس صورت میں، ہم اسے greet (opens in a new tab) فنکشن تک رسائی حاصل کرنے کے لیے استعمال کرتے ہیں، جو گریٹنگ واپس کرتا ہے۔
JavaScript سنگل تھریڈڈ (single-threaded) ہے، لہذا جب ہم کوئی طویل چلنے والا عمل شروع کرتے ہیں تو ہمیں یہ بتانے کی ضرورت ہوتی ہے کہ ہم اسے غیر ہم آہنگ (asynchronously) طور پر کرتے ہیں (opens in a new tab)۔ بلاک چین کو کال کرنے کے لیے، یہاں تک کہ صرف پڑھنے کے آپریشن کے لیے بھی، کمپیوٹر اور بلاک چین نوڈ کے درمیان ایک راؤنڈ ٹرپ کی ضرورت ہوتی ہے۔ یہی وجہ ہے کہ ہم یہاں بتاتے ہیں کہ کوڈ کو نتیجے کے لیے await کرنے کی ضرورت ہے۔
اگر آپ اس میں دلچسپی رکھتے ہیں کہ یہ کیسے کام کرتا ہے تو آپ اس کے بارے میں یہاں پڑھ سکتے ہیں (opens in a new tab)، لیکن عملی لحاظ سے آپ کو صرف یہ جاننے کی ضرورت ہے کہ اگر آپ کوئی ایسا آپریشن شروع کرتے ہیں جس میں طویل وقت لگتا ہے تو آپ نتائج کا await کرتے ہیں، اور یہ کہ ایسا کرنے والے کسی بھی فنکشن کو async کے طور پر ڈکلیئر کرنا ضروری ہے۔
ٹرانزیکشنز جاری کرنا
1const setGreeting = async (greeting: string): Promise<any> => {یہ وہ فنکشن ہے جسے آپ گریٹنگ کو تبدیل کرنے والی ٹرانزیکشن جاری کرنے کے لیے کال کرتے ہیں۔ چونکہ یہ ایک طویل آپریشن ہے، اس لیے فنکشن کو async کے طور پر ڈکلیئر کیا گیا ہے۔ اندرونی نفاذ کی وجہ سے، کسی بھی async فنکشن کو ایک Promise آبجیکٹ واپس کرنے کی ضرورت ہوتی ہے۔ اس صورت میں، Promise<any> کا مطلب ہے کہ ہم یہ واضح نہیں کرتے کہ Promise میں بالکل کیا واپس کیا جائے گا۔
1const txHash = await greeter.write.setGreeting([greeting])کنٹریکٹ انسٹینس کے write فیلڈ میں وہ تمام فنکشنز ہوتے ہیں جو بلاک چین کی اسٹیٹ پر لکھتے ہیں (وہ جن کے لیے ٹرانزیکشن بھیجنے کی ضرورت ہوتی ہے)، جیسے کہ setGreeting (opens in a new tab)۔ پیرامیٹرز، اگر کوئی ہوں، ایک فہرست کے طور پر فراہم کیے جاتے ہیں، اور فنکشن ٹرانزیکشن کا ہیش واپس کرتا ہے۔
1 console.log(`Working on a fix, see https://eth-holesky.blockscout.com/tx/${txHash}`)2
3 return txHash4}ٹرانزیکشن کے ہیش کی اطلاع دیں (اسے دیکھنے کے لیے بلاک ایکسپلورر کے URL کے حصے کے طور پر) اور اسے واپس کریں۔
ایونٹس کا جواب دینا
1greeter.watchEvent.SetGreeting({watchEvent فنکشن (opens in a new tab) آپ کو یہ بتانے دیتا ہے کہ جب کوئی ایونٹ خارج (emit) ہو تو کون سا فنکشن چلنا چاہیے۔ اگر آپ کو صرف ایک قسم کے ایونٹ کی پرواہ ہے (اس صورت میں، SetGreeting)، تو آپ خود کو اس ایونٹ کی قسم تک محدود رکھنے کے لیے یہ سنٹیکس (syntax) استعمال کر سکتے ہیں۔
1 onLogs: logs => {جب لاگ (log) اندراجات ہوتے ہیں تو onLogs فنکشن کو کال کیا جاتا ہے۔ ایتھیریم میں "لاگ" اور "ایونٹ" عام طور پر ایک دوسرے کی جگہ استعمال ہوتے ہیں۔
1console.log(2 `Address ${logs[0].args.sender} changed the greeting to ${logs[0].args.greeting}`3)متعدد ایونٹس ہو سکتے ہیں، لیکن سادگی کے لیے ہم صرف پہلے والے کی پرواہ کرتے ہیں۔ logs[0].args ایونٹ کے دلائل (arguments) ہیں، اس صورت میں sender اور greeting۔
1 if (logs[0].args.sender != account.address)2 setGreeting(`${account.address} insists on it being Hello!`)3 }4})اگر بھیجنے والا یہ سرور نہیں ہے، تو گریٹنگ کو تبدیل کرنے کے لیے setGreeting کا استعمال کریں۔
package.json
یہ فائل (opens in a new tab) Node.js (opens in a new tab) کنفیگریشن کو کنٹرول کرتی ہے۔ یہ مضمون صرف اہم تعریفوں کی وضاحت کرتا ہے۔
1{2 "main": "dist/index.js",یہ تعریف بتاتی ہے کہ کون سی JavaScript فائل چلانی ہے۔
1 "scripts": {2 "start": "tsc && node dist/app.js",3 },اسکرپٹس مختلف ایپلی کیشن ایکشنز ہیں۔ اس صورت میں، ہمارے پاس صرف ایک start ہے، جو سرور کو مرتب (compile) کرتا ہے اور پھر چلاتا ہے۔ tsc کمانڈ typescript پیکج کا حصہ ہے اور TypeScript کو JavaScript میں مرتب کرتی ہے۔ اگر آپ اسے دستی طور پر چلانا چاہتے ہیں، تو یہ node_modules/.bin میں واقع ہے۔ دوسری کمانڈ سرور کو چلاتی ہے۔
1 "type": "module",JavaScript نوڈ ایپلی کیشنز کی متعدد اقسام ہیں۔ module ٹائپ ہمیں ٹاپ لیول کوڈ میں await رکھنے کی اجازت دیتی ہے، جو اس وقت اہم ہوتا ہے جب آپ سست (اور اس لیے غیر ہم آہنگ) آپریشنز کرتے ہیں۔
1 "devDependencies": {2 "@types/node": "^20.14.2",3 "typescript": "^5.4.5"4 },یہ وہ پیکجز ہیں جو صرف ڈیولپمنٹ کے لیے درکار ہیں۔ یہاں ہمیں typescript کی ضرورت ہے اور چونکہ ہم اسے Node.js کے ساتھ استعمال کر رہے ہیں، اس لیے ہم نوڈ ویری ایبلز اور آبجیکٹس، جیسے کہ process کے لیے ٹائپس بھی حاصل کر رہے ہیں۔ ^<version> نوٹیشن (opens in a new tab) کا مطلب ہے وہ ورژن یا اس سے اعلیٰ ورژن جس میں بریکنگ تبدیلیاں (breaking changes) نہ ہوں۔ ورژن نمبرز کے معنی کے بارے میں مزید معلومات کے لیے یہاں (opens in a new tab) دیکھیں۔
1 "dependencies": {2 "dotenv": "^16.4.5",3 "viem": "2.14.1"4 }5}یہ وہ پیکجز ہیں جو رن ٹائم پر درکار ہوتے ہیں، جب dist/app.js چلایا جاتا ہے۔
نتیجہ
ہم نے یہاں جو سینٹرلائزڈ سرور بنایا ہے وہ اپنا کام کرتا ہے، جو کہ صارف کے لیے ایک ایجنٹ کے طور پر کام کرنا ہے۔ کوئی بھی دوسرا شخص جو چاہتا ہے کہ ڈی ایپ (dapp) کام کرتی رہے اور گیس خرچ کرنے کو تیار ہے، وہ اپنے ایڈریس کے ساتھ سرور کا ایک نیا انسٹینس چلا سکتا ہے۔
تاہم، یہ صرف اس وقت کام کرتا ہے جب سینٹرلائزڈ سرور کے افعال کی آسانی سے تصدیق کی جا سکے۔ اگر سینٹرلائزڈ سرور کے پاس کوئی خفیہ اسٹیٹ کی معلومات ہیں، یا وہ مشکل حساب کتاب چلاتا ہے، تو یہ ایک سینٹرلائزڈ اینٹیٹی ہے جس پر آپ کو ایپلی کیشن استعمال کرنے کے لیے بھروسہ کرنے کی ضرورت ہے، اور بلاک چینز بالکل اسی چیز سے بچنے کی کوشش کرتی ہیں۔ ایک مستقبل کے مضمون میں، میں یہ دکھانے کا ارادہ رکھتا ہوں کہ اس مسئلے سے نمٹنے کے لیے زیرو نالج پروفز کا استعمال کیسے کیا جائے۔
میرے مزید کام کے لیے یہاں دیکھیں (opens in a new tab)۔
صفحہ کی آخری اپ ڈیٹ: ۳ مارچ، ۲۰۲۶