TLD Parser Dart SDK

A Dart SDK for parsing and resolving AllDomains ANS Protocol domains on Solana. Supports any TLD including .skr, .abc, .poor, .bonk, and more.

Key Insight: ANS vs SNS

Feature SNS (Bonfida) ANS (AllDomains)
TLDs Supported Only .sol Any TLD (.skr, .abc, .poor, etc.)
Hash Prefix SPL Name Service ALT Name Service
Program ID namesLPne... ALTNSZ46uaAUU7XUV6awvdorLGqAsPwa9shm7h4uP2FK

The existing SNS Dart packages only support .sol domains. This SDK is for all other TLDs.

Installation

Add to your pubspec.yaml:

dependencies:
  tld_parser: ^0.1.0

Quick Start

import 'package:tld_parser/tld_parser.dart';
import 'package:solana/solana.dart';

void main() async {
  // Initialize
  final rpcClient = RpcClient('https://api.mainnet-beta.solana.com');
  final parser = TldParser(rpcClient);

  // Resolve a domain to its owner
  final owner = await parser.getOwnerFromDomainTld('miester.abc');
  print('Owner: ${owner?.toBase58()}');

  // Get a user's main domain (reverse lookup)
  final mainDomain = await parser.tryGetMainDomain(owner!);
  print('Main domain: ${mainDomain?.fullDomain}');

  // Fetch domain records
  final twitter = await parser.getRecord('miester.abc', Record.twitter);
  print('Twitter: $twitter');

  // Get all domains owned by a user
  final domains = await parser.getAllUserDomainsFromTld(owner, 'abc');
  print('User owns ${domains.length} .abc domains');
}

Features

Domain Resolution

// Get owner of any domain
final owner = await parser.getOwnerFromDomainTld('miester.abc');

// Works with subdomains too
final subOwner = await parser.getOwnerFromDomainTld('vault.miester.abc');

// Get the full name record with metadata
final record = await parser.getNameRecordFromDomainTld('miester.abc');
print('Created: ${record?.createdAtDateTime}');
print('Expires: ${record?.expiresAtDateTime}');
print('Is valid: ${record?.isValid}');

Reverse Lookup

// Get a user's main domain
final mainDomain = await parser.getMainDomain(userPubkey);
print('${mainDomain.fullDomain}'); // e.g., 'miester.abc'

// Try without throwing (returns null if not set)
final maybe = await parser.tryGetMainDomain(userPubkey);

Domain Listing

// Get all domains owned by a user (all TLDs)
final allDomains = await parser.getAllUserDomains(userPubkey);

// Get domains in a specific TLD
final abcDomains = await parser.getAllUserDomainsFromTld(userPubkey, 'abc');

// Get parsed domain names (with reverse lookups)
final parsed = await parser.getParsedAllUserDomainsFromTld(userPubkey, 'abc');
for (final d in parsed) {
  print('${d.domain} -> ${d.nameAccount.toBase58()}');
}

Record Fetching

// Get a specific record
final twitter = await parser.getRecord('miester.abc', Record.twitter);
final email = await parser.getRecord('miester.abc', Record.email);

// Get multiple records at once (efficient batching)
final records = await parser.getRecords('miester.abc', [
  Record.twitter,
  Record.discord,
  Record.github,
  Record.email,
]);

// Get all available records
final allRecords = await parser.getAllRecords('miester.abc');

Available Records

The SDK supports the following record types:

Content/Storage:

  • Record.ipfs - IPFS content hash
  • Record.arwv - Arweave content hash
  • Record.url - URL link
  • Record.shdw - Shadow Drive

Crypto Addresses:

  • Record.sol, Record.eth, Record.btc
  • Record.aptos, Record.near, Record.sui
  • Record.base, Record.stacks
  • Record.ltc, Record.doge, Record.point, Record.lattica

Social:

  • Record.twitter, Record.discord
  • Record.github, Record.reddit, Record.telegram

Profile:

  • Record.pic - Profile picture
  • Record.email - Email address

NFT-Wrapped Domains

The SDK automatically handles NFT-wrapped domains. When a domain is wrapped as an NFT, the SDK resolves through the NFT record to find the actual token holder:

// This works whether the domain is wrapped or not
final owner = await parser.getOwnerFromDomainTld('miester.abc');

Domain Key Derivation

For advanced use cases, you can derive domain keys directly:

// Derive a domain key
final domainKey = await getDomainKey('miester.abc');
print('Domain account: ${domainKey.pubkey.toBase58()}');

// Derive a subdomain key
final subKey = await getDomainKey('vault.miester.abc');
print('Is subdomain: ${subKey.isSub}'); // true
print('Parent: ${subKey.parent?.toBase58()}');

// Derive a record key
final recordKey = await getDomainKey('Twitter.miester.abc', record: true);

Error Handling

try {
  final owner = await parser.getOwnerFromDomainTld('nonexistent.abc');
} on DomainNotFoundException catch (e) {
  print('Domain not found: ${e.domain}');
} on InvalidDomainFormatException catch (e) {
  print('Invalid format: ${e.message}');
} on TldParserException catch (e) {
  print('Parser error: ${e.message}');
}

Architecture

tld_parser_dart/
├── lib/
│   ├── tld_parser.dart           # Main export
│   └── src/
│       ├── constants.dart        # Program IDs, prefixes
│       ├── utils.dart            # Hashing, seeds
│       ├── pda.dart              # PDA derivation
│       ├── name_record_handler.dart  # getDomainKey
│       ├── tld_parser.dart       # Main TldParser class
│       ├── exceptions.dart       # Exception types
│       ├── state/
│       │   ├── name_record_header.dart
│       │   ├── main_domain.dart
│       │   └── nft_record.dart
│       └── types/
│           ├── records.dart
│           ├── domain_key_result.dart
│           └── tag.dart
├── test/
│   └── ...
└── example/
    └── example.dart

Performance

The SDK is optimized for performance:

  • Batched RPC calls: Multiple accounts are fetched in single calls
  • Chunking: Large requests are automatically chunked to respect RPC limits
  • Efficient PDAs: Program addresses are derived using optimized algorithms

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT License - see LICENSE for details.

References

Libraries

tld_parser
TLD Parser - AllDomains ANS Protocol SDK for Dart