If so, Are there any docs about converting Index, Raw, Address20 and Address32?

Is it just applying the following algorithm to the variant bytes as <address>?

base58encode ( concat ( <address-type>, <address>, <checksum> ) )
checksum = ss58hash(<address-type> + <address>)[0..2]

const PREFIX: &[u8] = b"SS58PRE";
ss58hash(data) = Blake2b512(PREFIX + data)

1 Answer 1


SS58 is interesting. I did an implementation of it a couple months ago, and what I found is that we currently have one document -- https://docs.substrate.io/reference/address-formats/ -- with relatively complete information, other than just reading our implementation code.

The order of your lines is a little confusing, but yes, you have the gist of the basic algorithm. Blake2b512 hash prefix + data. Take a checksum from that, and concat the address type, the payload (address), and the checksum to get the full sequence of bytes to base58encode.

The wrinkle is that our address format doc specifies a wide variety of checksum lengths, from 1 to 8 bytes. HOWEVER, if you look at the implementation:


It just assumes a 2 byte checksum, like in your example, but I think that this is because it is just being applied to ed25519/sr25519 32 byte sequences, and isn't being used as a more general purpose library.

If you go look at other implementations, like Polkascan's: https://github.com/polkascan/py-scale-codec/blob/master/scalecodec/utils/ss58.py

Or Subscan's: https://github.com/subscan-explorer/subscan-essentials/blob/master/util/ss58/ss58.go

You see something a little different. They will decode according to that variable checksum size table, but they only encode for a one or a two byte checksum, and only for a strict set of byte payload sizes.

The wrinkle comes in when you are dealing with data that doesn't fit that ss58 checksum length table.

So, can you SS58 encode a 20 byte string? Or a 57 byte string (or any other arbitrary size)? From a pure tech POV, of course you can. However, you have to pick the checksum size, and it's basically undefined behavior as far as I was able to discern back when I was hunting for information.

What I did in my implementation (https://wyhaines.github.io/base58.cr/Base58/SS58.html) was to separate SS58 encoding of substrate addresses from SS58 encoding of arbitrary data, and for the arbitrary data, I assign a checksum length of 2 by default. Given that the use-case, using SS58 on payloads of something other than 1, 2, 4, 8, or 32 bytes, is currently undefined so far as I can tell, picking a reasonable default is about all that one can do right now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.