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.