मी गेल्या आठवड्यात .night domains प्रत्यक्षात कसे काम करतात हे समजून घेण्याचा प्रयत्न केला. एखाद्या spec sheet मधून नाही, तर थेट चेनला स्पर्श करणारी काहीतरी गोष्ट तयार करून. याचा निकाल एक छोटा profile viewer आहे. तुम्ही tomin.night सारखे नाव टाईप करता आणि ते थेट ब्लॉकचेनवरून प्रोफाइल रिझॉल्व्ह (resolve) करते. कोणतीही registrar API नाही. कोणतेही auth wall नाही. फक्त एक smart contract आणि काही JavaScript.

यापुढे हे कसे काम करते, मी काय शिकलो आणि त्या दोन विशिष्ट अडथळ्यांबद्दल माहिती दिली आहे ज्यांनी माझा बराच वेळ वाया घालवला.

On-Chain नावांचे महत्त्व का आहे

Midnight वर, .night domains Midnames द्वारे व्यवस्थापित केले जातात. एखाद्या कंपनीच्या मध्यवर्ती सर्व्हरला क्वेरी करण्याऐवजी, तुम्ही थेट चेनवर असलेल्या smart contract मधून माहिती वाचता. रजिस्ट्री स्वतः डोमेन, मालक आणि मालकाने जोडलेली प्रोफाइल फील्ड्स धारण करते. ही डेटा on-chain असल्यामुळे, ओळख (identity) पोर्टेबल असते. तुम्ही ती अशा प्लॅटफॉर्मकडून भाड्याने घेतलेली नसते जी अटी बदलू शकते किंवा सेवा बंद करू शकते. जर तुमच्याकडे कीज (keys) असतील, तर नावावर तुमचे नियंत्रण असते.

हा बदल डेव्हलपर्ससाठी देखील महत्त्वाचा आहे. जेव्हा तुम्ही पारंपारिक DNS सिस्टमवर काम करता, तेव्हा तुम्हाला rate limits, API keys आणि uptime च्या आश्वासनांशी जुळवून घ्यावे लागते. येथे, contract state हाच सत्याचा स्रोत (source of truth) आहे. तुमचे ॲप्लिकेशन तेच वाचते जे इतर सर्व ॲप्लिकेशन्स वाचतात. येथे कोणताही विशेषाधिकार प्राप्त (privileged) API tier नाही.

कोड अत्यंत सोपा आहे

@midnames/sdk सर्व कठीण कामे हाताळते. एखाद्या डोमेनला ॲड्रेस आणि प्रोफाइलमध्ये रिझॉल्व्ह करण्यासाठी फक्त दोन ओळी लागतात:

const provider = createDefaultProvider({ networkId: "mainnet" });
const result = await resolveDomain(provider, "tomin.night");

बस एवढेच. Provider नेटवर्कला लक्ष्य करतो आणि resolver contract शी संवाद साधतो. SDK एक result object परत करते ज्यामध्ये success flag समाविष्ट असतो. जर डोमेन अस्तित्वात नसेल, तर तुम्हाला RPC failures साठी एरर हँडलिंगच्या किंवा try-catch ब्लॉक्सच्या थराओंची गरज नसते. SDK तुम्हाला स्पष्टपणे सांगते की तिथे काहीही नाही. यामुळे UI तयार करणे आश्चर्यकारकपणे सोपे होते. तुम्ही success flag वर आधारित निर्णय घेऊ शकता आणि त्रुटी ही नाव नसण्यामुळे आहे की नोड बंद असल्यामुळे, याचा अंदाज न लावता “not found” स्थिती दाखवू शकता.

तुम्हाला काय मिळते

जेव्हा लूकअप यशस्वी होतो, तेव्हा payload मध्ये दोन महत्त्वाचे भाग असतात.

Target हा तो wallet address आहे ज्याकडे डोमेन निर्देश करते. हे मुख्य उपयुक्तता (utility) आहे. हे एका लांब hex address ला मानवाला वाचता येईल, टाईप करता येईल आणि लक्षात राहतील अशा स्वरूपात बदलते.

Fields मध्ये प्रोफाइल तपशील असतात. मालकाने डोमेनला जे काही जोडले असेल—social links, avatars, text records—ते या स्ट्रक्चरमध्ये असते. ही फील्ड्स एखाद्या कंपनीच्या MongoDB क्लस्टरवर साठवलेली नसतात. ती contract state मधील फील्ड्स आहेत, याचा अर्थ रजिस्ट्री कशी वाचायची हे माहित असलेले कोणतेही ॲप तेच प्रोफाइल रेंडर करू शकते. कोणत्याही डेटाबेस सिंकची (database sync) आवश्यकता नाही.

दोन अडथळे ज्यांनी माझा वेग कमी केला

बिल्डमधील सर्व काही दोन ओळी आणि success flag इतके सोपे नव्हते. मला दोन विशिष्ट अडचणी आल्या ज्या स्पष्ट करणे गरजेचे आहे जेणेकरून तुम्हाला त्या पुन्हा अनुभवाव्या लागणार नाहीत.

Network Mismatch

SDK mainnet आणि preprod दोन्ही वातावरणांना सपोर्ट करते. मी “डोमेन अस्तित्वात नाही” अशा डोमेनचे डीबगिंग करण्यात बराच वेळ घालवला. नाव बरोबर होते. कोड योग्य दिसत होता. Provider चालू होता. समस्या अशी होती की माझे स्क्रिप्ट preprod ला क्वेरी करत होते, तर डोमेन स्वतः mainnet वर नोंदणीकृत होते. त्रुटी अशी दिसत होती की डोमेन गहाळ आहे, पण प्रत्यक्षात ती नेटवर्क कॉन्टेक्स्टची त्रुटी होती.

जर तुम्ही नाव रिझॉल्व्ह करत असाल आणि त्रुटी येत असेल, तर इतर काहीही डीबग करण्यापूर्वी provider settings तपासा. तुमचे networkId ज्या नेटवर्कवर डोमेन प्रत्यक्षात मिंट (mint) केले आहे, त्या नेटवर्कशी जुळते की नाही याची खात्री करा. ही अशी चूक आहे जी नंतर समजल्यावर अगदी साधी वाटते, पण जेव्हा तुम्हाला वाटते की समस्या contract logic मध्ये आहे, तेव्हा ती शोधणे खरोखर कठीण असते.

Serialization Pain

SDK कडून येणारा डेटा मानक (standard) JavaScript नाही. त्यामध्ये BigInt व्हॅल्यूज आणि Map ऑब्जेक्ट्स असतात. जर तुम्ही तो थेट ब्राउझरला पाठवण्यासाठी JSON.stringify मध्ये वापरण्याचा प्रयत्न केला, तर तो एरर देईल किंवा डेटा गहाळ करेल. BigInt चे कोणतेही नेटिव्ह JSON प्रतिनिधित्व नाही आणि Map साध्या ऑब्जेक्ट्सप्रमाणे सिरीयलाईज (serialize) होत नाही.

शेवटी मला एक कस्टम सिरीयलाईझर लिहावा लागला. तो result object मधून फिरत जातो, BigInt व्हॅल्यूज स्ट्रिंगमध्ये रूपांतरित करतो आणि रिस्पॉन्स सर्व्हरवरून बाहेर जाण्यापूर्वी Map इन्स्टन्सचे नियमित ऑब्जेक्ट्समध्ये रूपांतर करतो. जर तुम्ही Midnight डेटा फ्रंटएंडला देण्यासाठी API तयार करत असाल, तर या पायरीचे नियोजन आधीच करा. केवळ ते JavaScript आहे म्हणून SDK आउटपुट लगेच फ्रंटएंडसाठी तयार असेल असे समजू नका.

आर्किटेक्चर (The Architecture)

मी हे स्टॅक मुद्दाम साधे ठेवले आहे. बॅकएंड हे Express चालवणारे एक Node सर्व्हर आहे. ते @midnames/sdk इम्पोर्ट करते, रिझोल्यूशन लॉजिक चालवते, सिरीयलायझेशनमधील कसरत हाताळते आणि स्वच्छ JSON सर्व्ह करते. फ्रंटएंड हे साधे HTML आणि व्हॅनिला JavaScript आहे. कोणताही बिल्ड स्टेप नाही. कोणताही फ्रेमवर्क नाही. कोणताही वॉलेट अडॅप्टर नाही.

काही व्यावहारिक कारणांमुळे मी ब्राउझरऐवजी बॅकएंडवर SDK चालवण्याचा निर्णय घेतला. यामुळे क्लायंटपासून कोणताही प्रोव्हायडर कॉन्फिगरेशन दूर राहतो, सिरीयलायझेशनमधील गोंधळ सुधारण्यासाठी मला एकच जागा मिळते आणि याचा अर्थ असा की फ्रंटएंडला फक्त डेटा मिळवणे (fetch) आणि तो रेंडर करणे आवश्यक आहे.

मला सर्वात जास्त आश्चर्य वाटलेली गोष्ट म्हणजे: नाव रिझॉल्व्ह करणे ही एक पब्लिक 'रीड' (read) प्रक्रिया आहे. तुम्हाला वॉलेट कनेक्शनची गरज नाही. तुम्हाला सिग्नेचरची गरज नाही. वापरकर्त्याला कोणत्याही गोष्टीने लॉग इन करण्याची गरज नाही. जर डोमेन अस्तित्वात असेल, तर कॉन्ट्रॅक्टची स्थिती (state) विचारणाऱ्या कोणालाही पाहता येते. हे सामान्य web3 फ्लोपेक्षा वेगळे आहे, जिथे प्रत्येक इंटरॅक्शनची सुरुवात "connect wallet" ने होते. Midnight वर ओळख (identity) वाचणे हे एखाद्या सार्वजनिक वेबसाइटला भेट देण्याइतकेच परवानगीमुक्त (permissionless) आहे.

मुख्य निष्कर्ष

हे व्ह्यूअर बनवताना मला आठवण झाली की ब्लॉकचेन डेव्हलपमेंटमधील सर्वात कठीण भाग सहसा ब्लॉकचेन स्वतः नसतो. Midnight ने आधीच कठीण समस्या सोडवली आहे: कोणत्याही मध्यवर्ती डेटाबेसशिवाय लोकांना त्यांचे नाव आणि प्रोफाइल स्वतःच्या मालकीचे ठेवू देणे. बिल्डरच्या दृष्टिकोनातून, कठीण भाग म्हणजे मी कोणत्या नेटवर्कला पॉइंट करत आहे हे लक्षात ठेवणे आणि डेटा टाइप्स स्वच्छ करण्यासाठी एक हेल्पर फंक्शन लिहिणे हा होता.

प्रोटोकॉल तुम्हाला पोर्टेबल आयडेंटिटी (portable identity) देतो. डेव्हलपर म्हणून तुमचे काम फक्त ते योग्यरित्या वाचणे आणि वापरकर्त्याच्या मार्गात न येणे हे आहे. आर्किटेक्चर साधे ठेवा, चेन-फेसिंग लॉजिकला UI पासून वेगळे करा आणि पब्लिक रीड्सना ते जे आहेत तसेच समजा: साध्या डेटाबेस क्वेरीज ज्या फक्त एका डिस्ट्रिब्युटेड लेजरवर (distributed ledger) आहेत.

जर तुम्हाला कोड पाहायचा असेल किंवा तो स्वतः चालवायचा असेल, तर संपूर्ण सोर्स https://github.com/tomiin/midnames-profile-viewer वर उपलब्ध आहे.