Spędziłem ostatni tydzień, próbując zrozumieć, jak właściwie działają domeny .night. Nie na podstawie specyfikacji, ale budując coś, co bezpośrednio dotyka łańcucha. Wynikiem jest mały podgląd profilu. Wpisujesz nazwę, np. tomin.night, a ona rozwiązuje profil bezpośrednio z blockchaina. Żadnego API rejestratora. Żadnej bariery uwierzytelniania. Tylko smart kontrakt i trochę JavaScriptu.

Poniżej opisuję, jak to działa, czego się nauczyłem i dwie konkretne pułapki, które pochłonęły moje popołudnie.

Dlaczego nazwy on-chain mają znaczenie

Na Midnight domeny .night są zarządzane przez Midnames. Zamiast odpytywać centralny serwer firmy, czytasz dane bezpośrednio ze smart kontraktu, który żyje na łańcuchu. Sam rejestr przechowuje domenę, właściciela i wszelkie pola profilu, które właściciel do niej przypisał. Ponieważ te dane są on-chain, tożsamość jest przenośna. Nie wynajmujesz jej od platformy, która może zmienić warunki lub nagle wyłączyć usługę. Jeśli kontrolujesz klucze, kontrolujesz nazwę.

Ta zmiana jest istotna również dla programistów. Budując w oparciu o tradycyjny system DNS, mierzysz się z limitami zapytań (rate limits), kluczami API i obietnicami uptime'u. Tutaj stan kontraktu jest źródłem prawdy (source of truth). Twoja aplikacja odczytuje go w taki sam sposób, w jaki każda inna aplikacja go odczytuje. Nie ma uprzywilejowanych poziomów dostępu do API.

Kod jest niemal zbyt prosty

@midnames/sdk wykonuje najtrudniejszą pracę. Rozwiązanie domeny do adresu i profilu zajmuje dokładnie dwie linie:

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

To wszystko. Provider celuje w sieć, a resolver rozmawia z kontraktem. SDK zwraca obiekt wyniku, który zawiera flagę sukcesu. Jeśli domena nie istnieje, nie potrzebujesz warstw obsługi błędów ani bloków try-catch wokół błędów RPC. SDK czysto informuje Cię, że nic tam nie ma. Dzięki temu budowanie interfejsów użytkownika (UI) jest zaskakująco przyjemne. Możesz rozgałęzić logikę na podstawie flagi sukcesu i wyświetlić stan „nie znaleziono”, nie zgadując, czy błąd wynikał z braku nazwy, czy z martwego węzła.

Co otrzymujesz z powrotem

Gdy wyszukiwanie zakończy się sukcesem, payload zawiera dwie istotne części.

Target to adres portfela, na który wskazuje domena. To stanowi główną użyteczność. Zamienia długi adres szesnastkowy na coś, co człowiek może przeczytać, wpisać i zapamiętać.

Fields przechowują szczegóły profilu. Wszystko, co właściciel przypisał do domeny — linki społecznościowe, awatary, rekordy tekstowe — znajduje się w tej strukturze. Te pola nie są przechowywane w klastrze MongoDB jakiejś firmy. Są to pola w stanie kontraktu, co oznacza, że każda aplikacja, która wie, jak czytać rejestr, może wyrenderować ten sam profil. Nie jest wymagana synchronizacja baz danych.

Dwie pułapki, które mnie spowolniły

Nie wszystko w procesie budowania sprowadzało się do dwóch linii i flagi sukcesu. Natknąłem się na dwie konkretne przeszkody, o których warto wspomnieć, abyś nie powtarzał moich błędów.

Niezgodność sieci

SDK obsługuje zarówno środowisko mainnet, jak i preprod. Spędziłem sporo czasu na debugowaniu domeny, która „nie istniała”. Nazwa była poprawna. Kod wyglądał właściwie. Provider działał. Problem polegał na tym, że mój skrypt odpytywał preprod, podczas gdy sama domena była zarejestrowana na mainnet. Błąd wyglądał jak brak domeny, ale w rzeczywistości był to brak kontekstu sieci.

Jeśli rozwiązujesz nazwę i otrzymujesz błąd, sprawdź ustawienia providera, zanim zaczniesz debugować cokolwiek innego. Upewnij się, że Twój networkId zgadza się z siecią, w której domena została faktycznie wyemitowana. To rodzaj błędu, który wydaje się oczywisty z perspektywy czasu, ale jest naprawdę trudny do zauważenia, gdy zakładasz, że problem leży w logice kontraktu.

Problemy z serializacją

Dane zwracane przez SDK nie są standardowym JavaScriptem. Zawierają wartości BigInt oraz obiekty Map. Jeśli spróbujesz przekazać je bezpośrednio do JSON.stringify, aby wysłać do przeglądarki, rzucą błędem lub po cichu zgubią dane. BigInt nie ma natywnej reprezentacji JSON, a Map nie serializuje się w taki sam sposób jak zwykłe obiekty.

Skończyło się na tym, że napisałem własny serializer. Przechodzi on przez obiekt wyniku, konwertuje wartości BigInt na ciągi znaków (strings) i przekształca instancje Map w zwykłe obiekty, zanim odpowiedź opuści serwer. Jeśli budujesz API, które serwuje dane Midnight do frontendu, zaplanuj ten krok wcześniej. Nie zakładaj, że wyjście z SDK jest od razu przyjazne dla frontendu tylko dlatego, że jest w JavaScript.

Architektura

Celowo postawiłem na nudny stos technologiczny. Backend to serwer Node działający na Express. Importuje on @midnames/sdk, uruchamia logikę rozwiązywania nazw, zajmuje się gimnastyką związaną z serializacją i serwuje czysty JSON. Frontend to zwykły HTML i czysty JavaScript. Bez etapu budowania. Bez frameworka. Bez adaptera portfela.

Zdecydowałem się uruchomić SDK na backendzie, a nie w przeglądarce, z kilku praktycznych powodów. Dzięki temu konfiguracja dostawcy pozostaje poza klientem, mam jedno miejsce do naprawiania bałaganu w serializacji, a frontend musi jedynie pobrać dane i je wyrenderować.

Oto część, która zaskoczyła mnie najbardziej: rozwiązywanie nazwy to operacja odczytu publicznego. Nie potrzebujesz połączenia z portfelem. Nie potrzebujesz podpisu. Nie musisz wymagać od użytkownika logowania się czymkolwiek. Jeśli domena istnieje, stan kontraktu jest widoczny dla każdego, kto o niego zapyta. To znacząca różnica w porównaniu do typowego przepływu web3, gdzie każda interakcja zaczyna się od „połącz portfel”. Odczytywanie tożsamości na Midnight jest bezwymogowe w taki sam sposób, w jaki przeglądanie publicznej strony internetowej jest bezwymogowe.

Najważniejszy wniosek

Budowa tego podglądu przypomniała mi, że najtrudniejszą częścią rozwoju blockchaina rzadko jest sam blockchain. Midnight rozwiązało już trudny problem: umożliwienie ludziom posiadania własnej nazwy i profilu bez centralnej bazy danych. Trudną częścią, z perspektywy twórcy, było pamiętanie, do której sieci się odnoszę, oraz napisanie funkcji pomocniczej do czyszczenia typów danych.

Protokół zapewnia przenośną tożsamość. Twoim zadaniem jako programisty jest po prostu poprawne jej odczytanie i nieprzeszkadzanie użytkownikowi. Zachowaj prostą architekturę, oddziel logikę skierowaną na łańcuch od interfejsu użytkownika i traktuj odczyty publiczne takimi, jakimi są: zwykłymi zapytaniami do bazy danych, które po prostu znajdują się na rozproszonym rejestrze.

Jeśli chcesz zobaczyć kod lub uruchomić go samodzielnie, pełne źródło jest dostępne pod adresem https://github.com/tomiin/midnames-profile-viewer.