Multi-network SDK for ODude Name Service ecosystem - TypeScript ready, tree-shakeable, and compatible with Node.js, browsers, and modern bundlers
npm install @odude/odude-sdkA comprehensive, developer-friendly npm package for interacting with the ODude smart contracts ecosystem across multiple blockchain networks. This SDK provides easy-to-use wrappers for Registry, Resolver, TLD, and RWAirdrop contracts with seamless multi-network support.
- ๐ Simple and intuitive API - Easy to use for both beginners and experts
- ๐ Multi-network support - Base Sepolia, Filecoin, BNB Chain, Localhost
- ๐ Automatic TLD-based network routing - Smart routing based on domain TLD
- ๐ฆ Full TypeScript support - Complete type definitions and IntelliSense
- ๐ง Built on ethers.js v6 - Modern, secure, and well-maintained foundation
- ๐งช Comprehensive test coverage - Thoroughly tested with 50+ test cases
- ๐ Well-documented with examples - Extensive documentation and examples
- ๐ฏ Helper utilities - Name parsing, formatting, and validation utilities
- ๐ Batch operations - Efficient bulk queries and operations
- ๐ก Event monitoring - Real-time event listening across networks
- โก Retry logic - Configurable retry limits for network resilience
- ๐ณ Tree-shakeable - Import only what you need for optimal bundle size
- ๐ Dual module support - Works with both CommonJS and ES Modules
- ๐ Universal compatibility - Node.js, browsers, Next.js, and modern bundlers
bash
npm install @odude/odude-sdk
`$3
`bash
yarn add @odude/odude-sdk
`$3
`bash
pnpm add @odude/odude-sdk
`$3
- Node.js 16+ (for Node.js environments)
- Modern browser with ES2020 support (for browser environments)
- TypeScript 4.5+ (for TypeScript projects)Table of Contents
1. Quick Start
2. TypeScript Usage
3. Configuration
4. Core Concepts
5. Import Patterns
6. API Reference
7. Extended Functions
8. Usage Examples
9. Testing
10. Browser Usage
11. Next.js Integration
12. Troubleshooting
Quick Start
$3
The ODude SDK supports multiple networks with automatic TLD-based routing:
`javascript
const ODudeSDK = require('@odude/odude-sdk');// Initialize SDK with multiple network RPC URLs
const sdk = new ODudeSDK({
rpcUrl_filecoin: 'https://api.node.glif.io',
rpcUrl_bnb: 'https://bsc-dataseed1.binance.org',
rpcUrl_sepolia: 'https://sepolia.base.org'
});
// Connect to all networks
sdk.connectAllNetworks();
// The SDK automatically routes based on TLD:
// - @fil, @fvm domains โ Filecoin network
// - @bnb, @binance domains โ BNB Smart Chain
// - Other domains โ Base Sepolia (default)
// Resolve names automatically routes to correct network
const address = await sdk.resolve('alice@fil'); // Uses Filecoin network
const bnbAddress = await sdk.resolve('bob@bnb'); // Uses BNB network
`Note: Currently, only Base Sepolia network has deployed contracts and is fully functional.
TypeScript Usage
The ODude SDK is built with TypeScript and provides comprehensive type definitions for all functions, interfaces, and classes.
$3
`typescript
import ODudeSDK, {
ODudeSDKConfig,
NameInfo,
NetworkInfo,
NameNotFoundError
} from '@odude/odude-sdk';// Type-safe configuration
const config: ODudeSDKConfig = {
rpcUrl_sepolia: 'https://sepolia.base.org',
rpcUrl_filecoin: 'https://api.node.glif.io',
privateKey: process.env.PRIVATE_KEY
};
// Create SDK instance with full type support
const sdk = new ODudeSDK(config);
// Connect to networks
await sdk.connectAllNetworks();
// Type-safe method calls with IntelliSense
try {
const address: string = await sdk.resolve('alice@crypto');
const nameInfo: NameInfo = await sdk.getNameInfo('alice@crypto');
const networkInfo: NetworkInfo = sdk.NetworkList();
console.log('Resolved address:', address);
console.log('Name info:', nameInfo);
} catch (error) {
if (error instanceof NameNotFoundError) {
console.log('Name not found:', error.domainName);
}
}
`$3
`typescript
import {
ODudeSDK,
Registry,
Resolver,
TLD,
RWAirdrop,
AirdropInfo,
MintEligibility,
TransferCallback,
NameResolvedCallback
} from '@odude/odude-sdk';
import { BigNumberish, ContractTransactionResponse } from 'ethers';class ODudeManager {
private sdk: ODudeSDK;
constructor(config: ODudeSDKConfig) {
this.sdk = new ODudeSDK(config);
}
async initialize(): Promise {
await this.sdk.connectAllNetworks();
this.setupEventListeners();
}
private setupEventListeners(): void {
// Type-safe event callbacks
const transferCallback: TransferCallback = (from, to, tokenId) => {
console.log(
Transfer: ${from} โ ${to} (Token: ${tokenId}));
}; const nameResolvedCallback: NameResolvedCallback = (name, address) => {
console.log(
Name resolved: ${name} โ ${address});
}; this.sdk.onTransfer(transferCallback);
this.sdk.onNameResolved(nameResolvedCallback);
}
async mintDomain(
domainName: string,
recipient: string
): Promise {
const tld: TLD = this.sdk.tld();
// Check eligibility with full type safety
const eligibility: MintEligibility = await tld.checkMintEligibility(domainName);
if (!eligibility.eligible) {
throw new Error(
Cannot mint ${domainName}: ${eligibility.reason});
} // Mint with proper typing
return await tld.mintDomain(domainName, recipient, {
value: eligibility.cost
});
}
async getAirdropStatus(address: string): Promise {
const rwairdrop: RWAirdrop = this.sdk.rwairdrop();
return await rwairdrop.getAirdropInfo(address);
}
}
// Usage
const manager = new ODudeManager({
rpcUrl_sepolia: 'https://sepolia.base.org',
privateKey: process.env.PRIVATE_KEY
});
await manager.initialize();
`$3
The SDK exports comprehensive TypeScript types:
`typescript
// Configuration types
import type {
ODudeSDKConfig,
ContractAddresses,
NetworkConfig,
NetworkInfo
} from '@odude/odude-sdk';// Data types
import type {
NameInfo,
NFTMetadata,
ResolutionRecord,
ReverseRecord,
AirdropInfo,
MintEligibility,
ParsedName
} from '@odude/odude-sdk';
// Error types
import type {
ODudeSDKError,
NameNotFoundError,
NetworkError,
UnsupportedTLDError
} from '@odude/odude-sdk';
// Callback types
import type {
TransferCallback,
NameResolvedCallback,
DomainMintedCallback
} from '@odude/odude-sdk';
`Configuration
$3
You can also configure RPC URLs using environment variables:
`bash
Set environment variables
export FILECOIN_RPC_URL="https://your-filecoin-rpc.com"
export BNB_RPC_URL="https://your-bnb-rpc.com"
export BASE_SEPOLIA_RPC_URL="https://your-base-sepolia-rpc.com"
``javascript
// SDK will automatically use environment variables
const sdk = new ODudeSDK();
sdk.connectAllNetworks();
`$3
`javascript
const ODudeSDK = require('odude-sdk');// Initialize SDK with localhost
const sdk = new ODudeSDK({
rpcUrl: 'http://127.0.0.1:8545'
});
// Connect to deployed contracts
sdk.connectLocalhost();
// Now you can use all contracts
const totalSupply = await sdk.registry().totalSupply();
console.log('Total names registered:', totalSupply.toString());
`$3
`javascript
const ODudeSDK = require('odude-sdk');// Connect to a specific network
const sdk = new ODudeSDK();
sdk.connectNetwork('basesepolia'); // or 'filecoin', 'bnb', 'localhost'
// Access contracts for the connected network
const registry = sdk.registry;
const resolver = sdk.resolver;
`$3
`javascript
const { Wallet } = require('ethers');
const ODudeSDK = require('odude-sdk');const sdk = new ODudeSDK({
rpcUrl: 'http://127.0.0.1:8545',
privateKey: 'YOUR_PRIVATE_KEY'
});
sdk.connectLocalhost();
// Now you can perform write operations
const tx = await sdk.registry().setReverse(tokenId);
await tx.wait();
`Core Concepts
$3
The ODude SDK is designed to work seamlessly across multiple blockchain networks:
- Base Sepolia (default): Primary testnet with full contract deployment
- Filecoin: For FIL/FVM ecosystem domains
- BNB Chain: For BNB/Binance ecosystem domains
- Localhost: For local development and testing
$3
The SDK automatically routes operations to the correct network based on the TLD (Top-Level Domain):
`javascript
// These automatically route to the correct network
await sdk.resolve('alice@fil'); // โ Filecoin network
await sdk.resolve('bob@bnb'); // โ BNB Chain
await sdk.resolve('charlie@crypto'); // โ Base Sepolia (default)
`TLD Mappings:
-
@fil, @fvm โ Filecoin network
- @bnb, @binance โ BNB Chain
- All other TLDs โ Base Sepolia (default network)$3
The SDK provides access to four main contracts:
1. Registry: NFT-based name ownership and management
2. Resolver: Name-to-address resolution and reverse resolution
3. TLD: Top-level domain management and pricing
4. RWAirdrop: Token airdrop functionality for domain holders
$3
ODude uses the
@ symbol for domain separation (not .):`javascript
// โ
Correct format
'alice@crypto'
'subdomain@alice@crypto'
'test@fil'// โ Incorrect format
'alice.crypto'
'test.fil'
`$3
The SDK includes intelligent network detection with automatic retry:
- Maximum 3 retry attempts for network detection
- Graceful fallback for failed connections
- Detailed error logging for debugging
- Skip network detection for external RPCs to avoid timeouts
$3
The SDK manages providers and signers for each network:
`javascript
// Get provider for specific network
const provider = sdk.getProvider('basesepolia');// Get signer for write operations
const signer = sdk.getSigner('basesepolia');
// Connect custom signer
sdk.connectSigner(customSigner, 'basesepolia');
`Import Patterns
The ODude SDK supports multiple import patterns for maximum compatibility:
$3
`javascript
// Default import
const ODudeSDK = require('@odude/odude-sdk');
const sdk = new ODudeSDK();// Named imports
const { ODudeSDK, Registry, Resolver, utils } = require('@odude/odude-sdk');
// Error imports
const { NameNotFoundError, NetworkError } = require('@odude/odude-sdk');
`$3
`javascript
// Default import
import ODudeSDK from '@odude/odude-sdk';
const sdk = new ODudeSDK();// Named imports
import { ODudeSDK, Registry, Resolver, utils } from '@odude/odude-sdk';
// Mixed imports
import ODudeSDK, { NameNotFoundError, NetworkError } from '@odude/odude-sdk';
// Tree-shaking friendly imports
import { utils } from '@odude/odude-sdk';
const normalized = utils.normalizeName('TEST@CRYPTO');
`$3
`typescript
// Default import with types
import ODudeSDK, { ODudeSDKConfig } from '@odude/odude-sdk';// Named imports with types
import {
ODudeSDK,
Registry,
NameInfo,
NetworkError
} from '@odude/odude-sdk';
// Type-only imports
import type {
ODudeSDKConfig,
NameInfo,
AirdropInfo
} from '@odude/odude-sdk';
`$3
`html
`$3
`javascript
// Lazy loading
const loadSDK = async () => {
const { ODudeSDK } = await import('@odude/odude-sdk');
return new ODudeSDK();
};// Conditional loading
if (typeof window !== 'undefined') {
const { ODudeSDK } = await import('@odude/odude-sdk');
// Browser-specific code
}
`Usage Examples
$3
`javascript
// Get name information
const tokenId = await sdk.registry().getTokenId('alice@crypto');
const owner = await sdk.registry().ownerOf(tokenId);
const metadata = await sdk.registry().getNFTMetadata(tokenId);console.log('Token ID:', tokenId.toString());
console.log('Owner:', owner);
console.log('Metadata:', metadata);
// Get owner by name
const ownerAddress = await sdk.registry().getOwnerByName('alice@crypto');
// Get name by token ID
const name = await sdk.registry().nameOf(tokenId);
// Get total supply
const totalSupply = await sdk.registry().totalSupply();
// mintDomain a name (requires signer)
const tx = await sdk.registry().mintDomain(
tokenId,
'myname@crypto',
'ipfs://...',
recipientAddress,
{ value: ethers.parseEther('0.1') }
);
await tx.wait();
// Set reverse resolution
const reverseTx = await sdk.registry().setReverse(tokenId);
await reverseTx.wait();
// Listen to events
sdk.registry().onTLDMinted((tokenId, name, owner) => {
console.log('New TLD minted:', name, 'by', owner);
});
`$3
`javascript
// Resolve name to address
const address = await sdk.resolver().resolve('alice@crypto');
console.log('Resolved address:', address);// Reverse resolve address to name
const name = await sdk.resolver().reverse('0x...');
console.log('Primary name:', name);
// Check if name exists
const exists = await sdk.resolver().nameExists('alice@crypto');
// Get resolution record
const record = await sdk.resolver().getResolutionRecord('alice@crypto');
console.log('Resolution record:', record);
// {
// resolvedAddress: '0x...',
// name: 'alice@crypto',
// tokenId: 123n,
// exists: true
// }
// Get reverse record
const reverseRecord = await sdk.resolver().getReverseRecord('0x...');
console.log('Reverse record:', reverseRecord);
// {
// primaryName: 'alice@crypto',
// primaryTokenId: 123n,
// exists: true
// }
// Check if address has reverse
const hasReverse = await sdk.resolver().hasReverse('0x...');
`$3
`javascript
// Get base TLD price
const basePrice = await sdk.tld().getBaseTLDPrice();
console.log('Base TLD price:', ethers.formatEther(basePrice), 'ETH');// Get TLD-specific price
const tldPrice = await sdk.tld().getTLDPrice(tldTokenId);
// Get commission rate
const commission = await sdk.tld().getCommission(tldTokenId);
console.log('Commission:', commission.toString(), '%');
// Get TLD owner
const tldOwner = await sdk.tld().getTLDOwner(tldTokenId);
// Check if TLD is active
const isActive = await sdk.tld().isTLDActive(tldTokenId);
// Get TLD name
const tldName = await sdk.tld().getTLDName(tldTokenId);
// Set TLD price (requires signer and ownership)
const tx = await sdk.tld().setTLDPrice(tldTokenId, ethers.parseEther('0.05'));
await tx.wait();
// Set commission (requires signer and ownership)
const commissionTx = await sdk.tld().setCommission(tldTokenId, 15);
await commissionTx.wait();
// New TLD Management Functions
// Set ERC token for a TLD
const setErcTx = await sdk.tld().setErcToken(tldTokenId, '0x...');
await setErcTx.wait();
// Get ERC token for a TLD
const ercToken = await sdk.tld().getErcToken(tldTokenId);
// Get token price for a TLD (same as TLD price)
const tokenPrice = await sdk.tld().getTokenPrice(tldTokenId);
// Domain Minting Functions
// Check if domain is eligible for minting
const eligibility = await sdk.tld().checkMintEligibility('alice@fil');
console.log('Eligibility:', eligibility);
// {
// eligible: true,
// available: true,
// tldActive: true,
// cost: 500000000000000000n,
// reason: 'Eligible for minting'
// }
// Estimate minting cost
const cost = await sdk.tld().estimateMintCost('alice@fil');
console.log('Minting cost:', ethers.formatEther(cost), 'ETH');
// Mint domain
const mintTx = await sdk.tld().mintDomain(
'alice@fil',
'0x...',
{ value: cost }
);
await mintTx.wait();
// TLD Minting Functions
// Check TLD minting eligibility (no @ symbol for TLD names)
const tldEligibility = await sdk.tld().checkMintEligibility('mycompany');
console.log('TLD Eligibility:', tldEligibility);
// {
// eligible: true,
// available: true,
// tldActive: true, // Not applicable for TLD minting
// cost: 1000000000000000000n, // Base TLD price
// reason: 'Eligible for TLD minting'
// }
// Mint a TLD (Top-Level Domain)
const tldMintTx = await sdk.mintTLD('mycompany', '0x...', {
value: tldEligibility.cost
});
await tldMintTx.wait();
`$3
`javascript
// Get claimable airdrops for a user
const claimableAirdrops = await sdk.rwairdrop().getClaimableAirdrops('0x...');
console.log('Claimable airdrops:', claimableAirdrops.length);// Get airdrop information for a specific TLD and airdrop ID
const airdropInfo = await sdk.rwairdrop().getAirdropInfoByTLD('crypto', 0);
console.log('Airdrop info:', airdropInfo);
// {
// tokenAddress: '0x...',
// totalAmount: 1000000000000000000n,
// perUserShare: 100000000000000000n,
// remainingBalance: 900000000000000000n,
// granter: '0x...',
// tldOwner: '0x...',
// isActive: true,
// isWithdrawn: false,
// createdAt: 1234567890n,
// airdropId: 0n
// }
// Check if a domain has claimed from an airdrop
const hasClaimed = await sdk.rwairdrop().hasDomainClaimed('alice@crypto', 0);
// Get user domains in a specific TLD
const userDomains = await sdk.rwairdrop().getUserDomainsInTLD('0x...', 'crypto');
// Claim airdrop share (requires signer)
const claimTx = await sdk.rwairdrop().claimShare('crypto', 0, 'alice@crypto');
await claimTx.wait();
console.log('Airdrop claimed!');
// Create an airdrop (requires signer and TLD ownership)
const createTx = await sdk.rwairdrop().createAirdrop(
'crypto',
'0x...', // token address
ethers.parseEther('100'), // total amount
ethers.parseEther('1') // per user share
);
await createTx.wait();
// Sync your domains (new method - replaces old syncDomainOwnership)
const domains = ['alice@crypto', 'bob@crypto'];
const syncTx = await sdk.rwairdrop().syncMyDomains(domains);
await syncTx.wait();
console.log('Domains synced!');
// Admin-only: Emergency sync for data recovery
// (Only contract owner can call this)
const adminSyncTx = await sdk.rwairdrop().syncDomainOwnershipsAdmin(
['alice@crypto', 'bob@crypto'],
['0x...', '0x...'] // corresponding owners
);
await adminSyncTx.wait();
`$3
`javascript
const { utils } = sdk;// Normalize name
const normalized = utils.normalizeName('ALICE@CRYPTO');
// 'alice@crypto'
// Extract TLD
const tld = utils.extractTLD('alice@crypto');
// 'crypto'
// Extract subdomain
const subdomain = utils.extractSubdomain('alice@crypto');
// 'alice'
// Check if TLD
const isTLD = utils.isTLD('crypto');
// true
// Check if subdomain
const isSubdomain = utils.isSubdomain('alice@crypto');
// true
// Parse name
const parsed = utils.parseName('demo@alice@crypto');
// {
// full: 'demo@alice@crypto',
// tld: 'crypto',
// subdomain: 'demo@alice',
// parts: ['demo', 'alice', 'crypto'],
// isTLD: false,
// isSubdomain: true
// }
// Validate address
const isValid = utils.isValidAddress('0x...');
// Format token ID
const formatted = utils.formatTokenId(123n);
// '123'
// Parse and format ether
const wei = utils.parseEther('1.0');
const ether = utils.formatEther(wei);
// Generate random token ID
const tokenId = utils.randomTokenId();
// 1234567890 (random 10-digit number)
// Validate sub-name format
const isValidName = utils.isValidSubName('test@sepolia');
// true
const isInvalidName = utils.isValidSubName('test@@sepolia');
// false
`$3
`javascript
// Quick resolve
const address = await sdk.resolve('alice@crypto');// Quick reverse
const name = await sdk.reverse('0x...');
// Get owner (throws error if not found)
const owner = await sdk.getOwner('alice@crypto');
// Get owner safely (returns null if not found)
const ownerSafe = await sdk.getOwnerSafe('alice@crypto');
// Check if domain exists
const exists = await sdk.domainExists('alice@crypto');
// Get comprehensive domain status
const status = await sdk.getDomainStatus('alice@crypto');
console.log(status);
// {
// name: 'alice@crypto',
// exists: true,
// owner: '0x...',
// canMint: false,
// mintEligibility: null,
// inAirdropContract: true,
// claimableAirdrops: 3,
// airdropDetails: [...]
// }
// Get comprehensive name info
const nameInfo = await sdk.getNameInfo('alice@crypto');
console.log(nameInfo);
// {
// name: 'alice@crypto',
// tokenId: 123n,
// owner: '0x...',
// metadata: { ... },
// tokenURI: 'ipfs://...',
// resolvedAddress: '0x...',
// exists: true
// }
// Get user domains from RWAirdrop contract
const domains = await sdk.rwairdrop().getUserDomainsInTLD('0x...', 'crypto');
// Get airdrop info
const airdropInfo = await sdk.getAirdropInfo('0x...');
// Mint a domain (requires signer)
const mintTx = await sdk.mintDomain('alice@crypto', recipientAddress, {
value: ethers.parseEther('0.1')
});
await mintTx.wait();
// Mint a TLD (requires signer and more ETH)
const tldMintTx = await sdk.mintTLD('mycompany', recipientAddress, {
value: ethers.parseEther('1.0') // TLD minting typically costs more
});
await tldMintTx.wait();
`TLD vs Subdomain Minting
The ODude SDK supports two types of domain minting:
$3
- Format: Single name without @ symbol (e.g., crypto, sepolia, mycompany)
- Cost: Higher cost (typically 1+ ETH)
- Benefits:
- Own the entire TLD namespace
- Set pricing for subdomains under your TLD
- Earn commission from subdomain sales
- Control TLD policies and settings
- Method: sdk.mintTLD(tldName, toAddress, options)
- Example: node examples/TLDMint.js$3
- Format: Name with @ symbol (e.g., alice@crypto, bob@sepolia)
- Cost: Lower cost (set by TLD owner)
- Benefits:
- Get a unique name under an existing TLD
- Use for identity, websites, or applications
- Transfer ownership like any NFT
- Method: sdk.mintDomain(domainName, toAddress, options)
- Example: node examples/SubNameMint.js$3
- Eligibility Checking: TLD minting checks if TLD already exists; subdomain minting checks if TLD is active and subdomain is available
- Pricing: TLD uses base TLD price; subdomain uses TLD-specific pricing
- Registration: TLD minting includes automatic TLD registration; subdomain minting only creates the NFTEnhanced Domain Management
The ODude SDK v2.0+ includes enhanced domain management features with improved error handling, automatic synchronization, and comprehensive status checking.
$3
####
domainExists(name)
Check if a domain exists without throwing errors.`javascript
const exists = await sdk.domainExists('test@crypto');
console.log('Domain exists:', exists); // true or false
`####
getOwnerSafe(name)
Get domain owner with safe error handling (returns null instead of throwing).`javascript
const owner = await sdk.getOwnerSafe('test@crypto');
console.log('Owner:', owner); // address or null
`####
getDomainStatus(name)
Get comprehensive domain information including existence, ownership, and airdrop status.`javascript
const status = await sdk.getDomainStatus('test@crypto');
console.log(status);
// {
// name: 'test@crypto',
// exists: true,
// owner: '0x...',
// canMint: false,
// mintEligibility: null,
// inAirdropContract: true,
// claimableAirdrops: 3,
// airdropDetails: [...]
// }
`$3
The RWAirdrop contract provides synchronization functions to keep domain ownership data in sync:
#### Available Functions
- โ
syncMyDomains(domainNames[]) - Domain holders sync all their domains at once
- โ
syncDomainOwnershipsAdmin(domainNames[], owners[]) - Emergency/admin-only function for data recovery#### Usage Examples
`javascript
// User syncs their own domains
const myDomains = ['alice@crypto', 'bob@crypto'];
await sdk.rwairdrop().syncMyDomains(myDomains);// Admin emergency sync (admin only)
await sdk.rwairdrop().syncDomainOwnershipsAdmin(
['alice@crypto', 'bob@crypto'],
['0x...', '0x...']
);
`$3
- Fixed Domain Format Parsing: All methods now correctly use
@ separator instead of .
- Better Error Handling: Safe methods return null instead of throwing errors
- Automatic Synchronization: Airdrop operations automatically sync with Registry contract
- Comprehensive Status: Single method to get complete domain informationExtended Functions
The SDK provides additional extended functions for comprehensive name management and querying.
$3
Get the total number of names owned by a specific address.
`javascript
const totalNames = await sdk.getTotalNames('0x...');
console.log('Total names owned:', totalNames.toString());
`Parameters:
-
address (string): Wallet address to checkReturns: Promise - Total number of names owned
Use Case: Check how many domains a wallet owns before fetching the full list.
$3
Get a list of all names owned by a specific address with their token IDs.
`javascript
const namesList = await sdk.getNamesList('0x...');
console.log('Names owned:');
namesList.forEach(item => {
console.log( ${item.name} (Token ID: ${item.tokenId}));
});
`Parameters:
-
address (string): Wallet address to queryReturns: Promise> - Array of name objects
Use Case: Display all domains owned by a user in a portfolio view.
$3
Get comprehensive details about a specific name including metadata, ownership, and resolution status.
`javascript
const details = await sdk.getNameDetails('alice@crypto');
console.log('Name Details:', {
name: details.name,
tokenId: details.tokenId,
owner: details.owner,
exists: details.exists,
resolvedAddress: details.resolvedAddress,
tokenURI: details.tokenURI,
metadata: details.metadata
});
`Parameters:
-
name (string): ODude name to query (format: name@tld)Returns: Promise
Use Case: Get full information about a domain for display or verification.
$3
Get the name associated with a specific token ID.
`javascript
const name = await sdk.getNameById(123);
console.log('Name:', name);
`Parameters:
-
tokenId (number|string|bigint): Token ID to queryReturns: Promise - Name associated with the token
Use Case: Reverse lookup from token ID to name.
$3
Get a paginated list of all registered names in the system.
`javascript
// Get first 10 names
const names = await sdk.getAllNames(0, 10);// Get next 10 names
const moreNames = await sdk.getAllNames(10, 10);
names.forEach(item => {
console.log(
${item.name} - Owner: ${item.owner} - Token ID: ${item.tokenId});
});
`Parameters:
-
startIndex (number): Starting index (default: 0)
- count (number): Number of names to fetch (default: 10)Returns: Promise> - Array of name objects
Use Case: Build a marketplace, explorer, or analytics dashboard with pagination.
$3
Check if a name exists (is registered).
`javascript
const exists = await sdk.resolver().nameExists('newname@crypto');
if (exists) {
console.log('Name is already registered.');
} else {
console.log('Name is available for registration!');
}
`Parameters:
-
name (string): ODude name to check (format: name@tld)Returns: Promise - True if available, false if taken
Use Case: Validate name availability before attempting registration.
$3
Get the approved address for a specific token (for transfers).
`javascript
const approved = await sdk.getApproved(123);
if (approved !== '0x0000000000000000000000000000000000000000') {
console.log('Approved address:', approved);
} else {
console.log('No approval set');
}
`Parameters:
-
tokenId (number|string|bigint): Token ID to checkReturns: Promise - Approved address (zero address if none)
Use Case: Check transfer approvals before performing operations.
$3
Get comprehensive network configuration and connection status information.
`javascript
const networkInfo = sdk.NetworkList();
console.log('Current Network:', networkInfo.currentNetwork);
console.log('Connected Networks:', networkInfo.connectedNetworks);
console.log('Supported Networks:', Object.keys(networkInfo.supportedNetworks));
console.log('TLD Mappings:', networkInfo.tldMappings);// Check specific network details
const baseSepoliaInfo = networkInfo.supportedNetworks.basesepolia;
console.log('Base Sepolia:', {
chainId: baseSepoliaInfo.chainId,
isConnected: baseSepoliaInfo.isConnected,
hasContracts: baseSepoliaInfo.hasContracts,
contracts: baseSepoliaInfo.contracts
});
`Returns: Object with network configuration and status
Use Case: Debug network issues, validate configuration, display network status in UI.
$3
Display formatted network information to the console.
`javascript
sdk.displayNetworkList();
// Outputs:
// === ODude SDK Network Information ===
// Current Network: basesepolia
// Default Network: basesepolia
// Connected Networks: basesepolia
//
// --- Supported Networks ---
// โ
basesepolia (Base Sepolia)
// Chain ID: 84532
// RPC URL: https://sepolia.base.org
// Contracts Deployed: โ
// Connected: true
// ...
`Returns: Object with network information (same as NetworkList())
Use Case: Quick debugging and status checking during development.
$3
The SDK supports efficient batch operations for multiple queries:
`javascript
// Registry batch operations
const tokenIds = [1, 2, 3, 4, 5];// Get multiple names at once
const names = await sdk.registry().getMultipleNames(tokenIds);
console.log('Names:', names);
// Get multiple owners at once
const owners = await sdk.registry().getMultipleOwners(tokenIds);
console.log('Owners:', owners);
// Get comprehensive info for multiple tokens
const tokenInfo = await sdk.registry().getMultipleTokenInfo(tokenIds);
console.log('Token info:', tokenInfo);
// Resolver batch operations
const domainNames = ['alice@fil', 'bob@bnb', 'charlie@crypto'];
// Resolve multiple names at once
const resolutions = await sdk.resolver().resolveMultiple(domainNames);
console.log('Resolutions:', resolutions);
// [
// { name: 'alice@fil', address: '0x...', resolved: true },
// { name: 'bob@bnb', address: '0x...', resolved: true },
// { name: 'charlie@crypto', address: null, resolved: false, error: '...' }
// ]
// Reverse resolve multiple addresses
const addresses = ['0x...', '0x...', '0x...'];
const reverseResolutions = await sdk.resolver().reverseMultiple(addresses);
// Check multiple names existence
const existenceResults = await sdk.resolver().checkMultipleNamesExist(domainNames);
`$3
Listen to real-time events across networks:
`javascript
// Listen to Transfer events
sdk.onTransfer((from, to, tokenId) => {
console.log(Transfer: ${from} โ ${to} (Token: ${tokenId}));
});// Listen to NameResolved events
sdk.onNameResolved((name, address) => {
console.log(
Name resolved: ${name} โ ${address});
});// Listen to DomainMinted events
sdk.onDomainMinted((name, owner) => {
console.log(
Domain minted: ${name} by ${owner});
});// Listen to events across all networks
sdk.onTransferAllNetworks((from, to, tokenId, network) => {
console.log(
Transfer on ${network}: ${from} โ ${to} (Token: ${tokenId}));
});// Remove all event listeners
sdk.removeAllListeners();
// Remove listeners for specific network
sdk.removeAllListeners('filecoin');
`$3
The SDK provides specific error types for better error handling:
`javascript
const {
TokenNotFoundError,
NameNotFoundError,
NetworkError,
UnsupportedTLDError,
MintingError
} = require('odude-sdk');try {
const address = await sdk.resolve('nonexistent@fil');
} catch (error) {
if (error instanceof NameNotFoundError) {
console.log('Name not found:', error.name);
} else if (error instanceof UnsupportedTLDError) {
console.log('Unsupported TLD:', error.tld);
} else if (error instanceof NetworkError) {
console.log('Network error:', error.networkName);
}
}
try {
await sdk.tld().mintDomain('alice@fil', '0x...');
} catch (error) {
if (error instanceof MintingError) {
console.log('Minting failed:', error.domainName, error.message);
}
}
`Testing
The SDK includes comprehensive tests for all functionality.
$3
`bash
Install dependencies
npm installVerify setup
npm run verifyRun all tests
npm testRun specific test suites
npm run test:ownership # Name ownership tests
npm run test:connectivity # Network connectivity tests
npm run test:extended # Extended functions testsRun tests in watch mode
npm run test:watch
`$3
`bash
Basic usage
npm run example:basicName resolution
npm run example:resolveAirdrop checking
npm run example:airdropTLD minting (requires private key and funds)
node examples/TLDMint.jsSubdomain minting (requires private key and funds)
node examples/SubNameMint.jsTLD management
npm run example:tldGet names list for an address
npm run example:names-listGet all names with pagination
npm run example:all-namesNetwork information
npm run example:network-info
`$3
Each test file has configurable variables at the top. Update these to test with your own values:
`javascript
// In test/extended-functions.test.js
const TEST_CONFIG = {
WALLET_ADDRESS: '0x...', // Your wallet address
TLD_NAME: 'crypto',
ODUDE_NAME: 'test@crypto',
TOKEN_ID: 1,
TEST_NETWORK: 'basesepolia'
};
`$3
Each example file also has configurable variables:
`javascript
// In examples/get-names-list.js
const CONFIG = {
WALLET_ADDRESS: '0x...', // Replace with actual address
NETWORK: 'basesepolia',
RPC_URL: process.env.BASE_SEPOLIA_RPC_URL || 'https://sepolia.base.org'
};
`$3
For local testing with Hardhat:
`bash
In your ODude contracts repository
npx hardhat nodeIn another terminal, run SDK tests
npm test
`Browser Usage
The ODude SDK is fully compatible with modern browsers and can be used in various ways:
$3
`html
ODude SDK Browser Example
`$3
`html
ODude SDK ES Modules
`$3
`javascript
// Connect to user's wallet
async function connectWallet() {
if (typeof window.ethereum !== 'undefined') {
// Request account access
await window.ethereum.request({ method: 'eth_requestAccounts' }); // Create provider from wallet
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
// Initialize SDK with wallet
const sdk = new ODudeSDK({
provider: provider,
signer: signer
});
await sdk.connectNetwork('basesepolia');
// Now you can perform write operations
return sdk;
} else {
throw new Error('Please install MetaMask or another Web3 wallet');
}
}
// Usage
try {
const sdk = await connectWallet();
const address = await sdk.resolve('alice@crypto');
console.log('Resolved:', address);
} catch (error) {
console.error('Error:', error.message);
}
`Next.js Integration
The ODude SDK works seamlessly with Next.js applications:
$3
`typescript
// app/lib/odude.ts
import ODudeSDK, { ODudeSDKConfig } from '@odude/odude-sdk';const config: ODudeSDKConfig = {
rpcUrl_sepolia: process.env.NEXT_PUBLIC_BASE_SEPOLIA_RPC_URL,
rpcUrl_filecoin: process.env.NEXT_PUBLIC_FILECOIN_RPC_URL,
};
export const sdk = new ODudeSDK(config);
// Initialize on server startup
sdk.connectAllNetworks().catch(console.error);
``typescript
// app/api/resolve/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { sdk } from '@/lib/odude';export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const name = searchParams.get('name');
if (!name) {
return NextResponse.json({ error: 'Name parameter required' }, { status: 400 });
}
try {
const address = await sdk.resolve(name);
return NextResponse.json({ name, address });
} catch (error) {
return NextResponse.json({ error: 'Name not found' }, { status: 404 });
}
}
``typescript
// app/components/NameResolver.tsx
'use client';import { useState } from 'react';
import { NameNotFoundError } from '@odude/odude-sdk';
export default function NameResolver() {
const [name, setName] = useState('');
const [result, setResult] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const resolveName = async () => {
if (!name) return;
setLoading(true);
setError(null);
setResult(null);
try {
const response = await fetch(
/api/resolve?name=${encodeURIComponent(name)});
const data = await response.json(); if (response.ok) {
setResult(data.address);
} else {
setError(data.error);
}
} catch (err) {
setError('Failed to resolve name');
} finally {
setLoading(false);
}
};
return (
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Enter ODude name (e.g., alice@crypto)"
className="flex-1 p-2 border rounded"
/>
onClick={resolveName}
disabled={loading || !name}
className="px-4 py-2 bg-blue-500 text-white rounded disabled:opacity-50"
>
{loading ? 'Resolving...' : 'Resolve'}
{result && (
Resolved: {result}
)} {error && (
Error: {error}
)}
);
}
`$3
`typescript
// lib/odude.ts
import ODudeSDK from '@odude/odude-sdk';export const sdk = new ODudeSDK({
rpcUrl_sepolia: process.env.NEXT_PUBLIC_BASE_SEPOLIA_RPC_URL,
});
// Initialize once
let initialized = false;
export const initializeSDK = async () => {
if (!initialized) {
await sdk.connectAllNetworks();
initialized = true;
}
return sdk;
};
``typescript
// pages/api/resolve.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { initializeSDK } from '@/lib/odude';export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({ error: 'Method not allowed' });
}
const { name } = req.query;
if (!name || typeof name !== 'string') {
return res.status(400).json({ error: 'Name parameter required' });
}
try {
const sdk = await initializeSDK();
const address = await sdk.resolve(name);
res.status(200).json({ name, address });
} catch (error) {
res.status(404).json({ error: 'Name not found' });
}
}
`$3
`bash
.env.local
NEXT_PUBLIC_BASE_SEPOLIA_RPC_URL=https://sepolia.base.org
NEXT_PUBLIC_FILECOIN_RPC_URL=https://api.node.glif.io
NEXT_PUBLIC_BNB_RPC_URL=https://bsc-dataseed1.binance.org
PRIVATE_KEY=your_private_key_here
`Troubleshooting
$3
#### Network Connection Failures
Problem:
JsonRpcProvider failed to detect network or connection timeoutsSolution:
- Verify RPC URL is correct and accessible
- Check network connectivity
- The SDK automatically retries up to 3 times
- Currently, only Base Sepolia network has deployed contracts
- Use environment variables for RPC URLs:
`bash
export BASE_SEPOLIA_RPC_URL="https://sepolia.base.org"
export FILECOIN_RPC_URL="https://api.node.glif.io"
export BNB_RPC_URL="https://bsc-dataseed1.binance.org"
`#### Contract Not Found Errors
Problem:
Contract not connected or could not decode result dataSolution:
- Verify contracts are deployed on the target network
- Only Base Sepolia has deployed contracts currently
- Check contract addresses in
config/networks.json
- Use sdk.NetworkList() to verify deployment status:`javascript
const networkInfo = sdk.NetworkList();
console.log(networkInfo.supportedNetworks.basesepolia.hasContracts);
`#### Name Not Found Errors
Problem:
Name "..." not found or Registry: Name not foundSolution:
- This is expected when names don't exist
- Use
nameExists() to check before resolving:`javascript
const exists = await sdk.resolver().nameExists('name@crypto');
if (exists) {
const address = await sdk.resolve('name@crypto');
}
`#### Method Not Found Errors
Problem:
sdk.registry.name is not a functionSolution:
- Use method calls with parentheses:
sdk.registry().name()
- Not property access: sdk.registry.name`javascript
// โ
Correct
const name = await sdk.registry().name();
const resolver = sdk.resolver('basesepolia');// โ Incorrect
const name = await sdk.registry.name();
`$3
Currently supported networks:
- โ
Base Sepolia: Fully working with deployed contracts
- โ ๏ธ Localhost: Works when local Hardhat node is running
- โ ๏ธ Filecoin: RPC accessible but no contracts deployed yet
- โ ๏ธ BNB Chain: RPC accessible but no contracts deployed yet
$3
1. Always use try-catch blocks for async operations
2. Check network connectivity before performing operations
3. Use environment variables for sensitive data like private keys
4. Validate names exist before attempting resolution
5. Handle network-specific errors gracefully
6. Use the working network (Base Sepolia) for testing
7. Configure test variables at the top of test/example files
8. Check NetworkList() to debug network issues
$3
- Check the examples in the
examples/ directory
- Review test files in the test/ directory
- Examine network configuration in config/networks.json
- Use sdk.displayNetworkList() to debug network issues
- Open an issue on GitHub with detailed error messagesAPI Reference
$3
Main SDK class that provides access to all contracts.
#### Constructor Options
-
rpcUrl (string): RPC URL for the network
- provider (ethers.Provider): Custom provider instance
- signer (ethers.Signer): Signer for write operations
- privateKey (string): Private key to create a signer#### Methods
-
connect(addresses): Connect to contracts with custom addresses
- connectLocalhost(): Connect using localhost-deployment.json
- connectSigner(signer): Connect a signer for write operations#### Properties
-
registry: Registry contract instance
- resolver: Resolver contract instance
- tld: TLD contract instance
- rwairdrop: RWAirdrop contract instance
- utils: Helper utilities
- provider: Ethers provider
- signer`: Ethers signer (if connected)MIT
Contributions are welcome! Please feel free to submit a Pull Request.