@bonfida/spl-name-service
v4.0.1
Published
<p align="center"> <img width="200" src="https://www.sns.id/assets/logo/brand.svg" alt="SNS logo" /> </p>
Readme
SNS JavaScript SDK
JavaScript SDK for resolving Solana Name Service (SNS) domains, reading records and ownership data, and constructing SNS transaction instructions with @solana/web3.js 1.x.
Migrating from v3? This release is not fully backward compatible. Review the changelog for breaking changes and migration notes.
Installation
npm install @bonfida/spl-name-service @solana/web3.jsThe SDK has a peer dependency on @solana/web3.js ^1.98.2. Supply a web3.js Connection for RPC operations. While Node.js is the primary tested environment, browser applications are fully supported via compatible bundlers.
Read APIs fetch and decode account data. Mutation bindings return TransactionInstruction, Promise<TransactionInstruction>, or Promise<TransactionInstruction[]>. Add the returned instruction(s) to a transaction, set its fee payer and recent blockhash, collect the signatures required by its account metas, and submit that transaction through your application.
Subpath Imports
While the root entry point remains available, applications can also use subpath imports:
import { getPrimaryDomain } from "@bonfida/spl-name-service/address";
import { resolve, safeResolve } from "@bonfida/spl-name-service/domain";
import { getMultipleRecords, Record } from "@bonfida/spl-name-service/record";Supported subpaths are address, bindings, constants, domain, errors, instructions, nft, record, states, twitter, types, and utils.
Quick Start
Resolve A Domain
import { Connection } from "@solana/web3.js";
import { resolve } from "@bonfida/spl-name-service/domain";
const connection = new Connection("https://your-rpc-endpoint.example");
const owner = await resolve(connection, "mydomain.sns"); // Or use `safeResolve`.
console.log(owner.toBase58());Get A Primary Domain
import { Connection, PublicKey } from "@solana/web3.js";
import { getPrimaryDomain } from "@bonfida/spl-name-service/address";
const connection = new Connection("https://your-rpc-endpoint.example");
const wallet = new PublicKey("<WALLET_ADDRESS>");
const primaryDomain = await getPrimaryDomain(connection, wallet);
console.log(`${primaryDomain.reverse}.sns`, primaryDomain.stale);List Domains For An Owner
import { Connection, PublicKey } from "@solana/web3.js";
import { getSnsDomainsForOwner } from "@bonfida/spl-name-service/address";
const connection = new Connection("https://your-rpc-endpoint.example");
const wallet = new PublicKey("<WALLET_ADDRESS>");
const domains = await getSnsDomainsForOwner(connection, wallet);
console.log(domains.map(({ domain }) => `${domain}.sns`));Domain Inputs And Resolution
Use the form required by each API rather than normalizing names yourself:
| API family | Required input | Scope |
| ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------------- |
| High-level reads such as resolve, safeResolve, getRecord, and getMultipleRecords | Full suffixed name, for example mydomain.sns | .sns; legacy .sol reads have the transition rule below |
| Top-level writes such as registration, transfer, burn, and background changes | Canonical lowercase mydomain.sns | Exactly one label before .sns |
| Record writes | Canonical lowercase mydomain.sns or sub.mydomain.sns | Top-level domain or one-level subdomain |
| Subdomain creation | Canonical lowercase sub.mydomain.sns | Exactly one subdomain level |
| Derivation and raw name-account helpers | TLD-trimmed or raw input, for example mydomain or sub.mydomain | Follow each low-level helper's account-format contract |
High-level .sol reads use the legacy SNS-backed path only before finalized slot 452,825,395. At and after that slot, .sol is rejected. .sol writes are not supported.
API Reference
Resolution
resolve— resolves a.snsdomain, or a legacy.soldomain while the transition path remains available.resolve(connection: Connection, domain: string, config?: ResolveConfig): Promise<PublicKey>safeResolve— followsresolve, but when SRS-backed.solresolution is enabled, it also checks the SNS-backed target and throwsSnsSolResolutionMismatchErrorif the addresses differ.safeResolve(connection: Connection, domain: string, config?: ResolveConfig): Promise<PublicKey>
Record Reads And Validation
getRecord— fetches and verifies one V2 record.getRecord(connection: Connection, domain: string, record: Record, options?: { deserialize?: boolean }): Promise<RecordResult>getMultipleRecords— returns results in input order; a missing record account isundefinedat its corresponding index.getMultipleRecords(connection: Connection, domain: string, records: Record[], options?: { deserialize?: boolean }): Promise<(RecordResult | undefined)[]>
The following standalone helpers do not come from RecordResult:
getRecordV1Key/getRecordV2Key— derive record account keys from a TLD-trimmed domain such asmydomain.getRecordV1Key(domain: string, record: Record): PublicKey getRecordV2Key(domain: string, record: Record): PublicKeyserializeRecordContent/deserializeRecordContent— encode content for storage, or decode stored content.serializeRecordContent(content: string, record: Record): Buffer deserializeRecordContent(content: Buffer, record: Record): stringverifyStaleness— independently fetches and checks a V2 record's staleness validation.verifyStaleness(connection: Connection, record: Record, domain: string): Promise<boolean>verifyRightOfAssociation— independently fetches and checks a V2 record's right-of-association validation.verifyRightOfAssociation(connection: Connection, record: Record, domain: string, verifier?: Buffer): Promise<boolean>
Record Writes
Each function below synchronously returns one TransactionInstruction:
createRecordcreateRecord(domain: string, record: Record, content: string, owner: PublicKey, payer: PublicKey): TransactionInstructionupdateRecordupdateRecord(domain: string, record: Record, content: string, owner: PublicKey, payer: PublicKey): TransactionInstructiondeleteRecorddeleteRecord(domain: string, record: Record, owner: PublicKey, payer: PublicKey): TransactionInstructionsetRecordStalenessVerifiersetRecordStalenessVerifier(domain: string, record: Record, owner: PublicKey, payer: PublicKey, verifier: PublicKey): TransactionInstructionsetRecordRoaVerifiersetRecordRoaVerifier(domain: string, record: Record, owner: PublicKey, payer: PublicKey, verifier: PublicKey): TransactionInstructionvalidateRecordRoavalidateRecordRoa(domain: string, record: Record, owner: PublicKey, payer: PublicKey, verifier: PublicKey): TransactionInstructionvalidateRecordRoaEthereum—signatureis 64 bytes andexpectedPubkeyis a 20-byte Ethereum address.validateRecordRoaEthereum(domain: string, record: Record, owner: PublicKey, payer: PublicKey, signature: Buffer, expectedPubkey: Buffer): TransactionInstruction
Registration And Lifecycle
Registration is limited to a lowercase top-level .sns name. Return shapes differ by operation:
registerDomain— returns the registration instruction and may prepend an idempotent referrer associated-token-account instruction. The selected mint must have a configured Pyth price feed.registerDomain(domain: string, space: number, buyer: PublicKey, buyerTokenAccount: PublicKey, mint?: PublicKey, referrerKey?: PublicKey): Promise<TransactionInstruction[]>registerDomainWithNft— returns one Wolves-NFT registration instruction. DerivenameAccountandreverseLookupAccountbefore calling it.registerDomainWithNft(domain: string, space: number, nameAccount: PublicKey, reverseLookupAccount: PublicKey, buyer: PublicKey, nftSource: PublicKey, nftMint: PublicKey): TransactionInstructiontransferDomain— retrieves the current owner and returns one transfer instruction.transferDomain(connection: Connection, domain: string, newOwner: PublicKey): Promise<TransactionInstruction>burnDomain— returns one burn instruction;targetreceives reclaimed lamports.burnDomain(domain: string, owner: PublicKey, target: PublicKey): TransactionInstructionsetPrimaryDomain— returns one instruction for an already derived domain account.setPrimaryDomain(connection: Connection, nameAccount: PublicKey, owner: PublicKey): Promise<TransactionInstruction>setBackground— returns the instructions required to set an issued custom background.setBackground(connection: Connection, domain: string, bg: CustomBg, owner: PublicKey): Promise<TransactionInstruction[]>
Subdomains
createSubdomain— returns the name-account creation instruction and, when the reverse account is absent, a reverse-lookup instruction.subdomainmust be a lowercase one-level.snssubdomain.createSubdomain(connection: Connection, subdomain: string, owner: PublicKey, space?: number, feePayer?: PublicKey): Promise<TransactionInstruction[]>transferSubdomain— returns one transfer instruction. Whenowneris omitted, the function retrieves the current owner; setisParentOwnerSignerwhen the parent owner authorizes the transfer.transferSubdomain(connection: Connection, subdomain: string, newOwner: PublicKey, isParentOwnerSigner?: boolean, owner?: PublicKey): Promise<TransactionInstruction>findSubdomains— returns reverse-resolved subdomain labels for a parent name-account key.findSubdomains(connection: Connection, parentKey: PublicKey): Promise<string[]>
Ownership And Reverse Lookup
getSnsDomainKeysForOwner— returns directly registry-owned top-level name-account keys.getSnsDomainKeysForOwner(connection: Connection, wallet: PublicKey): Promise<PublicKey[]>getSnsDomainsForOwner— adds TLD-less reverse names to those directly owned keys; it excludes tokenized domains, subdomains, and entries without reverse data.getSnsDomainsForOwner(connection: Connection, wallet: PublicKey): Promise<{ domain: string; key: PublicKey }[]>getSnsNftsForOwner— returns tokenized domains with reverse data.getSnsNftsForOwner(connection: Connection, owner: PublicKey): Promise<{ domain: string; key: PublicKey; mint: PublicKey }[]>getAllSnsDomains— returns raw top-level registry program accounts whoseaccount.datais sliced to the 32-byte registry-owner field.getAllSnsDomains(connection: Connection): Promise<GetProgramAccountsResponse>getPrimaryDomain— returns the primary name-account key, TLD-less reverse name, and stale status.getPrimaryDomain(connection: Connection, owner: PublicKey): Promise<{ domain: PublicKey; reverse: string; stale: boolean }>getMultiplePrimaryDomains— returns TLD-less primary names in input order; missing entries areundefined.getMultiplePrimaryDomains(connection: Connection, wallets: PublicKey[]): Promise<(string | undefined)[]>reverseLookup— returns one TLD-less reverse name.reverseLookup(connection: Connection, nameAccount: PublicKey, parent?: PublicKey): Promise<string>reverseLookupBatch— returns TLD-less names in input order; missing reverse records areundefined.reverseLookupBatch(connection: Connection, nameAccounts: PublicKey[]): Promise<(string | undefined)[]>
Advanced APIs
For account-level integrations, the root export also includes derivation and raw name-registry helpers such as getSnsDomainKeySync, getReverseKeySync, getReverseKeyFromDomainKey, getHashedNameSync, getNameAccountKeySync, createNameRegistry, updateNameRegistry, deleteNameRegistry, and createReverse.
NFT helpers and state classes include domain-mint/owner/record retrieval, NameRegistryState, PrimaryDomain, and NFT state exports. CustomBg, getCustomBgKeys, and setBackground support issued custom backgrounds. The devnet export provides devnet-specific bindings and constants. Low-level instruction classes and raw state decoders are also exported for specialized integrations.
Legacy Twitter registry APIs remain exported for advanced compatibility use. They are not part of the recommended domain-resolution or registration path.
Documentation And Migration
License
This project is available under the MIT License.
