Polkadot 101 — Initiate · Lesson 2 of 5

SS58 — prefixes, checksums, and replay safety

4 min · read

A Polkadot relay-chain address looks like:

1FRMM8PEiWXYax7rpS6X4XZX1aAAxSWx1CrKTyrVYhV24fg

That's SS58 — the Polkadot address format. Conceptually similar to Cosmos's bech32 (which we covered in the Cosmos course): a public-key hash, a chain identifier, and a checksum, all bundled into a base-58 string.

The structure

SS58 is:

<chain-prefix-byte(s)><32-byte-public-key><checksum-bytes>  →  base58 encode

The prefix is not a human-readable HRP like Cosmos uses. It's a number — for example, 0 for the Polkadot relay chain, 2 for Kusama, 5 for AssetHub. When encoded with base58, prefix 0 always produces an address starting with "1". Kusama (prefix 2) produces addresses starting with one of D/E/F/G/H. The wallet validates the leading character to catch wrong-chain pastes early.

The full prefix registry is at https://github.com/paritytech/ss58-registry. Iter-G only handles prefix 0 (Polkadot relay); parachain support arrives later.

The checksum

SS58 ends with 2 bytes of blake2 checksum computed over the prefix + pubkey. A typo flips bits in the base58 decode, the checksum fails, and the address is rejected — same logic as Cosmos's bech32 6-character checksum or Bitcoin's bech32 SegWit format. You get one free shot at noticing the error before broadcast.

Why a prefix per chain

Consider: a single sr25519 keypair gives you addresses on Polkadot, Kusama, every parachain. The 32-byte pubkey is the same everywhere. Without a prefix, the same string would represent your account on every chain, and a transaction signed for one chain could trivially be replayed on another.

The prefix breaks this. Even though the key is the same, the address differs. A tx broadcast to Kusama with a Polkadot-prefixed destination is rejected before the runtime even tries to dispatch it. Combined with the chain-spec identifier baked into every signed extrinsic, this makes cross-chain replays impossible.

The wallet's behaviour

When you receive Polkadot, the wallet shows the prefix-0 (relay-chain) form. If someone sends you DOT to that address, the relay chain accepts it. If they accidentally sent KSM to the same string of characters but with a Kusama prefix instead, that would be a different address — and Kusama would route the KSM there.

This is why pastes matter. The wallet validates that the destination starts with "1" before submitting a DOT send. If you paste a Kusama-prefixed address, the send is rejected — you would have lost the DOT to an unknown owner on Kusama otherwise.

The "default address" gotcha

Polkadot.js and some other UIs let users toggle which prefix is displayed for their pubkey. The same key might be shown as a Polkadot relay address sometimes and an AssetHub-formatted address other times. They're the same key — the toggle is just UX. But people copy-paste between contexts and confuse themselves.

The wallet always renders the relay-chain prefix. We don't display alternate prefixes for the same key. If a parachain attaches in iter-G+1 we'll add a tab toggle, the same as we do for Hub/Osmosis on Cosmos.

Compare to bech32

If you took the Cosmos course: SS58 and bech32 solve the same problem (per-chain address namespace + checksum) with different encodings. Bech32 uses a human-readable HRP and base32; SS58 uses a numeric prefix and base58. Both add a typo-detecting checksum. Neither is dramatically better — they're parallel design choices made by different ecosystems around the same time.

The practical difference: bech32 addresses tell you the chain at a glance (cosmos1…, osmo1…); SS58 addresses don't, unless you remember which leading character maps to which chain. The wallet does the lookup for you so you don't need to memorise the table.

The TL;DR

SS58 encodes a chain-prefix + 32-byte pubkey + checksum into a base58 string. Polkadot relay = prefix 0, addresses start with "1". The checksum catches typos. The prefix prevents cross-chain replays. The wallet validates the leading character before broadcasting.

Next: what happens if your DOT balance gets too small.