What is the format of XRPL addresses?
Last updated:
XRPL addresses follow a specific format designed for security, error detection, and human usability. Understanding the structure and encoding scheme is essential for developers working with the XRP Ledger.
Classic Address Format
The standard XRPL address format (called "classic address") consists of:
Structure: ``` [Base58Check Encoded String] = Base58([Type Prefix][Account ID][Checksum]) ```
Components:
1. Type Prefix: 1 byte (0x00 for accounts) 2. Account ID: 20 bytes (160-bit RIPEMD-160 hash) 3. Checksum: 4 bytes (first 4 bytes of double SHA-256 hash) 4. Total: 25 bytes before encoding
Encoded Result: - Length: 25-35 characters (typically 33-34) - Prefix: Always starts with lowercase 'r' - Character set: Base58 alphabet (excluding 0, O, I, l) - Case-sensitive: Yes - Example: `rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh`
Base58 Alphabet
XRPL uses the Bitcoin Base58 alphabet:
``` rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz ```
Excluded characters: 0 (zero), O (capital o), I (capital i), l (lowercase L)
This eliminates visually similar characters that could cause transcription errors.
Anatomy of an Address
Let's decode an example address: `rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh`
```javascript const xrpl = require('xrpl');
function decodeAddress(address) { // Decode from Base58 const decoded = xrpl.decodeAddress(address); console.log('Raw bytes (hex):', decoded.toString('hex')); // Example: 9bf42984481a207feaf81e57e1e26e74f9ff6e4a console.log('Length:', decoded.length, 'bytes'); // 20 bytes return decoded; }
// Verify with checksum function verifyAddress(address) { try { const decoded = xrpl.decodeAddress(address); return decoded.length === 20; // Valid if 160 bits } catch (error) { return false; // Invalid format or checksum } }
console.log(verifyAddress('rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh')); // true console.log(verifyAddress('rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZi')); // false (bad checksum) ```
Address Generation Flow
``` Public Key (32 or 33 bytes) ↓ SHA-256 256-bit hash ↓ RIPEMD-160 160-bit hash (Account ID) ← 20 bytes ↓ Add type prefix (0x00) 21 bytes ↓ Double SHA-256, take first 4 bytes Checksum (4 bytes) ↓ Concatenate 25 bytes total ↓ Base58 encode Address string (starts with 'r') ```
Why 'r' Prefix?
The 'r' prefix results from: 1. Type byte 0x00 for accounts 2. Base58 encoding mathematics 3. Intentional design to distinguish XRPL addresses
Comparison: - Bitcoin P2PKH: starts with '1' (type 0x00) - Bitcoin P2SH: starts with '3' (type 0x05) - XRPL accounts: starts with 'r' (type 0x00, different Base58 mapping)
Address Validation Rules
A valid XRPL classic address must:
1. Start with 'r' 2. Be 25-35 characters long (typically 33-34) 3. Use only Base58 characters 4. Have valid checksum 5. Decode to exactly 20 bytes (after removing checksum)
```javascript function validateAddress(address) { // Check 1: Starts with 'r' if (!address.startsWith('r')) { return { valid: false, reason: 'Must start with r' }; } // Check 2: Length if (address.length < 25 || address.length > 35) { return { valid: false, reason: 'Invalid length' }; } // Check 3: Valid Base58 characters const base58Regex = /^[rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz]+$/; if (!base58Regex.test(address)) { return { valid: false, reason: 'Invalid characters' }; } // Check 4 & 5: Checksum and length try { const decoded = xrpl.decodeAddress(address); if (decoded.length !== 20) { return { valid: false, reason: 'Invalid decoded length' }; } } catch (error) { return { valid: false, reason: 'Checksum verification failed' }; } return { valid: true }; } ```
Special Address Types
1. Genesis Account (Black Hole) ``` rrrrrrrrrrrrrrrrrrrrrhoLvTp ``` This is the account that holds the initial XRP. Its private key is unknown (generated from all zeros), making it effectively a burn address.
2. NaN Address ``` rrrrrrrrrrrrrrrrrrrrrr1yLvTp ``` Used in special circumstances; funds sent here are effectively burned.
X-Address Format
In addition to classic addresses, XRPL also supports X-addresses (packaged format):
Format: Starts with 'X' (mainnet) or 'T' (testnet) Example: `XVLhHMPHU98es4dbozjVtdWzVrDjtV5fdx1mHp98tDMoQXb`
X-addresses encode both the account address and destination tag in a single string:
```javascript const xrpl = require('xrpl');
// Convert classic address + tag to X-address const xAddress = xrpl.encodeXAddress( 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh', 12345, // destination tag false // false = mainnet, true = testnet ); console.log('X-Address:', xAddress);
// Decode X-address const decoded = xrpl.decodeXAddress(xAddress); console.log('Classic Address:', decoded.address); console.log('Tag:', decoded.tag); console.log('Test network:', decoded.isTestNetwork); ```
Comparison: Classic vs X-Address
| Feature | Classic Address | X-Address | |---------|----------------|----------| | Format | rXXXXX... | XXXXXX... (mainnet) or TXXXXX... (testnet) | | Length | 25-35 chars | ~47 chars | | Includes tag | No | Yes | | Backward compatible | Yes | No (requires X-address support) | | Use case | General purpose | Exchanges, destinations requiring tags |
Address Case Sensitivity
XRPL addresses are case-sensitive:
```javascript const addr1 = 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh'; // correct const addr2 = 'rn7n7otqdd6fczfgldlqtymvrn3hmfgnzh'; // WRONG - different address!
console.log(addr1 === addr2); // false ```
Always preserve exact case when handling addresses.
Common Format Mistakes
1. Lowercase 'r': Must be lowercase 'r', not 'R' 2. Confusing characters: Using '0' instead of 'O', or 'I' instead of 'l' 3. Truncation: Copying partial address 4. Extra spaces: Adding whitespace before/after address 5. Case changes: Modifying character case
Format Validation Examples
```javascript const validAddresses = [ 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh', 'rLHzPsX6oXkzU9rFkyJ5R8J4HqBzZT4Sfp', 'rrrrrrrrrrrrrrrrrrrrrhoLvTp' // genesis ];
const invalidAddresses = [ 'RN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh', // starts with 'R' not 'r' 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZ', // too short 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZi', // bad checksum '0N7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh', // has invalid char '0' 'rN7n7 otQDd6FczFgLdlqtyMVrn3HMfgnZh' // contains space ]; ```
Encoding Implementation
```javascript const crypto = require('crypto');
function encodeAddress(accountId) { // accountId is 20-byte buffer const prefix = Buffer.from([0x00]); const payload = Buffer.concat([prefix, accountId]); // Compute checksum const hash1 = crypto.createHash('sha256').update(payload).digest(); const hash2 = crypto.createHash('sha256').update(hash1).digest(); const checksum = hash2.slice(0, 4); // Concatenate and encode const full = Buffer.concat([payload, checksum]); return base58Encode(full); // Returns string starting with 'r' } ```
The XRPL address format balances multiple requirements: security through checksums, human usability through careful character selection, and compatibility with established blockchain addressing patterns. The result is a robust addressing system that has served the XRP Ledger reliably since its inception.