Name Service
Name registry
The name registry stores information about the domain name. It is made of two things:
- The header
- The data
The data for a domain name is always prefixed by the header, below is the structure of the header in JS:
import { PublicKey } from "@solana/web3.js";
import { Schema } from "borsh";
export class NameRegistryState {
  parentName: PublicKey;
  owner: PublicKey;
  class: PublicKey;
  data: Buffer | undefined;
  static HEADER_LEN = 96;
  static schema: Schema = new Map([
    [
      NameRegistryState,
      {
        kind: "struct",
        fields: [
          ["parentName", [32]],
          ["owner", [32]],
          ["class", [32]],
        ],
      },
    ],
  ]);
  constructor(obj: {
    parentName: Uint8Array;
    owner: Uint8Array;
    class: Uint8Array;
  }) {
    this.parentName = new PublicKey(obj.parentName);
    this.owner = new PublicKey(obj.owner);
    this.class = new PublicKey(obj.class);
  }
}
export class NameRegistryState {
    parentName: PublicKey;
    owner: PublicKey;
    class: PublicKey;
    data: Buffer | undefined;
  
    static HEADER_LEN = 96;
  
    static schema: Schema = new Map([
      [
        NameRegistryState,
        {
          kind: "struct",
          fields: [
            ["parentName", [32]],
            ["owner", [32]],
            ["class", [32]],
          ],
        },
      ],
    ]);
    constructor(obj: {
      parentName: Uint8Array;
      owner: Uint8Array;
      class: Uint8Array;
    }) {
      this.parentName = new PublicKey(obj.parentName);
      this.owner = new PublicKey(obj.owner);
      this.class = new PublicKey(obj.class);
    }
Resolving SOL domains
.SOL domains are unique, human-friendly domain names that convert to publicKeys. Many wallets use these as another option to send tokens or SOL. You can convert .SOL domains to their publicKey with the following:
import {
  getHashedName,
  getNameAccountKey,
  NameRegistryState,
} from "@bonfida/spl-name-service";
import { Connection, clusterApiUrl, PublicKey } from "@solana/web3.js";
(async () => {
  const domain = "levi.sol";
  const hashedName = await getHashedName(domain.replace(".sol", ""));
  const nameAccountKey = await getNameAccountKey(
    hashedName,
    undefined,
    new PublicKey("58PwtjSDuFHuUkYjH9BYnnQKHfwo9reZhC2zMJv9JPkx") // SOL TLD Authority
  );
  const owner = await NameRegistryState.retrieve(
    new Connection(clusterApiUrl("mainnet-beta")),
    nameAccountKey
  );
  console.log(owner.owner.toBase58());
  // JUskoxS2PTiaBpxfGaAPgf3cUNhdeYFGMKdL6mZKKfR
})();
const domain = "levi.sol";
const hashedName = await getHashedName(domain.replace(".sol", ""));
const nameAccountKey = await getNameAccountKey(
    hashedName,
    undefined,
    new PublicKey("58PwtjSDuFHuUkYjH9BYnnQKHfwo9reZhC2zMJv9JPkx") // SOL TLD Authority
);
const owner = await NameRegistryState.retrieve(
    new Connection(clusterApiUrl("mainnet-beta")),
    nameAccountKey
);
console.log(owner.owner.toBase58());
// JUskoxS2PTiaBpxfGaAPgf3cUNhdeYFGMKdL6mZKKfR
Reverse look up
This can be used to resolve the domain name from its public key
import { performReverseLookup } from "@bonfida/spl-name-service";
import { PublicKey, Connection, clusterApiUrl } from "@solana/web3.js";
async () => {
  const connection = new Connection(clusterApiUrl("mainnet-beta"));
  // Public key of bonfida.sol
  const domainKey = new PublicKey(
    "Crf8hzfthWGbGbLTVCiqRqV5MVnbpHB1L9KQMd6gsinb"
  );
  const domainName = await performReverseLookup(connection, domainKey); // bonfida
};
// Public key of bonfida.sol
const domainKey = new PublicKey("Crf8hzfthWGbGbLTVCiqRqV5MVnbpHB1L9KQMd6gsinb");
const domainName = await performReverseLookup(connection, domainKey); // bonfida
Subdomain look up
In order to resolve a subdomain you need to:
- Get the parent domain key
- Get the subdomain key
- Retrieve the account info
import {
  getHashedName,
  getNameAccountKey,
  NameRegistryState,
  getDNSRecordAddress,
} from "@bonfida/spl-name-service";
import { Connection, clusterApiUrl, PublicKey } from "@solana/web3.js";
async () => {
  const SOL_TLD_AUTHORITY = new PublicKey(
    "58PwtjSDuFHuUkYjH9BYnnQKHfwo9reZhC2zMJv9JPkx"
  );
  const connection = new Connection(clusterApiUrl("mainnet-beta"));
  // Resolution of demo.bonfida.sol
  const parentDomain = "bonfida";
  const subDomain = "demo";
  // Step 1
  const hashedParentDomain = await getHashedName(parentDomain);
  const parentDomainKey = await getNameAccountKey(
    hashedParentDomain,
    undefined,
    SOL_TLD_AUTHORITY
  );
  // Step 2
  const subDomainKey = await getDNSRecordAddress(parentDomainKey, subDomain);
  // Step 3
  const registry = await NameRegistryState.retrieve(connection, subDomainKey);
};
const parentDomain = "bonfida";
const subDomain = "demo";
// Step 1
const hashedParentDomain = await getHashedName(mainDomain);
const parentDomainKey = await getNameAccountKey(
  hashedParentDomain,
  undefined,
  SOL_TLD_AUTHORITY
);
// Step 2
const subDomainKey = await getDNSRecordAddress(parentDomainKey, subDomain);
// Step 3
const registry = await NameRegistryState.retrieve(connection, subDomainKey);
Find all the domain names owned by a public key
You can retrieve all the domain names of a wallet by doing a getProgramAccounts request with a memcmp filter
import { Connection, PublicKey } from "@solana/web3.js";
import { NAME_PROGRAM_ID } from "@bonfida/spl-name-service";
export async function findOwnedNameAccountsForUser(
  connection: Connection,
  userAccount: PublicKey
): Promise<PublicKey[]> {
  const filters = [
    {
      memcmp: {
        offset: 32,
        bytes: userAccount.toBase58(),
      },
    },
  ];
  const accounts = await connection.getProgramAccounts(NAME_PROGRAM_ID, {
    filters,
  });
  return accounts.map((a) => a.publicKey);
}
export async function findOwnedNameAccountsForUser(
  connection: Connection,
  userAccount: PublicKey
): Promise<PublicKey[]> {
  const filters = [
    {
      memcmp: {
        offset: 32,
        bytes: userAccount.toBase58(),
      },
    },
  ];
  const accounts = await connection.getProgramAccounts(NAME_PROGRAM_ID, {
    filters,
  });
  return accounts.map((a) => a.publicKey);
}
Resolve a Twitter handle
Twitter handles can be registered on the Solana name service and be used like .SOL domain names
import { PublicKey, clusterApiUrl, Connection } from "@solana/web3.js";
import { getHandleAndRegistryKey } from "@bonfida/spl-name-service";
async () => {
  const connection = new Connection(clusterApiUrl("mainnet-beta"));
  // Pubkey of the wallet you want to retrieve the Twitter handle
  const pubkey = new PublicKey("FidaeBkZkvDqi1GXNEwB8uWmj9Ngx2HXSS5nyGRuVFcZ");
  const [handle, registryKey] = await getHandleAndRegistryKey(
    connection,
    pubkey
  );
};
// Pubkey of the wallet you want to retrieve the Twitter handle
const pubkey = new PublicKey("FidaeBkZkvDqi1GXNEwB8uWmj9Ngx2HXSS5nyGRuVFcZ");
const [handle, registryKey] = await getHandleAndRegistryKey(connection, pubkey);
Reverse look up of a Twitter handle
In order to find the SOL address associated to a Twitter handle you can perform a reverse look up
import { getTwitterRegistry } from "@bonfida/spl-name-service";
import { Connection, clusterApiUrl } from "@solana/web3.js";
async () => {
  const handle = "bonfida";
  const connection = new Connection(clusterApiUrl("mainnet-beta"));
  const registry = await getTwitterRegistry(connection, handle);
};
const handle = "bonfida";
const registry = await getTwitterRegistry(connection, handle);