Skip to main content

Rust SDK v2

Use this guide to migrate an application from Rust SDK v1 to Rust SDK v2.

Install v2

The default build exposes asynchronous RPC APIs under sns_sdk::non_blocking:

[dependencies]
sns-sdk = "2"
solana-client = "2.1"
solana-sdk = "2.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }

For synchronous RPC APIs, disable default features and enable blocking:

[dependencies]
sns-sdk = { version = "2", default-features = false, features = ["blocking"] }
solana-client = "2.1"
solana-sdk = "2.1"

Add subdomain to the blocking feature list when the application uses sub-registrar APIs.

Import v2 APIs

The examples use the default non-blocking modules:

use sns_sdk::{
derivation::get_sns_domain_key,
non_blocking::{
domain::get_sns_domains_for_owner,
primary_domain::get_primary_domain,
resolve::{resolve, AllowPda},
},
record::Record,
};

In a blocking build, use the equivalent modules under sns_sdk::blocking and remove .await from RPC calls.

Update domain inputs

Use the input form required by each API family:

API familyv1 inputv2 input
High-level reads, including resolution and record readsBare name or legacy .sol nameFull name with a supported suffix, such as name.sns
Registration and V2 record writesAPI-dependent bare or legacy .sol nameLowercase full .sns name
SNS and record-key derivationBare name or legacy .sol nameTLD-less name, such as name or sub.name
Raw name-registry buildersRaw name or explicit account keyRaw name or explicit account key

Do not append .sns to derivation helpers or raw name-registry inputs.

.sol resolution and reads

SNS-backed .sol resolution and reads pause automatically at finalized slot 452,825,395 to transition .sol domains to the SRS program; slot checks are not needed at the application level. SRS-backed .sol resolution will be enabled in a future SDK update.

Update resolution

Rename resolve_owner to resolve, pass a full .sns name, and add the required AllowPda argument:

v1
use sns_sdk::non_blocking::resolve::resolve_owner;

let owner = resolve_owner(&client, "name").await?;
v2
use sns_sdk::non_blocking::resolve::{resolve, AllowPda};

let owner = resolve(&client, "name.sns", AllowPda::Deny).await?;
// Or use `safe_resolve`.

Remove None handling from successful resolution. A domain that does not exist now returns SnsError::DomainDoesNotExist.

Use AllowPda::Deny unless the application intentionally supports program-derived resolution targets.

New API: safe_resolve

A new safe_resolve API has also been added, currently functioning identically to resolve. Once SRS-backed .sol resolution is enabled in a future update, safe_resolve will only return an address if the .sol domain and the corresponding .sns domain resolve to the same address — returning an error on mismatch.

This is intended as a short-term safety measure for the period immediately following SRS-backed .sol enablement. In the long term, .sns and .sol should be treated as separate namespaces, and integrations should use resolve instead.

Recommended alternative: rather than relying on safe_resolve (which throws on mismatch), apps and wallets can implement custom logic to detect resolution mismatches and surface a warning or confirmation dialog to the user.

Update primary-domain lookup

Rename favourite-domain APIs and move the RPC lookup into its mode-specific module:

v1
use sns_sdk::non_blocking::resolve::get_favourite_domain;

let primary = get_favourite_domain(&client, &owner).await?;
v2
use sns_sdk::non_blocking::primary_domain::get_primary_domain;

let primary = get_primary_domain(&client, &owner).await?;

The result remains Option<Pubkey> containing the primary name-account key, not a display name. Other renamed symbols include FavouriteDomain to PrimaryDomain, register_favourite to set_primary_domain, and get_register_favourite_instruction to set_primary_domain_instruction.

Update owner-domain lookup

Rename get_domains_owner and move it into the domain module:

v1
let domains = sns_sdk::non_blocking::resolve::get_domains_owner(
&client,
owner,
)
.await?;
v2
let domains = sns_sdk::non_blocking::domain::get_sns_domains_for_owner(
&client,
owner,
)
.await?;

The result remains Vec<Pubkey> containing top-level name-account keys owned by the wallet. Tokenized domains are not included.

For tokenized domains, rename get_tokenized_domains to nft::get_sns_nfts_for_owner. Its result changes from Vec<(String, Pubkey)> to Vec<SnsNftDomain> with reverse, key, and mint fields.

Update key derivation

Replace get_domain_key with get_sns_domain_key, keep the input TLD-less, and read the returned .key field:

v1
let key = sns_sdk::derivation::get_domain_key("sub.name.sol")?;
v2
let domain = get_sns_domain_key("sub.name")?;
let key = domain.key;

The v2 DomainKeyWithParent also exposes parent and is_sub. get_srs_domain_key("name") derives an SRS address only; it does not enable SRS-backed resolution.

Update record reads

Move record getters into versioned, mode-specific modules:

v1v2
resolve::resolve_recordrecord_v1::get_record
record::record_v2::retrieve_record_v2record_v2::get_record_v2
record::record_v2::retrieve_records_batch_v2record_v2::get_multiple_records_v2

V2 getters now borrow the RPC client, take the domain before the record, and require a full domain:

v2
let record = sns_sdk::non_blocking::record_v2::get_record_v2(
&client,
"name.sns",
Record::Url,
)
.await?;

The result remains optional when the record account does not exist. Returned bytes have the SPL Name Registry header removed; pass the payload to decode_record_v2_fields before parsing and validating it.

Update registration

Replace the mode-specific register_domain_name RPC helper with the shared instruction builder:

v1
let transaction = sns_sdk::non_blocking::register::register_domain_name(
&client,
"name",
space,
&buyer,
&buyer_token_account,
None,
None,
)
.await?;
v2
let instructions = sns_sdk::bindings::register_domain::register_domain(
"name.sns",
space,
&buyer,
&buyer_token_account,
None,
None,
)?;

The v2 builder has no RPC argument and returns Vec<Instruction> instead of an unsigned Transaction. The application constructs the transaction, selects its fee payer, fetches a recent blockhash, collects required signatures, and submits it.

Verify the migration

  • Rename resolve_owner, add AllowPda, pass a full .sns name, and remove successful None handling.
  • Update primary-domain, owner-domain, tokenized-domain, and record module paths and result handling.
  • Pass full lowercase .sns domain names to registration and V2 record builders.
  • Keep address-derivation inputs TLD-less and raw registry inputs unchanged.
  • Update frontend formatting to display TLD-less SNS domains with .sns suffix.
  • If your app displays tokenized-domain artwork, download and bundle this image instead of using the NFT metadata image.