SS58

A Dart library for encoding and decoding SS58 addresses used in Substrate-based blockchains like Polkadot, Kusama, and 80+ other networks.

Features

  • Decode SS58 addresses to extract prefix and public key
  • Encode public keys to SS58 address format
  • Safe parsing with tryDecode() (returns null instead of throwing)
  • Convert addresses between different networks
  • Built-in registry with 80+ supported networks
  • Full exception handling for invalid inputs
  • Uses Blake2b hashing (RFC 7693 compliant)

Installation

Add this to your pubspec.yaml:

dependencies:
  ss58: any

Then run:

dart pub get

Quick Start

import 'package:ss58/ss58.dart';

void main() {
  // Decode an address
  final address = Address.decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
  print('Prefix: ${address.prefix}');  // 42 (Substrate)
  print('Public Key: ${address.pubkey}');

  // Encode to a different network
  final polkadotAddress = address.withPrefix(0).encode();
  print('Polkadot: $polkadotAddress');
}

Usage

Decoding Addresses

Standard Decoding (throws on invalid input)

import 'package:ss58/ss58.dart';

// Decode any SS58 address
final address = Address.decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');

print('Network Prefix: ${address.prefix}');  // 42
print('Public Key Length: ${address.pubkey.length}');  // 32 bytes

Safe Decoding (returns null on invalid input)

// Safe parsing - no exceptions thrown
final address = Address.tryDecode('invalid-address');

if (address != null) {
  print('Valid address with prefix: ${address.prefix}');
} else {
  print('Invalid address');
}

Encoding Addresses

From an Address Object

final address = Address.decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');

// Encode with original prefix
final encoded = address.encode();

// Encode with a different prefix
final polkadotEncoded = address.encode(prefix: 0);

From Raw Bytes

import 'dart:typed_data';
import 'package:ss58/ss58.dart';

final pubkey = Uint8List.fromList([/* 32 bytes */]);
final address = Address(prefix: 0, pubkey: pubkey);
final encoded = address.encode();

Cross-Network Address Conversion

Convert the same public key between different networks:

// Start with a Substrate address (prefix 42)
final substrate = Address.decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');

// Convert to Polkadot (prefix 0)
final polkadot = substrate.withPrefix(0);
print('Polkadot: ${polkadot.encode()}');

// Convert to Kusama (prefix 2)
final kusama = substrate.withPrefix(2);
print('Kusama: ${kusama.encode()}');

// The public key remains the same across all networks
assert(substrate.pubkey == polkadot.pubkey);

Using the Codec Class

For network-specific encoding/decoding:

// Create a codec for a specific network
final polkadotCodec = Codec.fromNetwork('polkadot');
final kusamaCodec = Codec.fromNetwork('kusama');

// Or by prefix
final substrateCodec = Codec(42);

// Decode (validates the address matches the expected network)
final bytes = substrateCodec.decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');

// Encode
final address = substrateCodec.encode(bytes);

Registry Lookups

Look up network information:

// Get network by prefix
final polkadot = Codec.registry.getByPrefix(0);
print('Network: ${polkadot.network}');  // "polkadot"

// Get network by name
final kusama = Codec.registry.getByNetwork('kusama');
print('Prefix: ${kusama.prefix}');  // 2

// List all registered networks
for (final item in Codec.registry.items) {
  print('${item.network}: prefix ${item.prefix}');
}

Common Networks

Network Prefix Example Address
Polkadot 0 15oF4uVJwmo4TdGW7VfQxNLavjCXviqxT9S1MgbjMNHr6Sp5
Kusama 2 EXtQYFeY2ivDsfazZvGC9aG87DxnhWH2f9kjUUq2pXTZKF5
Substrate 42 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY
Crust 66 cTMxUeDi2HdYVpedqu5AFMtyDcn4djbBfCKiPDds6k1fuFYXL
Acala 10 23M5ttkmR6KcoTAAE6gcmibnKFtVaTP5yxnY8HF1BmrJ2A1i
Astar 5 ajYMsCKsEAhEvHpeA4XqsfiA9v1CdzZPrCfS6pEfeGHW9j8

See the full list in the SS58 Registry.

Error Handling

The library provides specific exceptions for different error cases:

import 'package:ss58/ss58.dart';

try {
  final address = Address.decode('invalid');
} on BadAddressLengthException catch (e) {
  print('Address too short or invalid length');
} on InvalidPrefixException catch (e) {
  print('Invalid network prefix: ${e.prefix}');
} on InvalidCheckSumException catch (e) {
  print('Checksum verification failed');
}

// For Codec with network validation
try {
  Codec(2).decode('5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY');
} on InvalidAddressPrefixException catch (e) {
  print('Expected prefix ${e.prefix}, got ${e.address.prefix}');
}

// For registry lookups
try {
  Codec.registry.getByNetwork('nonexistent');
} on NoEntryForNetworkException catch (e) {
  print('Unknown network: ${e.network}');
}

Exception Types

Exception When Thrown
BadAddressLengthException Address is too short or has invalid pubkey length
InvalidPrefixException Prefix is negative, > 16383, or cannot be parsed
InvalidCheckSumException Blake2b checksum verification failed
InvalidAddressPrefixException Address prefix doesn't match Codec's expected prefix
NoEntryForNetworkException Network name not found in registry
NoEntryForPrefixException Prefix not found in registry
DuplicatePrefixException Duplicate prefix when building registry
DuplicateNetworkException Duplicate network name when building registry

API Reference

Address Class

Method Description
Address.decode(String) Decode SS58 address string (throws on error)
Address.tryDecode(String) Decode SS58 address string (returns null on error)
encode({int? prefix}) Encode to SS58 string, optionally with different prefix
withPrefix(int) Create new Address with same pubkey but different prefix
prefix Network prefix (0-16383)
pubkey Raw public key bytes

Codec Class

Method Description
Codec(int prefix) Create codec for specific prefix
Codec.fromNetwork(String) Create codec for named network
encode(List<int>) Encode bytes to SS58 string
decode(String) Decode SS58 string to bytes (validates prefix)
Codec.registry Access the global network registry

Registry Class

Method Description
getByPrefix(int) Get RegistryItem by prefix
getByNetwork(String) Get RegistryItem by network name
items List of all registered networks

Supported Public Key Lengths

Length Hash Length Use Case
1 byte 1 byte Minimal identifiers
2 bytes 1 byte Short identifiers
4 bytes 1 byte Account indices
8 bytes 1 byte Extended indices
32 bytes 2 bytes Standard Ed25519/Sr25519 keys
33 bytes 2 bytes Compressed ECDSA keys

Resources

License

This project is licensed under the Apache 2.0 License - see the LICENSE file for details.