XRPL Technology

What is X-address format vs classic address?

Last updated:

The XRP Ledger supports two address formats: classic addresses and X-addresses. Understanding the differences, use cases, and conversion between these formats is crucial for developers and integrators working with XRPL.

Classic Address Format

The original XRPL address format, in use since the ledger's inception:

Structure: - Starts with lowercase 'r' - 25-35 characters (typically 33-34) - Base58Check encoded - Example: `rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh`

With Destination Tag: When using classic addresses, destination tags are separate: ```javascript { address: 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh', tag: 12345 // Separate field } ```

X-Address Format

Introduced to solve the destination tag problem by packaging address and tag together:

Structure: - Starts with 'X' (mainnet) or 'T' (testnet) - ~47 characters - Base58Check encoded - Includes network identifier - Example: `XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb`

Encoding: X-addresses encode three pieces of information: 1. Account address (classic address equivalent) 2. Destination tag (optional, 0 if not used) 3. Network identifier (mainnet/testnet flag)

The Destination Tag Problem

The motivation for X-addresses stems from a critical user experience issue:

Classic Address Issues:

1. Lost Funds: Users forgetting to include destination tags at exchanges 2. Two-Field Requirement: Addresses require two separate fields (address + tag) 3. User Error: Easy to send to address without required tag 4. Support Burden: Exchanges handling numerous "forgot tag" support tickets

```javascript // Classic address payment - WRONG if tag required! const payment = { TransactionType: 'Payment', Account: 'rSender...', Destination: 'rExchange...', // Missing required tag! Amount: '1000000' }; // Result: Funds arrive at exchange but can't be credited to user account ```

X-Address Solution:

```javascript // X-address payment - tag is included in address const payment = { TransactionType: 'Payment', Account: 'rSender...', Destination: 'XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb', Amount: '1000000' }; // Tag is automatically included! ```

Conversion Between Formats

```javascript const xrpl = require('xrpl');

// Classic to X-address function classicToX(classicAddress, tag, isTestnet = false) { const xAddress = xrpl.encodeXAddress( classicAddress, tag, isTestnet ); return xAddress; }

// X-address to Classic function xToClassic(xAddress) { const decoded = xrpl.decodeXAddress(xAddress); return { address: decoded.address, tag: decoded.tag, isTestnet: decoded.isTestNetwork }; }

// Examples const classic = 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh'; const tag = 12345;

const xAddr = classicToX(classic, tag, false); console.log('X-Address:', xAddr); // XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb

const decoded = xToClassic(xAddr); console.log('Classic:', decoded.address); // rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh console.log('Tag:', decoded.tag); // 12345 console.log('Testnet:', decoded.isTestnet); // false ```

Practical Usage Examples

Exchange Deposit Scenario:

```javascript // OLD WAY (Classic Address + Tag) const depositInfo = { address: 'rExchangeMainWallet123456789', tag: 847592, // User's unique ID instructions: 'IMPORTANT: Include destination tag 847592 or funds will be lost!' };

// NEW WAY (X-Address) const depositInfo = { address: 'XVexchangeAddressWithEmbeddedTag123456789', instructions: 'Send to this address (tag included)' }; ```

Transaction Handling:

```javascript const xrpl = require('xrpl');

async function sendPayment(destination, amount, wallet) { // Automatically handle both address formats let destAddress = destination; let destTag = undefined; // Check if X-address if (destination.startsWith('X') || destination.startsWith('T')) { const decoded = xrpl.decodeXAddress(destination); destAddress = decoded.address; destTag = decoded.tag; } const payment = { TransactionType: 'Payment', Account: wallet.address, Destination: destAddress, Amount: amount.toString() }; // Add tag if present if (destTag !== undefined && destTag !== false) { payment.DestinationTag = destTag; } return payment; }

// Works with both formats await sendPayment('rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh', 1000000, wallet); await sendPayment('XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb', 1000000, wallet); ```

Format Detection

```javascript function detectAddressFormat(address) { if (address.startsWith('r')) { return { format: 'classic', network: 'unknown' // classic addresses don't encode network }; } else if (address.startsWith('X')) { return { format: 'x-address', network: 'mainnet' }; } else if (address.startsWith('T')) { return { format: 'x-address', network: 'testnet' }; } else { return { format: 'invalid', network: null }; } }

console.log(detectAddressFormat('rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh')); // { format: 'classic', network: 'unknown' }

console.log(detectAddressFormat('XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb')); // { format: 'x-address', network: 'mainnet' } ```

Comparison Table

| Feature | Classic Address | X-Address | |---------|----------------|----------| | Prefix | 'r' | 'X' (mainnet) / 'T' (testnet) | | Length | 25-35 chars | ~47 chars | | Includes Tag | No | Yes | | Network Info | No | Yes | | Backward Compatible | N/A | No (requires X-address support) | | User Friendly | Medium | High | | Exchange Use | Two fields needed | Single field | | Error Prone | Yes (tag can be forgotten) | No | | Adoption | Universal | Growing |

When to Use Each Format

Use Classic Addresses When:

1. No destination tag needed: Personal wallets, direct P2P transfers 2. Legacy integration: Working with systems that don't support X-addresses 3. On-ledger operations: Most ledger operations use classic addresses internally 4. Maximum compatibility: All XRPL tools support classic addresses

Use X-Addresses When:

1. Exchange deposits: Prevents users from forgetting tags 2. Merchant payments: Simplified user experience 3. Mobile apps: Single field easier for mobile UI 4. Reducing support burden: Fewer "forgot tag" issues 5. Cross-network safety: Network identifier prevents testnet/mainnet confusion

Internal Representation

Importantly, X-addresses are a presentation format only:

```javascript // On the ledger, everything is still classic addresses const transaction = { TransactionType: 'Payment', Account: 'rSender...', // Always classic format Destination: 'rReceiver...', // Always classic format DestinationTag: 12345 // Separate field };

// X-addresses are converted before submission const xAddress = 'XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb'; const { address, tag } = xrpl.decodeXAddress(xAddress); // Now use 'address' and 'tag' in transaction ```

Validation

```javascript const xrpl = require('xrpl');

function validateXAddress(xAddress) { try { const decoded = xrpl.decodeXAddress(xAddress); return { valid: true, address: decoded.address, tag: decoded.tag, isTestnet: decoded.isTestNetwork }; } catch (error) { return { valid: false, error: error.message }; } }

function validateClassicAddress(address) { try { const decoded = xrpl.decodeAddress(address); return { valid: decoded.length === 20, accountId: decoded.toString('hex') }; } catch (error) { return { valid: false, error: error.message }; } } ```

Best Practices

1. Support Both Formats: Modern applications should accept both 2. Convert for Ledger Operations: Always convert X-addresses to classic + tag before submitting transactions 3. Display X-Addresses to Users: When tag is required, show X-address to reduce errors 4. Validate Format: Check address format before processing 5. Store Classic Internally: Store classic address + tag separately in databases

Migration Strategy

```javascript class AddressHandler { // Accept any format, normalize internally static normalize(address, tag) { if (address.startsWith('X') || address.startsWith('T')) { // X-address provided const decoded = xrpl.decodeXAddress(address); return { address: decoded.address, tag: decoded.tag }; } else { // Classic address return { address: address, tag: tag }; } } // Display as X-address if tag present static format(address, tag, isTestnet = false) { if (tag !== undefined && tag !== null) { return xrpl.encodeXAddress(address, tag, isTestnet); } return address; // Return classic if no tag } } ```

X-addresses represent an evolutionary improvement in XRPL address handling, solving real-world user experience problems while maintaining backward compatibility through their role as a presentation layer over the classic address format.

Was this helpful?

Related Questions

Go Deeper

Expand your knowledge with these related lessons

XRPL Payment Architecture for E-commerce

55 minbeginner

Understanding XRPLs Unique Model

45 minintermediate

XRPL Data Architecture - How the Ledger Stores Information

50 minintermediate

Have more questions?

Browse our complete FAQ or contact support.