ss58 Formats

In your previous examples, we explicitly passed the default ss58Format to the keyring. To understand the impact, we will use a real example of formatting using a known mnemonic.

...
// known mnemonic, well, now it is - don't use it for funds
const MNEMONIC = 'sample split bamboo west visual approve brain fox arch impact relief smile';
// type: ed25519, ssFormat: 42 (all defaults)
const keyring = new Keyring();
const pair = keyring.createFromUri(MNEMONIC);
// use the default as setup on init
// 5CSbZ7wG456oty4WoiX6a1J88VUbrCXLhrKVJ9q95BsYH4TZ
console.log('Substrate generic', pair.address);
// adjust the default ss58Format for Kusama
// CxDDSH8gS7jecsxaRL9Txf8H5kqesLXAEAEgp76Yz632J9M
keyring.setSS58Format(2);
console.log('Kusama', pair.address);
// adjust the default ss58Format for Polkadot
// 1NthTCKurNHLW52mMa6iA8Gz7UFYW5UnM3yTSpVdGu4Th7h
keyring.setSS58Format(0);
console.log('Polkadot', pair.address);

While the pair has not changed at all since creation, each change of the ss58Format yields a different output. The human-readable format does not represent any difference in the underlying data, but the encoding is different.

How is the encoding done?#

To understand the above example, we will re-construct the actual encoding, based on the actual publicKey and using the underlying util-crypto libraries. First we will decode the addresses generated above.

// 16,178,46,190,137,179,33,55,11,238,141,57,213,197,212,17,218,241,232,252,145,201,209,83,64,68,89,15,31,150,110,188
console.log(pair.publicKey);
// 16,178,46,190,137,179,33,55,11,238,141,57,213,197,212,17,218,241,232,252,145,201,209,83,64,68,89,15,31,150,110,188
console.log(keyring.decodeAddress('5CSbZ7wG456oty4WoiX6a1J88VUbrCXLhrKVJ9q95BsYH4TZ'));
// 16,178,46,190,137,179,33,55,11,238,141,57,213,197,212,17,218,241,232,252,145,201,209,83,64,68,89,15,31,150,110,188
console.log(keyring.decodeAddress('CxDDSH8gS7jecsxaRL9Txf8H5kqesLXAEAEgp76Yz632J9M'));
// 16,178,46,190,137,179,33,55,11,238,141,57,213,197,212,17,218,241,232,252,145,201,209,83,64,68,89,15,31,150,110,188
console.log(keyring.decodeAddress('1NthTCKurNHLW52mMa6iA8Gz7UFYW5UnM3yTSpVdGu4Th7h'));

Here we used the decodeAddress exposed on the keyring, however we could also import it from @polkadot/util-crypto. To understand what decode does, it takes the formatted address, verifies the checksums and then extracts the underlying publicKey. So the formatted address is a representation of the underlying AccountId, which on the default substrate chains maps to the publicKey for the pair.

This also means we can encode from publicKey back to an address.

// 5CSbZ7wG456oty4WoiX6a1J88VUbrCXLhrKVJ9q95BsYH4TZ
console.log(keyring.encodeAddress(pair.publicKey, 42));
// CxDDSH8gS7jecsxaRL9Txf8H5kqesLXAEAEgp76Yz632J9M
console.log(keyring.encodeAddress(pair.publicKey, 2));
// 1NthTCKurNHLW52mMa6iA8Gz7UFYW5UnM3yTSpVdGu4Th7h
console.log(keyring.encodeAddress(pair.publicKey, 0));

As per the previous example we used encodeAddress exposed on the keyring, however it could also be imported @polkadot/util-crypto.

Sign & verify#

Now that we have an understanding of the keyring basics, time to expand our horizons and use the pairs to sign and verify messages.