Skip to main content

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

ChainPrefixLengthModel
Alphaax142 charsUTXO/Record
Deltadx142 charsAccount

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

ComponentBytesDescription
Prefix-Human-readable part: ax1
Version1Address version (currently 0)
Public Key Hash20BLAKE2b-160 hash of public key
Checksum6Bech32 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

ComponentBytesDescription
Prefix-Human-readable part: dx1
Version1Address version (currently 0)
Account Hash20Keccak-256 hash of public key (last 20 bytes)
Checksum6Bech32 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:

AddressPurpose
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

CodeDescription
INVALID_PREFIXAddress prefix not recognized
INVALID_LENGTHAddress wrong length
INVALID_CHECKSUMBech32 checksum failed
INVALID_VERSIONUnsupported address version
INVALID_ENCODINGInvalid 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.