X1ID is live on X1 mainnet
X1ID / SDK reference

SDK reference

@x1id/resolve resolves @handles and X1NS names (.x1 / .xnt / .xen) to addresses on X1, an address back to its primary @handle, and a handle's per-chain payment records. It reads accounts directly off chain over RPC — it never calls a hosted resolution API, so it works against any X1 RPC and keeps working if x1id.io itself goes away.

This page targets @x1id/[email protected] — the current npm latest.

@handle resolution is live on X1 mainnet. The handle registry program is deployed on X1 mainnet (https://rpc.mainnet.x1.xyz), and the same program at the same ID is also on X1 testnet (https://rpc.testnet.x1.xyz) for development. X1NS domain resolution (.x1 / .xnt / .xen) is a separate, pre-existing system that exists on X1 mainnet only — against a testnet RPC an X1NS name is not found and reverse()'s X1NS fallback returns nothing. Point rpcUrl at the chain that holds the names you want; on an RPC where the registry is not deployed a handle is an honest not-found, never a fabricated address.

Install

npm install @x1id/[email protected]

@solana/web3.js is an optional peer dependency — you only need it if you pass web3.js types around; the SDK talks to RPC directly and holds no keys.

Loading the WASM module

Account derivation needs a real ed25519 on-curve check, which the SDK runs in a small Rust→WASM module rather than reimplementing in TypeScript — a hand-rolled JS version is the kind of thing that is subtly wrong for months, and this is code that loses money when it is wrong. wasm is therefore a required field on ResolverConfig; there is deliberately no JavaScript fallback. The package ships the module at the exported subpath @x1id/resolve/wasm/x1_resolve_wasm.wasm.

import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { WasmResolver, createResolver } from '@x1id/resolve';

const require = createRequire(import.meta.url);
const wasmPath = require.resolve('@x1id/resolve/wasm/x1_resolve_wasm.wasm');
const wasm = await WasmResolver.fromBytes(readFileSync(wasmPath));

// testnet works the same: rpcUrl: 'https://rpc.testnet.x1.xyz'
const x1id = createResolver({ rpcUrl: 'https://rpc.mainnet.x1.xyz', wasm });

Node ≥ 20.6 can use import.meta.resolve instead of createRequire:

import { fileURLToPath } from 'node:url';
const wasmPath = fileURLToPath(import.meta.resolve('@x1id/resolve/wasm/x1_resolve_wasm.wasm'));
// Vite / Rollup / webpack 5 — the `?url` suffix hands you the asset URL
import wasmUrl from '@x1id/resolve/wasm/x1_resolve_wasm.wasm?url';
import { WasmResolver, createResolver } from '@x1id/resolve';

const wasm = await WasmResolver.fromBytes(await (await fetch(wasmUrl)).arrayBuffer());

// testnet works the same: rpcUrl: 'https://rpc.testnet.x1.xyz'
const x1id = createResolver({ rpcUrl: 'https://rpc.mainnet.x1.xyz', wasm });

Or copy node_modules/@x1id/resolve/wasm/x1_resolve_wasm.wasm into your static directory and fetch('/x1_resolve_wasm.wasm') it — that is what x1id.io does.

WasmResolver.fromBytes() calls WebAssembly.instantiate(bytes, {}) on the bytes you hand it — the SDK never fetches the module itself. Load the copy that ships in the npm package you installed (or self-host that file); don't point at an unpinned CDN URL, which can serve a module from a different version than your JS. A resolver instance is single-threaded; create one per process (or per page) and reuse it.

Browser requirements

  • CSP. Compiling WebAssembly needs 'wasm-unsafe-eval' in script-src (no 'unsafe-eval' needed). connect-src must allow your X1 RPC (https://rpc.mainnet.x1.xyz), plus https://api.x1id.io if you call the REST API from the page. Server-side use needs no CSP change.
  • Serve the .wasm file as application/wasm. The SDK instantiates from bytes, so the MIME type isn't checked by fromBytes, but serve it correctly anyway so CDNs and proxies don't mangle or block it.

Next.js recipe

Written against Next.js 14/15 conventions; not yet tested on Next.js 16.

Server (route handlers, server components) — recommended for a payment flow. Use the Node runtime (the Edge runtime is untested), read the module from the package, and keep @x1id/resolve out of the server bundle so the .wasm file ships with the deployment:

lib/x1id.server.ts
import 'server-only';
import { readFile } from 'node:fs/promises';
import { createRequire } from 'node:module';
import { WasmResolver, createResolver, type Resolver } from '@x1id/resolve';

let resolver: Promise<Resolver> | undefined;

export function getResolver(): Promise<Resolver> {
  resolver ??= (async () => {
    const require = createRequire(import.meta.url);
    const bytes = await readFile(require.resolve('@x1id/resolve/wasm/x1_resolve_wasm.wasm'));
    const wasm = await WasmResolver.fromBytes(bytes);
    return createResolver({ rpcUrl: 'https://rpc.mainnet.x1.xyz', wasm });
  })();
  return resolver;
}
next.config.mjs
export default {
  output: 'standalone',
  serverExternalPackages: ['@x1id/resolve'],
  outputFileTracingIncludes: {
    '/**': ['./node_modules/@x1id/resolve/wasm/*.wasm'],
  },
};

This is the Next 15 form. On Next 14 both keys live under experimental: experimental.serverComponentsExternalPackages and experimental.outputFileTracingIncludes.

Browser (client components). Copy the module into public/ at install time, so it is served from your own origin at the version in your lockfile:

package.json
{
  "scripts": {
    "postinstall": "cp node_modules/@x1id/resolve/wasm/x1_resolve_wasm.wasm public/x1_resolve_wasm.wasm"
  }
}
lib/x1id.client.ts
'use client';
import { WasmResolver, createResolver } from '@x1id/resolve';

export async function loadResolver() {
  const bytes = await (await fetch('/x1_resolve_wasm.wasm')).arrayBuffer();
  const wasm = await WasmResolver.fromBytes(bytes);
  return createResolver({ rpcUrl: 'https://rpc.mainnet.x1.xyz', wasm });
}
next.config.mjs (headers)
export default {
  async headers() {
    return [
      { source: '/x1_resolve_wasm.wasm', headers: [{ key: 'Content-Type', value: 'application/wasm' }] },
      {
        source: '/:path*',
        headers: [
          {
            key: 'Content-Security-Policy',
            // One string. Merge these directives into your existing policy:
            // Next.js's own scripts may need a nonce or more sources.
            value: "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self' https://rpc.mainnet.x1.xyz https://api.x1id.io",
          },
        ],
      },
    ];
  },
};

createResolver

Create a resolver instance. Derivation and RPC reads happen lazily, on each call — nothing is fetched at construction time.

import { createResolver, WasmResolver } from '@x1id/resolve';

const wasm = await WasmResolver.fromBytes(/* module bytes, see above */);

const x1id = createResolver({
  rpcUrl: 'https://rpc.mainnet.x1.xyz',   // testnet works the same with rpc.testnet.x1.xyz
  wasm,
  cacheTtlMs: 30_000, // optional — this is the default
});

Parameters (ResolverConfig):

FieldTypeRequiredDescription
rpcUrlstringYesAny X1 RPC endpoint. Use https://rpc.mainnet.x1.xyz for @handles (https://rpc.testnet.x1.xyz for the testnet registry).
wasmWasmResolverYesThe loaded WASM module, from WasmResolver.fromBytes(). No JS fallback exists.
fetchImpltypeof fetchNoCustom transport for tests or proxies. Defaults to the global fetch.
cacheTtlMsnumberNoResolution cache TTL in milliseconds. Default 30_000. Set 0 to disable caching (resolve() only).
handleProgramIdstringNo@handle registry program ID. Defaults to the canonical X1 deployment, 8JgnNWi24bq9uzfnT9XmkWxvaWMVgoEs9bu8QsHhLe1P; override to point at a different deployment (e.g. a local test validator).

Returns: Resolver — an object exposing resolve, reverse, records, and clearCache.

resolve

Resolve a name to an address. resolve classifies its input by shape — a leading @ means a handle, a recognized TLD suffix (.x1 / .xnt / .xen) means an X1NS domain — and never falls back from one namespace to the other. Input shaped like both throws ambiguous rather than silently picking one.

resolve(input: string, opts?: { chain?: 'X1' | 'SOL' | 'ETH' | 'BTC' }): Promise<Resolved>

Returns Resolved:

FieldTypeDescription
inputstringExactly what was passed in, for display.
namestringCanonical form of the name.
namespaceNamespaceWhich naming system answered. The union is 'handle' | 'x1' | 'xnt' | 'xen' | (string & {}) — the open string arm is a customer-TLD label (e.g. 'shib' for alices.shib), so a UI must treat it as an arbitrary label, not an exhaustive enum. Always render this before letting a user send — see the one rule.
addressstringThe resolved address: base58 for X1/SOL, 0x-hex for ETH, bech32 for BTC.
chain'X1' | 'SOL' | 'ETH' | 'BTC'Chain the address belongs to.
verification'verified' | 'unverified'Whether ownership of address was proved for this chain — see Verification.
verifications?HandleVerification[]Optional per-platform "verified by [platform]" signals. The SDK's own on-chain resolve() does not populate this — read attestations directly.
const res = await x1id.resolve('@nike');
// {
//   input: '@nike', name: 'nike', namespace: 'handle',
//   address: 'E2cELMtby3YYYbDdxamuGKWCyte3kwLywv19pEk2iUq6',
//   chain: 'X1', verification: 'verified'
// }

@nike is a held (reserved) name — don't send to it. The address above is the X1ID treasury (E2cELMtby3YYYbDdxamuGKWCyte3kwLywv19pEk2iUq6), which holds reserved and premium names until they reach an owner through the marketplace. verification: 'verified' only means the treasury controls the account; it says nothing about the brand. The SDK carries no held flag. Before a payment, check the REST /quote/{name} endpoint and refuse the send when status is "held", and refuse any resolution whose address is the treasury. See the pre-send checks.

Tokenized handles

A handle can be minted into a capability NFT, after which holding the NFT is the authority and the registry's owner field stops moving with the name. resolve() handles this for you: for a tokenized handle it returns whoever currently holds the NFT in their associated token account, never the stale owner field. A handle whose NFT has been burned (mint supply 0) resolves to no one — ResolveError { code: 'not-found', reason: 'nft-burned' }, never the stale owner.

The ambiguous case

This is the one behavior to get right. @jack and jack.x1 can be registered to different people. There is no fallback chain in this SDK from one namespace to the other — a fallback is exactly how those two identities get silently conflated, and the failure mode is a transfer that succeeds, to the wrong person. Input shaped like both (@jack.x1) throws ambiguous instead. Never catch it and pick one — ask the user which they meant.

reverse

Look up the primary name for an address.

reverse(address: string): Promise<string | null>

Checked in this order, and returned as a bare string (the handle without the @) or null:

  1. On-chain @handle primary. Returned only when all three trust rules hold: the handle it names is a live registry account; the pointer was set no earlier than the handle's current registered_at (so a pointer left from before a release + re-register doesn't count); and the address still holds authority (the owner field if untokenized, or an associated token account holding exactly 1 of the handle's NFT if tokenized).
  2. X1NS primary domain, as a fallback. Only when there is no valid handle primary. The X1NS record stores the domain account, not its label, so the result is "<domainAccount>.<tld>" — not "jack.x1".
  3. null otherwise. "No primary name" is a normal state, not an error — don't treat it as a failure.
const name = await x1id.reverse('E2cELMtby3YYYbDdxamuGKWCyte3kwLywv19pEk2iUq6');
// null — reverse() returns the wallet's PRIMARY name (set via set_primary),
// or null when none is set. resolve() and reverse() are NOT strict inverses:
// this is the X1ID treasury, which holds @nike (and other reserved names)
// but has set no primary, so it reverses to null.
// A wallet that HAS set a primary returns the bare handle, e.g. "nike".

// Telling the two shapes apart: a handle result never contains a dot.
const display = name === null ? null : name.includes('.') ? name : `@${name}`;

A pointer that fails any rule is treated as absent (falls through to X1NS, then null), never as an error. Only a malformed address throws (ResolveError { code: 'unrecognized' }).

records

Every per-chain payment record of a @handle, with the staleness rule already applied: records left behind by a previous registration of the same name — or by a previous holder when a capability NFT changed hands in a native marketplace sale — come back flagged stale with verified forced false. (A plain NFT transfer stamps nothing on chain and is not covered — see the caveat under the staleness rule.)

records(input: string): Promise<readonly HandleRecord[]>
const all = await x1id.records('@nike');
// HandleRecord[]: { account, handle, coinType, chain, value, address,
//                   verified, updatedAt, stale }  ([] when none are set)

import { liveRecords } from '@x1id/resolve';
const live = liveRecords(all); // the ones that belong to the current owner

@handle inputs only — X1NS domains keep their addresses elsewhere. Throws not-found for an unregistered handle, unrecognized for a non-handle input. See Address records for reading and writing the full record family.

clearCache

x1id.clearCache();

resolve results are cached per (namespace, name, chain) for cacheTtlMs (default 30 s); reverse and records are not cached. Call clearCache() after a registration or transfer you know just happened on chain, so a stale result doesn't linger.

Re-resolve, uncached, right before signing a payment. A name can change hands inside the cache window. resolve() has no per-call cache bypass, so for the final resolve before you sign a send either call clearCache() first or keep a second resolver created with cacheTtlMs: 0 for that one call. A cached result is fine for display while the user types; cache a positive result for at most 30 s, and a not-found or a reverse label for at most 30–60 s.

The SDK is lease-unaware. A lease-to-own handle past its paid-through date still resolves, with no lease signal on Resolved (the hosted /v1/resolve is the same). Before sending funds, read the free REST /resolve/{name}, which adds lease: { expires_at, lapsed } for a leased handle, and refuse when lapsed is true.

Multi-chain resolution

A name can carry addresses for more than one chain. Request the one you want with opts.chain:

await x1id.resolve('@jack', { chain: 'X1' });  // default — the handle's authority
await x1id.resolve('@jack', { chain: 'SOL' }); // X1 shares Solana's ed25519 key format
await x1id.resolve('@jack', { chain: 'ETH' }); // the handle's ETH record, if one is set
await x1id.resolve('@jack', { chain: 'BTC' }); // the handle's BTC record, if one is set

For X1/SOL the SDK returns the handle's current authority. For ETH/BTC it reads the handle's per-chain Record accounts; a handle without a live record for the requested chain throws ResolveError { code: 'no-record-for-chain' } — an explicit "no record", never a wrong address on the wrong chain. Every record is judged against the universal staleness rule structurally.

Verification

verificationMeaning
verifiedControl of the address was proved: the address owns the @handle registry account, or — for a tokenized handle — holds its NFT in its associated token account (the only place the registry recognises as the name's authority).
unverifiedOwnership was not proved for that chain (X1NS), or — for a tokenized handle — the NFT is held outside the holder's ATA (an auxiliary account or escrow): that wallet controls the token but cannot act for the name until it is back in its ATA. Show a warning.

This is per-chain control. The separate "verified by [platform]" blue-tick signal (verified X, GitHub, domain…) is covered in Reading verified identity.

Beyond resolution: the full helper surface

@x1id/resolve is not only a resolver — the same package ships the instruction builders, sign-in, gating, and cross-namespace helpers that app.x1id.io and the developer platform are built on. Every builder is transport-neutral ({ programId, keys, data }, no @solana/web3.js import) and holds no keys; you sign and relay. The groups below are the ones the pre-mainnet surface adds on top of resolve / reverse / records.

Namespaces and scoped names

A launched customer TLD (.shib) is an on-chain Namespace account at ["namespace", tld]. A name registered under it — alices.shib — is an always-tokenized Handle at the two-seed PDA ["handle", tld, name], a different account from the bare @alices at ["handle", name], so the two can have different owners. The scoped helpers are the SDK last-mile for these (feature #8484/#8485):

import { scopedTldCandidate, namespaceIsActive, makeScopedHandleDeriver } from '@x1id/resolve';

// Shape-only classifier (no RPC): is this a `name.tld` scoped candidate?
const cand = scopedTldCandidate('alices.shib'); // → { sub: 'alices', tld: 'shib' } | null

// Is a fetched `Namespace` account live? (byte-reader; caller checks program ownership)
const active = namespaceIsActive(namespaceAccountData); // → boolean

// The one PDA derivation scoped resolution needs — `["handle", tld, name]` —
// injected (web3.js) because this package never hand-rolls curve math:
const deriver = await makeScopedHandleDeriver();
const handlePda = await deriver.scopedHandleKey('shib', 'alices', programId);

createResolver().resolve() answers scoped names directly — resolve('alices.shib') returns namespace: 'shib' once that TLD is a launched, Active namespace; the owner/tokenization/verification read reuses the bare-handle authority path. The low-level WasmResolver also exposes deriveNamespaceAccount(label, programId) for ["namespace", label].

Subnames (and subnames under a scoped handle)

A subname is a label under a parent handle — pay under alice → pay.alice. Subnames branch one further level, so pay.alice can carry its own deep.pay.alice sub-subname (see What you can do). A subname has no owner of its own: the parent's current authority controls it. Because a scoped name is itself a Handle, a subname can also hang under one, giving pay.alices.shib. Subname resolution enforces a two-layer staleness rule (parent-vs-subname, then record-vs-subname), so always read through resolveSubnameRecords:

import {
  buildCreateSubnameIx, buildRevokeSubnameIx,
  buildCreateSubnameRecordIx, buildUpdateSubnameRecordIx, buildCloseSubnameRecordIx,
  fetchSubnames, resolveSubnameRecords, subnameIsLive, decodeSubname,
} from '@x1id/resolve';

// Create `pay.alice` (rent-only, parent-authority-gated):
const ix = buildCreateSubnameIx({ programId, payer, owner, parent, subname, label: 'pay' });

// Resolve a subname's LIVE records — the ONLY safe entry point (both layers enforced):
const live = await resolveSubnameRecords(rpc, programId, subnameAccount, subname, parentRegisteredAt);

PDA derivation (["subname", parentHandlePda, label], and records at ["record", subnamePda, coinType]) is done with your runtime, exactly as the module docs show — this package does not hand-roll find_program_address.

Sign in (Connect)

The signin helpers are the client side of X1ID Connect — a domain-bound challenge, a wallet signature, verification, and the address→@handle identity, in one call:

import { signInWithX1ID, createSignInChallenge, verifySignIn, resolveIdentity } from '@x1id/resolve';

const identity = await signInWithX1ID({
  rpcUrl, wasm,                 // IdentityConfig: how to resolve the handle
  address,                      // the connected wallet (base58)
  signMessage,                  // wallet.signMessage: (bytes) => Promise<64-byte sig>
  domain: 'yourapp.com',
  requireHandle: true,          // default — "Sign in with X1ID" implies one; false allows handle-less
});
// → SignInIdentity: { address, handle, verified…, challenge, nonce, issuedAt, expiresAt }

createSignInChallenge / parseSignInChallenge / verifySignIn are the split primitives if you want the backend to verify a challenge your frontend had signed; resolveIdentity(cfg, address) is the identity read on its own. This is a server-less proof your own backend verifies; it doesn't use the hosted API.

Against the hosted Connect API, use signInWithWallet (SIWS, then a never-sent memo transaction, then v2 signMessage, falling back automatically) or signInWithSolana (SIWS only, allowlisted domains only). They sign the server's audience-bound strings verbatim and return a Bearer session. The memo-transaction path is what makes Backpack, the X1 wallet, and Ledger work.

import { signInWithWallet } from '@x1id/resolve';

const { method, ...session } = await signInWithWallet({
  wallet,                                         // Wallet Standard wallet
  apiBase: `${window.location.origin}/api/x1id`,  // your backend proxy, which adds X-API-Key
  apiKey: 'server-held',                          // placeholder; the real key never reaches the browser
  rpcUrl: 'https://rpc.mainnet.x1.xyz',
});

0.21.0 is required for hosted sign-in. The API now accepts only audience-bound strings, and signinWithTx / buildVerifyTxChallenge from SDK 0.20.x and earlier throw on the bound memo. See Sign in with X1ID for the full flow, the SIWS domain allowlist, and the backend proxy.

Gate on verified identity

The gate helpers test a handle's on-chain attestations against a policy — read it yourself over RPC, or call the hosted Gate endpoint:

import { attestationsPassGate, handlePassesGate, GateKind, checkGate, requireGate } from '@x1id/resolve';

// A Gate is "any-verified" | { kind } | { anyOf: number[] }, with named kinds:
const gate = { anyOf: [GateKind.x, GateKind.github] }; // dns,social,x,discord,github,telegram

// Fetch + test in one RPC call:
const { passed, attestations } = await handlePassesGate(rpc, programId, handleAccount, registeredAt, recordsClearedAt, gate);

// …or the hosted client (keyed or x402). Server-side only: never put the key in browser code.
const res = await checkGate({ apiKey: process.env.X1ID_API_KEY!, baseUrl }, { handle: '@jack' }, 'any-verified');

Stale attestations never count toward a gate.

Pay per call (x402)

Without an API key, the /v1 platform answers 402 and is paid per call. The SDK holds no keys and builds no transactions, but it parses the challenge and builds the message your wallet signs to redeem the payment:

import { encodeBase58, parseX402Response, x402RedeemMessage, X1_MAINNET_NETWORK } from '@x1id/resolve';

const res = await fetch(url, { headers: { 'X-Payer': wallet.publicKey.toBase58() } });
const req = parseX402Response(await res.json())?.accepts.find((a) => a.network === X1_MAINNET_NETWORK);
// …pay req.amount of req.asset to req.payTo with req.extra.reference on the TransferChecked → `signature`

const message = x402RedeemMessage(req!.extra.reference!); // === req.extra.redeemMessage
const payerSig = encodeBase58(await wallet.signMessage(new TextEncoder().encode(message)));
const payment = btoa(JSON.stringify({ x402Version: 2, scheme: 'exact', network: req!.network, payload: { signature, payerSig } }));
await fetch(url, { headers: { 'X-Payer': wallet.publicKey.toBase58(), 'PAYMENT-SIGNATURE': payment } });

Breaking in 0.22.0: api.x1id.io requires payload.payerSig on every redemption (REDEEM_SIGNATURE_REQUIRED without it), and buildX402Requirements no longer takes a memo; the request-bound reference is the only payment shape. X402_REDEEM_DOMAIN is the message's domain prefix, and canonicalX402Request / x402RequestHash / deriveX402Reference let you check a reference. The full client, with the payment transaction, is on Pay-per-call with x402.

Universal resolution (.sol / .eth)

createUniversalResolver wraps the native resolver with optional SNS (.sol) and ENS (.eth) adapters behind one resolve, mirroring the hosted universal resolver. Each external namespace is gated off unless you supply its RPC/config:

import { createUniversalResolver } from '@x1id/resolve';

const uni = createUniversalResolver({
  native: x1id,                 // the createResolver() instance
  sns: { rpcUrl, deriver },     // omit to gate .sol off
  ens: { rpcUrl: ETH_RPC_URL }, // omit to gate .eth off
});

await uni.resolve('vitalik.eth');  // throws `not-configured` if that namespace is disabled
uni.detect('jack.sol');            // classify by shape → namespace | null
uni.status();                      // which namespaces are live vs config-gated

Should I even try to resolve this?

Not every recipient-field input is a name — most of the time it's a pasted address. Use looksLikeName() to decide whether to attempt resolution at all, so a pasted base58 address never surfaces a spurious "invalid handle" error:

import { looksLikeName } from '@x1id/resolve';

if (looksLikeName(value)) {
  // "@jack", "jack.x1", "jack.xnt", "jack.xen" — attempt resolution
  resolveAndShowRecipient(value);
} else {
  // Treat as a raw address instead — validate/checksum it directly
  validateRawAddress(value);
}

looksLikeName() is a pure, synchronous shape check — no RPC, never throws. It returns true for anything starting with @, or ending in a recognized TLD, regardless of whether that name is actually registered.

Errors

resolve, reverse and records throw ResolveError, never a bare string — so a UI can branch on .code instead of parsing a message:

import { ResolveError } from '@x1id/resolve';

try {
  const res = await x1id.resolve(userInput);
} catch (e) {
  if (e instanceof ResolveError) {
    console.log(e.code);   // e.g. "not-found", "ambiguous", "invalid-handle"
    console.log(e.input);  // the original input that caused the error
  }
}

See the Error reference for the full list of ResolveErrorCode values and what a UI should do about each.

WasmResolver

The loaded module also exposes the low-level derivation primitives, if you need to derive accounts yourself (for instance to build a write instruction — see Register a handle):

class WasmResolver {
  static fromBytes(bytes: BufferSource): Promise<WasmResolver>;
  normalizeHandle(raw: string): string | null;
  deriveHandleAccount(canonical: string, programId: Uint8Array): Uint8Array | null;        // ["handle", name]
  deriveHandlePrimaryAccount(owner: Uint8Array, programId: Uint8Array): Uint8Array | null; // ["primary", owner]
  deriveX1nsAccount(label: string, tld: 'x1' | 'xnt' | 'xen'): Uint8Array | null;
  deriveNamespaceAccount(label: string, programId: Uint8Array): Uint8Array | null;         // ["namespace", label]
  derivePrimaryAccount(owner: Uint8Array): Uint8Array | null;                              // X1NS primary-domain record
  deriveAssociatedTokenAccount(owner: Uint8Array, mint: Uint8Array): Uint8Array | null;
}

Also exported: parseName, looksLikeName, normalizeHandle, namespaceLabel, encodeBase58, decodeBase58, decodeBase58_32, CHAIN_COIN_TYPE, and the types Resolved, Namespace, Chain, Verification, ResolveErrorCode.

Next

Browse the docs

SDK reference