Base58
Base58 encoding in Bitcoin: a 58-character alphabet with no 0, O, I or l, used for legacy addresses, WIF keys and extended keys.
By Dimitar Zurnov · Updated 4 Nov 2026
Adapted from Learn Me a Bitcoin by Greg Walker.
Base58 is a way to write a large number as text, using 58 characters. Bitcoin uses it for legacy addresses, WIF private keys and extended keys. The alphabet leaves out 0, O, I and l, which look alike in many fonts. It also has no symbols, so a string copies and pastes cleanly.
What characters does Base58 use?
The alphabet has 58 characters, in this order:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
It starts from the 62 letters and digits of a standard alphanumeric set, and removes four: 0, O, I and l. Satoshi explained the choice in a comment in the first release, in base58.h, a mirror of Bitcoin v0.1. Look-alike characters cause mistakes, email breaks lines at punctuation, and a string of only letters and digits selects whole with a double click.
What does "base 58" mean?
The base is the number of symbols a system uses. Everyday numbers use base 10, with ten digits from 0 to 9. Hexadecimal uses base 16. Base58 uses 58 symbols, so it writes the same number in fewer characters. The number 9999 is 270f in hex and 3yQ in Base58. All three mean the same value. They only use different symbols.
How do you encode a number in Base58?
Divide the number by 58 again and again. Each remainder is an index into the alphabet, so it gives one character. Stop when the quotient reaches zero. Read the characters from the last remainder back to the first.
The short script below does the same job. The 1 at the front of a result comes from leading zero bytes, which the next section explains.
ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
def base58_encode(data: bytes) -> str:
n = int.from_bytes(data, "big")
out = ""
while n > 0:
n, r = divmod(n, 58) # the remainder is the next character
out = ALPHABET[r] + out
# each leading zero byte becomes a "1"
return "1" * (len(data) - len(data.lstrip(b"\x00"))) + out
def base58_decode(text: str) -> bytes:
n = 0
for c in text:
n = n * 58 + ALPHABET.index(c)
body = n.to_bytes((n.bit_length() + 7) // 8, "big")
return b"\x00" * (len(text) - len(text.lstrip("1"))) + body
print(base58_encode(bytes.fromhex("001fe032088d648e925f15a24a0cf6a1e80f01bf9194686373")))
# 13uYXKWq8VoZgQApSxTXHPtYDs1m1xGi4z
print(base58_decode("13uYXKWq8VoZgQApSxTXHPtYDs1m1xGi4z").hex())
# 001fe032088d648e925f15a24a0cf6a1e80f01bf9194686373
How do you decode a Base58 string?
Decoding runs the other way. Each character gives an index from 0 to 57. Multiply that index by 58 raised to the character's position, counting from the right starting at zero, then add the results. For BukQL, the working is:
L = 19 × 58^0 = 19
Q = 23 × 58^1 = 1334
k = 43 × 58^2 = 144652
u = 52 × 58^3 = 10145824
B = 10 × 58^4 = 113164960
---------
123456789
The base58_decode function in the script above reaches the same total with Horner's method. It multiplies the running total by 58 for each character, from left to right, and adds the next index. That gives the same sum.
What do leading zeros become?
A number does not change when you add zeros at the front. So an encoder that only works with the number would lose any zero bytes at the start of the data. Bitcoin avoids that by turning each leading zero byte (00) into a 1 character. The decoder turns each leading 1 back into a zero byte.
That is why a P2PKH address begins with 1. Its payload starts with the version byte 00. A P2SH address starts with 3, because its version byte is 05.
Which prefixes give which first character?
Bitcoin adds a version byte to the front of the data before it encodes it. The version byte sets the first character of the result, so you can tell what the string is by looking at it.
| Mainnet prefix | First character | What it encodes |
|---|---|---|
00 |
1 | P2PKH address |
05 |
3 | P2SH address |
80 |
5, K or L | WIF private key |
0488ADE4 |
xprv | Extended private key |
0488B21E |
xpub | Extended public key |
| Testnet prefix | First character | What it encodes |
|---|---|---|
6F |
m or n | P2PKH address |
C4 |
2 | P2SH address |
EF |
c or 9 | WIF private key |
04358394 |
tprv | Extended private key |
043587CF |
tpub | Extended public key |
A WIF key starts with 5 when its public key is uncompressed. It starts with K or L when the key is compressed. The WIF page explains the flag that makes the difference.
What is Base58Check?
Base58Check is Base58 with a checksum added first. You append the first 4 bytes of the double SHA-256 hash to the data, then encode everything in Base58. The checksum catches most typing mistakes before the string is used.
Our example key's address is built this way. The payload is the version byte 00 and its 20-byte public key hash. The checksum is 94686373. Base58 encoding the 25 bytes gives 13uYXKWq8VoZgQApSxTXHPtYDs1m1xGi4z, the script's first printed value above.
Where is Base58 used?
- Legacy addresses. P2PKH addresses start with
1, and P2SH addresses start with3. See address. - WIF private keys. A WIF string is a private key in Base58Check.
- Extended keys. The
xprvandxpubstrings for HD wallets are Base58Check too.
SegWit and Taproot addresses do not use Base58. They use bech32, which has a stronger checksum and never mixes upper and lower case.
Summary
Base58 writes large numbers with 58 characters. It drops 0, O, I and l, so it is easy to read and copy. A few version bytes decide the first character, and a checksum guards the whole string. Never paste a private key, in WIF or any other form, into a website. A site that sees the key can keep a copy.
Sources: Bitcoin Core's base58.cpp is the reference implementation, and the Base58Check encoding page on the Bitcoin Wiki lists the prefixes.
In this section: Keys and addresses
Something look wrong? Tell us