DevToys Web Pro iconDevToys Web ProBlog
Çeviri: LocalePack logoLocalePack
Bizi değerlendirin:
Tarayıcı uzantısını deneyin:
← Back to Blog

Base58 Encoding Guide: Bitcoin Addresses, IPFS CIDs, and When to Use It

8 min read

Base58 is a binary-to-text encoding scheme designed for one specific purpose: producing strings that humans can read, transcribe, and type without making mistakes. You have probably seen it in Bitcoin wallet addresses and IPFS content identifiers without realizing there is a deliberate design decision behind every character in the alphabet. Use the Base58 encoder and decoder to follow along with the examples in this guide.

The Problem Base58 Solves

Base64 is the most common binary-to-text encoding. It is compact and fast to compute. But it has two practical problems when humans need to interact with the encoded string:

  • Ambiguous characters: The digits 0 (zero) and O (uppercase letter O) look identical in many fonts. So do l (lowercase L) and I (uppercase letter I). A single misread character corrupts the entire value.
  • URL and filename-unsafe characters: Base64 uses +, /, and = padding. These characters need percent-encoding in URLs and are invalid in filenames on some systems. Base64url solves the +// issue by substituting - and _, but the readability problem remains.

Satoshi Nakamoto designed Base58 for Bitcoin by taking the alphanumeric character set and removing exactly the four characters that cause visual confusion: 0, O, l, and I. The result is an alphabet where every character is visually distinct, no padding is needed, and the encoded string is safe to use in URLs and filenames.

The Base58 Alphabet

The Bitcoin variant of Base58 is the most widely used. Its 58-character alphabet, in order from value 0 to 57, is:

123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz

Notice what is absent:

Removed characterLooks likeReason
0 (digit zero)O (uppercase O)Identical in most sans-serif fonts
O (uppercase O)0 (digit zero)Identical in most sans-serif fonts
l (lowercase L)I (uppercase I)Indistinguishable in many typefaces
I (uppercase I)l (lowercase L), 1Indistinguishable in many typefaces

Also note that the digit 1 represents value 0 in Base58 — the alphabet starts at 1, not 0. This means a leading zero byte in the input encodes as the character 1, and leading 1 characters in a Base58 string represent leading zero bytes in the binary data.

Base58 vs Base58Check

Plain Base58 encodes arbitrary bytes with no error detection. If you mistype one character, you get silently wrong data. For Bitcoin addresses, a mistyped address means lost funds — there is no way to reverse the transaction.

Base58Check adds a 4-byte checksum before encoding:

  1. Start with the payload bytes (e.g., a public key hash).
  2. Compute SHA256(SHA256(payload)) — a double SHA-256 hash.
  3. Take the first 4 bytes of that hash as the checksum.
  4. Append those 4 bytes to the payload.
  5. Encode the result with Base58.

When you decode a Base58Check string, you re-compute the checksum from the payload and verify it matches the appended bytes. Any single-character transcription error will almost certainly produce a checksum mismatch, making it safe to display an error instead of accepting a corrupt address.

IPFS version-0 CIDs use plain Base58 (not Base58Check) because the multihash itself already provides integrity — the hash validates the content, not the identifier format.

Bitcoin Address Anatomy

A legacy Bitcoin P2PKH address (starting with 1) is constructed as follows:

1. Take the public key (33 or 65 bytes compressed/uncompressed)
2. SHA256(pubkey)            32 bytes
3. RIPEMD160(result)         20 bytes  (this is the "public key hash")
4. Prepend version byte 0x00 for mainnet P2PKH
5. Double-SHA256 the 21 bytes take first 4 bytes as checksum
6. Append checksum 25 bytes total
7. Base58 encode the address you see (e.g., 1A1zP1eP5...)

The version byte controls the address type and network. 0x00 gives mainnet P2PKH addresses starting with 1. 0x05 gives P2SH addresses starting with 3. Testnet addresses use 0x6F and start with m or n.

The RIPEMD160(SHA256(pubkey)) step shrinks a 256-bit public key down to a 160-bit hash. This saves space in every transaction output and provides an extra layer of security: the public key itself is not revealed until the owner spends funds from that address.

IPFS CID Format

IPFS uses content-addressed storage: every file is identified by a hash of its content rather than its location. The identifier is called a Content Identifier (CID).

CIDv0 is the original format. It encodes a SHA-256 multihash of the content using plain Base58:

QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG

The Qm prefix is not a magic constant — it falls out naturally because the SHA-256 multihash prefix bytes (0x1220) happen to encode to Qm in Base58.

CIDv1 is the newer format. It adds a multibase prefix character and supports multiple hash functions and encodings:

bafybeigdyrzt52gant6oigtbqz7y5j3zs5jd2aqppzq3cnfxchevhvo5iy

The leading b is the multibase prefix indicating Base32 (lowercase). CIDv1 can also use Base58 (prefix z) or Base16 (prefix f). The IPFS community prefers Base32 for CIDv1 because Base32 is case-insensitive, making CIDs safe to use as DNS labels and subdomain-based gateway URLs.

Base58 vs Base64 Size

Base58 is slightly less space-efficient than Base64 because it uses a smaller alphabet:

EncodingAlphabet sizeSize expansionPaddingURL safe
Base6464 characters~133% of inputYes (=)No (needs Base64url variant)
Base5858 characters~138% of inputNoYes
Base3232 characters~160% of inputYes (=)Yes (case-insensitive)

Base58 is also slower to compute than Base64. Base64 works on fixed 3-byte input blocks and maps them to 4 output characters using simple bit shifts — a constant-time operation. Base58 uses big-integer division of the entire input by 58 repeatedly, which is an O(n²) algorithm relative to the input length. For small inputs like wallet addresses this is negligible, but Base58 is not suitable for encoding large binary blobs.

Code Examples

JavaScript

npm install bs58
import bs58 from 'bs58';

// Encode bytes to Base58
const bytes = Buffer.from('Hello, Base58!');
const encoded = bs58.encode(bytes);
console.log(encoded); // e.g. "2NEpo7TZRRrLZSi2U"

// Decode Base58 back to bytes
const decoded = bs58.decode(encoded);
console.log(Buffer.from(decoded).toString()); // "Hello, Base58!"

Python

pip install base58
import base58

# Encode bytes to Base58
data = b"Hello, Base58!"
encoded = base58.b58encode(data)
print(encoded)  # b'2NEpo7TZRRrLZSi2U'

# Decode Base58 back to bytes
decoded = base58.b58decode(encoded)
print(decoded)  # b'Hello, Base58!'

# Base58Check encode (adds double-SHA256 checksum)
encoded_check = base58.b58encode_check(data)
decoded_check = base58.b58decode_check(encoded_check)  # verifies checksum

Rust

# Cargo.toml
[dependencies]
bs58 = "0.5"
use bs58;

fn main() {
    let data = b"Hello, Base58!";

    // Encode
    let encoded = bs58::encode(data).into_string();
    println!("{}", encoded); // "2NEpo7TZRRrLZSi2U"

    // Decode
    let decoded = bs58::decode(&encoded).into_vec().unwrap();
    println!("{}", String::from_utf8(decoded).unwrap()); // "Hello, Base58!"
}

When to Use Base58 — and When Not To

Base58 is the right choice in a narrow set of situations. Outside those situations, Base64 is almost always better.

ScenarioUse Base58?Reason
Wallet addresses, short identifiers humans must transcribeYesNo ambiguous characters reduces copy errors
IPFS CIDv0 compatibilityYesProtocol standard
Encoding large files or binary blobsNoO(n²) algorithm is too slow; use Base64
JWT tokens, API keys passed in headersNoBase64url is faster and widely supported
Data embedded in HTML or JSONNoBase64 has universal library support
Case-insensitive contexts (DNS labels, subdomains)NoBase32 is better — case-insensitive by design

The core rule: use Base58 when a human will need to read, type, or speak the encoded value. Use Base64 everywhere else. The ~5% size penalty and slower encoding of Base58 are worth paying only when the human-readability benefit is real.

If you work with Bitcoin addresses, IPFS identifiers, or any other system that uses Base58, the Base58 encoder and decoder lets you convert values instantly in your browser. For a broader overview of encoding formats, see the encoders and decoders guide and the Base64 in practice article.


Encode and decode Base58 values directly in your browser with the Base58 encoder — no server, no data leaving your machine.