我上周一直在尝试理解 .night 域名到底是如何运作的。不是通过规格说明书,而是通过构建一个直接与链交互的东西。结果是一个小型的个人资料查看器。你输入像 tomin.night 这样的名称,它就会直接从区块链上解析出个人资料。没有注册商 API,没有鉴权墙。只有一个智能合约和一些 JavaScript。

接下来是它的工作原理、我的心得,以及那两个浪费了我整个下午的特定陷阱。

为什么链上名称如此重要

在 Midnight 上,.night 域名由 Midnames 管理。你不需要查询公司的中央服务器,而是直接从运行在链上的智能合约中读取数据。注册表本身持有域名、所有者以及所有者附加的任何个人资料字段。因为这些数据是在链上的,所以身份是可移植的。你不是从一个可以随时更改条款或停止服务的平台租用它。如果你控制了密钥,你就控制了名称。

这种转变对开发者也同样重要。当你针对传统的 DNS 系统进行构建时,你需要处理速率限制、API 密钥和可用性承诺。而在这种情况下,合约状态就是事实来源 (source of truth)。你的应用程序读取它的方式与其他任何应用程序都完全相同。不存在特权 API 层级。

代码几乎简单得过头了

@midnames/sdk 处理了繁重的工作。将域名解析为地址和个人资料只需要两行代码:

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

就这样。Provider 指向网络,Resolver 与合约通信。SDK 返回一个包含成功标志 (success flag) 的结果对象。如果域名不存在,你不需要针对 RPC 失败编写多层错误处理或 try-catch 块。SDK 会清晰地告诉你那里什么都没有。这使得构建 UI 的过程出奇地愉快。你可以根据成功标志进行分支处理,显示“未找到”状态,而无需猜测失败是因为名称缺失还是节点宕机。

你会得到什么

当查询成功时,负载 (payload) 包含两个重要的部分。

Target 是域名指向的钱包地址。这是核心用途。它将冗长的十六进制地址转换为人类可读、可输入且易于记忆的内容。

Fields 包含个人资料详情。无论所有者在域名上附加了什么——社交链接、头像、文本记录——都存储在这个结构中。这些字段并不存储在某家公司的 MongoDB 集群中。它们是合约状态中的字段,这意味着任何知道如何读取注册表的应用都可以渲染相同的个人资料。无需数据库同步。

让我进度变慢的两个陷阱

构建过程并非全是两行代码和一个成功标志那么简单。我遇到了两个具体的障碍,值得说明一下,以免你重蹈覆辙。

网络不匹配

SDK 同时支持 mainnet 和 preprod 环境。我花了大量时间调试一个“不存在”的域名。名称是正确的,代码看起来也没问题,Provider 也在运行。问题在于,我的脚本正在查询 preprod,而域名本身是在 mainnet 上注册的。错误看起来像是域名缺失,但实际上是缺少了网络上下文。

如果你在解析名称时收到失败结果,请在进行其他调试之前先检查 Provider 设置。确保你的 networkId 与域名实际铸造 (minted) 的网络相匹配。这种错误在事后看来显而易见,但在你假设问题出在合约逻辑时,真的很难发现。

序列化难题

从 SDK 返回的数据不是标准的 JavaScript。它包含 BigInt 值和 Map 对象。如果你尝试直接将其通过 JSON.stringify 传递给浏览器,它会抛出错误或静默丢失数据。BigInt 没有原生的 JSON 表示形式,而 Map 的序列化方式与普通对象不同。

我最终写了一个自定义序列化器。它会遍历结果对象,在响应离开服务器之前,将 BigInt 值转换为字符串,并将 Map 实例转换为普通对象。如果你正在构建一个为前端提供 Midnight 数据的 API,请尽早规划这一步。不要仅仅因为它是 JavaScript 就假设 SDK 的输出能直接被前端友好地使用。

架构

我刻意保持了技术栈的简单乏味。后端是一个运行 Express 的 Node 服务器。它导入 @midnames/sdk,运行解析逻辑,处理复杂的序列化操作,并提供简洁的 JSON 数据。前端是纯 HTML 和原生 JavaScript。没有构建步骤,没有框架,也没有钱包适配器。

我选择在后端而不是浏览器中运行 SDK,是出于一些实际的考虑。这样可以将任何 provider 配置与客户端隔离,让我可以在一个地方修复混乱的序列化问题,并且意味着前端只需要获取数据并进行渲染。

最让我感到惊讶的是:解析名称是一个公开的读取操作。你不需要连接钱包,不需要签名,也不需要用户进行任何登录。只要域名存在,合约状态对任何请求者都是可见的。这与典型的 Web3 流程有着本质的区别,在典型的流程中,每一次交互都始于“连接钱包”。在 Midnight 上读取身份是无需许可的,就像访问一个公开网站无需许可一样。

真正的启示

构建这个查看器提醒了我,区块链开发中最难的部分往往不是区块链本身。Midnight 已经解决了那个难题:让人们在没有中心化数据库的情况下,能够拥有自己的名称和个人资料。从开发者的角度来看,难点在于记住我正在指向哪个网络,以及编写一个辅助函数来清理数据类型。

该协议为你提供了可移植的身份。作为开发者,你的工作仅仅是正确地读取它,然后不要干扰用户。保持架构简单,将面向链的逻辑与 UI 分离开来,并将公开读取视为其本质:仅仅是恰好存在于分布式账本上的普通数据库查询。

如果你想查看代码或亲自运行,完整的源代码可以在 https://github.com/tomiin/midnames-profile-viewer 获取。