Address Formats
ACDC uses distinct address formats for the Alpha and Delta chains. This reference documents the address encoding, validation, and conversion between formats.
Address Types
| Chain | Prefix | Length | Model |
|---|---|---|---|
| Alpha | ax1 | 42 chars | UTXO/Record |
| Delta | dx1 | 42 chars | Account |
Alpha Chain Addresses (ax1)
Alpha addresses are used for privacy-preserving transactions on the UTXO-based Alpha chain.
Format
ax1<38 characters of bech32 encoded data>
Example:
ax1qz3k7m8n9p0r2s4t6v8w0x2y4z6a8c0e2g4i6k8m
Structure
| Component | Bytes | Description |
|---|---|---|
| Prefix | - | Human-readable part: ax1 |
| Version | 1 | Address version (currently 0) |
| Public Key Hash | 20 | BLAKE2b-160 hash of public key |
| Checksum | 6 | Bech32 error detection |
Derivation
import { blake2b } from '@acdc/crypto';
import { bech32 } from 'bech32';
function deriveAlphaAddress(publicKey: Uint8Array): string {
// Hash public key
const hash = blake2b(publicKey, 20);
// Prepend version byte
const data = new Uint8Array([0, ...hash]);
// Convert to 5-bit groups for bech32
const words = bech32.toWords(data);
// Encode with prefix
return bech32.encode('ax1', words);
}
View Addresses
Alpha chain also supports view addresses for balance scanning without spending capability:
avx1<38 characters>
View addresses share the same underlying key but are derived differently, allowing read-only access.
Delta Chain Addresses (dx1)
Delta addresses are used for the account-based Delta chain, including DeFi, staking, and governance.
Format
dx1<38 characters of bech32 encoded data>
Example:
dx1abc123def456ghi789jkl012mno345pqr678stu
Structure
| Component | Bytes | Description |
|---|---|---|
| Prefix | - | Human-readable part: dx1 |
| Version | 1 | Address version (currently 0) |
| Account Hash | 20 | Keccak-256 hash of public key (last 20 bytes) |
| Checksum | 6 | Bech32 error detection |
Derivation
import { keccak256 } from '@acdc/crypto';
import { bech32 } from 'bech32';
function deriveDeltaAddress(publicKey: Uint8Array): string {
// Hash public key with Keccak-256
const hash = keccak256(publicKey);
// Take last 20 bytes
const accountHash = hash.slice(-20);
// Prepend version byte
const data = new Uint8Array([0, ...accountHash]);
// Convert to 5-bit groups for bech32
const words = bech32.toWords(data);
// Encode with prefix
return bech32.encode('dx1', words);
}
Contract Addresses
Contract addresses on Delta use the same format but are derived from the deployer address and nonce:
function deriveContractAddress(deployer: string, nonce: number): string {
const deployerBytes = decodeAddress(deployer);
const nonceBytes = encodeNonce(nonce);
const hash = keccak256(concat(deployerBytes, nonceBytes));
const accountHash = hash.slice(-20);
const data = new Uint8Array([0, ...accountHash]);
const words = bech32.toWords(data);
return bech32.encode('dx1', words);
}
Cross-Chain Addresses
A single key pair generates both Alpha and Delta addresses:
import { Wallet } from '@acdc/sdk';
const wallet = Wallet.create();
console.log('Alpha Address:', wallet.alphaAddress); // ax1...
console.log('Delta Address:', wallet.address); // dx1...
// Both derived from same private key
console.log('Private Key:', wallet.privateKey);
Address Validation
Checksum Validation
Bech32 encoding includes a checksum that catches typos:
import { isValidAddress } from '@acdc/sdk';
// Valid addresses
isValidAddress('dx1abc123...'); // true
isValidAddress('ax1xyz789...'); // true
// Invalid: wrong checksum
isValidAddress('dx1abc124...'); // false
// Invalid: wrong prefix
isValidAddress('xx1abc123...'); // false
// Invalid: wrong length
isValidAddress('dx1abc'); // false
Chain Detection
import { getAddressChain } from '@acdc/sdk';
getAddressChain('ax1...'); // 'alpha'
getAddressChain('dx1...'); // 'delta'
getAddressChain('avx1...'); // 'alpha-view'
getAddressChain('xxx...'); // throws InvalidAddressError
Full Validation
import { Address } from '@acdc/sdk';
try {
const addr = Address.parse('dx1abc123...');
console.log('Valid:', addr.isValid);
console.log('Chain:', addr.chain);
console.log('Version:', addr.version);
console.log('Hash:', addr.hash.toString('hex'));
console.log('Checksum:', addr.checksum);
} catch (error) {
console.log('Invalid address:', error.message);
}
Address Conversion
To Bytes
import { decodeAddress } from '@acdc/sdk';
const bytes = decodeAddress('dx1abc123...');
// Uint8Array of 21 bytes (version + 20-byte hash)
From Bytes
import { encodeAddress } from '@acdc/sdk';
const bytes = new Uint8Array([0, /* 20 bytes... */]);
const address = encodeAddress(bytes, 'delta');
// 'dx1...'
Checksum Address
Convert to checksummed format for display:
import { toChecksumAddress } from '@acdc/sdk';
// Lowercase input
const input = 'dx1abc123def456ghi789jkl012mno345pqr678stu';
// Checksummed output (mixed case based on hash)
const checksummed = toChecksumAddress(input);
// 'dx1Abc123Def456ghi789jkl012Mno345pqr678stu'
Special Addresses
Zero Address
The zero address represents burned tokens or empty recipients:
import { ZERO_ADDRESS, isZeroAddress } from '@acdc/sdk';
console.log(ZERO_ADDRESS.delta); // 'dx1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'
console.log(ZERO_ADDRESS.alpha); // 'ax1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'
isZeroAddress('dx1qqqqqqq...'); // true
System Addresses
Reserved addresses for protocol contracts:
| Address | Purpose |
|---|---|
dx1system0... | Block rewards |
dx1system1... | Staking contract |
dx1system2... | Governance contract |
dx1system3... | Bridge contract |
dx1treasury... | Community treasury |
CLI Commands
# Validate address
acdc address validate dx1abc123...
# Output:
# Address: dx1abc123...
# Valid: true
# Chain: Delta
# Version: 0
# Convert format
acdc address info dx1abc123...
# Derive from public key
acdc address derive --pubkey 0x04abc...
Error Codes
| Code | Description |
|---|---|
INVALID_PREFIX | Address prefix not recognized |
INVALID_LENGTH | Address wrong length |
INVALID_CHECKSUM | Bech32 checksum failed |
INVALID_VERSION | Unsupported address version |
INVALID_ENCODING | Invalid bech32 characters |
Code Examples
Rust
use acdc_sdk::{Address, Chain};
// Parse address
let addr: Address = "dx1abc123...".parse()?;
assert_eq!(addr.chain(), Chain::Delta);
// Validate
assert!(Address::is_valid("dx1abc123..."));
assert!(!Address::is_valid("invalid"));
// Create from bytes
let bytes = [0u8; 21]; // version + hash
let addr = Address::from_bytes(&bytes, Chain::Delta)?;
TypeScript
import { Address, isValidAddress, getAddressChain } from '@acdc/sdk';
// Validate and parse
if (isValidAddress(input)) {
const addr = new Address(input);
console.log(`Chain: ${addr.chain}`);
console.log(`Hash: ${addr.hash}`);
}
// Handle both chains
function formatAddress(address: string): string {
const chain = getAddressChain(address);
const shortened = `${address.slice(0, 10)}...${address.slice(-6)}`;
return `${chain === 'alpha' ? 'Alpha' : 'Delta'}: ${shortened}`;
}
Migration Notes
From Ethereum Addresses
If migrating from Ethereum (0x prefixed addresses):
import { convertFromEthereum } from '@acdc/sdk';
const ethAddress = '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE11';
const deltaAddress = convertFromEthereum(ethAddress);
// 'dx1wstn89scd...'
Note: This converts the address format only. Actual migration of funds requires using the bridge.