关于 seeker-domains
用于 Solana Mobile 应用的 `.skr` 域名解析技能,基于 `@solana-mobile/skr-domain` 实现名称与钱包地址的双向解析。它指导开发者选择客户端或后端代理,并处理缓存、验证与身份显示风险。
主要功能
- 01`.skr` 名称与地址双向解析
- 02主网解析及过期名称处理
- 03客户端与后端代理方案
- 04输入验证和错误状态映射
- 05反向解析的缓存与信任建议
适用场景
- 01将钱包地址显示为 `.skr` 名称
- 02把 `.skr` 名称解析为支付地址
- 03批量解析地址并减少客户端请求
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ solana-mobile/ solana-mobile-skills --skill seeker-domains安装后在 Claude Code 中输入/seeker-domains确认已加载。
附带文件references/client.mdreferences/server.md
详细介绍
.skr 域名解析
.skr 域名是 Solana 主网上的 AllDomains 名称。Seeker 用户默认会获得一个,因此在 UI 中,它们很适合用来替代被截断的地址。
有两个方向:
- 正向解析 — 将
alice.skr解析为钱包地址 - 反向解析 — 将钱包地址解析为
.skr名称:其主名称,或者它拥有的其他名称
这两个方向都在主网上进行,不受应用其他部分所使用的集群影响。即使应用在 devnet 上运行,也仍会对主网上的名称进行解析。
决定在哪里执行解析
解析会读取公开的链上数据,因此客户端可以直接执行解析。以下情况适合通过后端代理:
- 保护 RPC 密钥。
EXPO_PUBLIC_*中的密钥可被任何拥有 APK 的人读取。如果使用付费 RPC,就必须在服务器端使用。 - 共享缓存。 名称很少变化。由服务器端缓存一次,比每个客户端都重新解析相同的地址更好。
- 批量查询。 一次请求解析整个好友列表,比手机发起 N 次往返请求更好。
getProgramAccounts访问权限。 反向解析需要此方法,但许多提供商会禁用它,或对其施加严格的速率限制。使用经过确认的提供商,通过一个服务器端端点处理,比等到用户设备上才发现限制更好。
对于原型或低流量应用,直接通过公共 RPC 在客户端解析是合理的。公共端点有速率限制,因此无法支撑解析数十个地址的列表视图。
如果无法从项目中明确判断用户的选择,请询问用户。对于任何准备投入生产的项目,默认采用代理方式。
与现有后端集成
编写新服务器之前,先检查现有内容。 在别人的 NestJS 服务旁边再加一个 Express 应用,会让维护变得一团糟。
- 检查每个
package.json中的后端依赖——express、fastify、hono、@nestjs/core、koa,或带有 API 路由的 Next.js 应用。 - 查找入口文件:
server.ts、app.ts、main.ts、index.ts。 - 查找路由组织方式:
routes/、api/、controllers/。 - 如果仍不明确,请询问:“我看到
apps/api中有一个 Fastify 服务器;.skr端点应该加在那里吗?”
将路由添加到现有服务中,并遵循其路由、验证和错误处理约定。只有在确实没有后端时,才搭建一个最精简的服务器。
核心解析逻辑
使用 @solana-mobile/skr-domain。不要将解析器复制到项目中,也不要转而使用其他 SDK:该软件包由 Solana Mobile 维护,基于 @solana/kit 构建,并支持两个方向的解析。解析与框架无关;只有外围的路由方式会有所不同。
npm install @solana-mobile/skr-domain @solana/kit
import { getPrimarySkrDomain, getSkrDomains, normalizeSkrName, resolveSkrDomain } from '@solana-mobile/skr-domain'
import { address, createSolanaRpc } from '@solana/kit'
// Always mainnet, whatever cluster the rest of the app targets.
const rpc = createSolanaRpc(process.env.SOLANA_MAINNET_RPC_URL!)
// Forward: { owner, nameAccount, expiresAt }, or null when unregistered or expired.
const domain = await resolveSkrDomain(rpc, 'alice.skr')
// Reverse: the name the wallet chose, else the first name it owns (sorted), else null.
const wallet = address('5FHw...')
const name = (await getPrimarySkrDomain(rpc, wallet)) ?? (await getSkrDomains(rpc, wallet))[0] ?? null
该软件包的功能如下,可避免你重新实现:
- 未找到时返回
null(或[]),绝不会抛出异常。 拒绝意味着 RPC 调用失败,因此两者可以分别映射为 404 和 503。 - 无效输入会抛出普通的
Error。normalizeSkrName接受alice、Alice.skr和' alice ',但子域名、其他 TLD 或空标签会触发异常。在消耗 RPC 配额前,可在try/catch中调用它,将无效输入转换为 400。 - 已过期的名称会解析为未注册状态,两个方向均如此,且没有宽限期。
getPrimarySkrDomain会重新检查所有权。 转移所有权时,MainDomain账户不会更新,因此该软件包会正向解析名称,并且只有在钱包仍拥有该名称时才返回它。getSkrDomains使用getProgramAccounts,许多服务提供商会禁用或限制此接口的速率。它每次读取 100 个标签,每次只发出一个请求,因此即使某个钱包拥有大量名称,也无法一次性耗尽你的配额。- NFT 封装名称不在支持范围内。 目前
.skr下不存在此类名称;如果出现,resolveSkrDomain会抛出异常。
React Native 需要 WebCrypto polyfill。 派生名称地址时会调用 crypto.subtle.digest,而 Hermes 不支持此接口。安装 react-native-quick-crypto,并在启动时、导入该软件包之前调用它的 install()。Node 20.18+、Bun 和浏览器则无需额外配置。
该软件包仅支持 ESM。 CommonJS 服务端需要设置 "type": "module"(或使用 Node 22.12+,该版本可以 require() ES 模块)。
对于该软件包未涵盖的 ANS 记录或头像,请使用 AllDomains SDK,参见下方链接。
API 形式
两个端点,可根据所用框架进行调整:
| 路由 | 请求正文 | 成功 | 未找到 |
|---|---|---|---|
POST /api/resolve-domain | { domain: "alice.skr" } | { address } | 404 |
POST /api/resolve-address | { address: "5FHw..." } | { domain } | 404 |
访问 RPC 前先验证输入:如果 base58 地址格式错误,或名称被 normalizeSkrName 拒绝,则返回 400,避免无效输入消耗 RPC 配额。
区分“未注册域名”(404)和“RPC 调用失败”(503)。将两者都折叠为 404,会让服务中断看起来像是每个用户都没有域名。
用于保护 RPC 密钥的代理不能成为面向该密钥的开放中继。反向路由最多会调用两次 getAccountInfo、一次 getProgramAccounts,并批量读取该地址拥有的所有名称,因此端点需要设置来源允许列表、每 IP 速率限制、请求正文大小限制,以及能够缓存否定结果的缓存。启动时必须要求提供 RPC URL 和来源列表,不要回退到公共端点或开放式 CORS。
完整的 Express 和 Hono 实现,以及有关 Fastify、NestJS、Koa 和 Next.js 路由处理程序的说明:references/server.md。
客户端集成
const { data: domain } = useResolveAddress(account?.address)
const label = domain ?? ellipsify(account?.address)
始终回退到截断后的地址。名称解析失败时,应降级为可用内容,绝不能显示为空白或永远无法消失的加载动画。
对于 Android 模拟器,localhost 指的是模拟器本身。请通过 http://10.0.2.2:3000 访问主机。在实体设备上,请使用主机的局域网 IP。将任一地址硬编码到源代码中会导致下一个使用者的应用出问题——请从 EXPO_PUBLIC_API_URL 读取,并通过 __DEV__ 控制任何模拟器回退逻辑:EXPO_PUBLIC_* 会在构建时内联,因此未经限制的默认值会将一个失效的明文 HTTP URL 打包进发布版 APK,而 Android 默认也会阻止这种连接。任何包含 RPC 密钥的内容都不应放进 EXPO_PUBLIC_* 变量——这正是设置代理的原因。
上面的代码片段假定使用代理。如果改为直接在应用中解析,安装 WebCrypto polyfill 后,该软件包便可在 Hermes 下运行——请在同一个 hook 中调用它来代替 fetch。
Hook、组件和截断辅助函数:references/client.md。
信任反向解析的名称
主名称由钱包持有者选择,而 getSkrDomains 返回的名称不是:.skr 转让不需要接收方进行任何操作,因此任何人都能把名称推送到任意钱包,陌生人可以通过发送一个排序靠前的名称,决定没有主名称的用户在你的 UI 中显示什么名称。
- 优先使用
getPrimarySkrDomain,仅在显示时才回退到getSkrDomains。 - 将名称显示在截断后的地址旁边,而不要用名称取代地址。 用户真正能够核实的是地址。
- 当标签代替身份标识时——例如收款人、交易对手或审核界面——只使用主名称,并在没有主名称时回退到地址。
按方向缓存
| 方向 | 用途 | 缓存 |
|---|---|---|
| 正向(名称 → 地址) | 付款目的地 | 不缓存。发送时重新解析。 |
| 反向(地址 → 名称) | 显示标签 | 较长的 staleTime;缓存一小时即可。 |
名称很少变更,因此缓存反向解析结果几乎没有代价。正向解析则不同:.skr 名称可以转让和重新注册,因此,缓存一小时的正向解析结果随后被用于构建转账时,款项会支付给一小时前拥有该名称的人,而 UI 看起来却一切正常。请在签名前立即重新解析,并在确认步骤中显示解析出的地址,让用户看到资金实际将转往何处。
这一点在两端都必须成立。如果代理使用自己的正向缓存作答,那么客户端即使在发送时重新解析也毫无意义,因此服务器只能缓存正向解析未命中的结果,绝不缓存已解析出的地址。
参考资料
- references/server.md — Express 和 Hono 实现、其他框架、验证与错误处理
- references/client.md — 解析 hook、显示组件、模拟器网络
相关技能
solana-mobile-wallet— 提供待解析地址的钱包连接seeker-genesis-token— 验证 Seeker 所有权
链接
@solana-mobile/skr-domain:https://www.npmjs.com/package/@solana-mobile/skr-domain@solana-mobile/skr-domain源代码和 README:https://github.com/solana-mobile/skr-js/tree/main/packages/skr-domain- AllDomains 开发者指南:https://docs.alldomains.id/protocol/developer-guide/ad-sdks/svm-sdks/solana-mainnet-sdk
@solana/kit:https://www.npmjs.com/package/@solana/kit