Skip to content
DonateБГ

Keys and addresses

WIF private key

WIF private key format: Wallet Import Format writes a Bitcoin private key in Base58, with a network byte and a checksum. Learn to encode and decode it.

By Dimitar Zurnov · Updated 4 Nov 2026
Adapted from Learn Me a Bitcoin by Greg Walker.

WIF, short for Wallet Import Format, is a way to write a private key as a Base58 string. It adds a byte that marks the network, an optional byte that marks compressed keys, and a checksum that catches typing mistakes. Mainnet WIF strings start with 5, K or L.

A WIF string is not a different key. The same 32 bytes can be written as hex or as WIF, and you can always convert back. Because a WIF string holds the key itself, it needs the same care as the private key it encodes.

Why use WIF instead of hex?

WIF has three practical advantages over a 64-digit hex key:

  • It is shorter, 51 or 52 characters, so it is easier to copy.
  • It starts with a recognizable character, so you can tell a private key from a random string.
  • Its checksum catches a mistyped character when you import it. It cannot repair the mistake, but it stops a bad string from being used.

The compression flag adds a fourth benefit. It tells a wallet which public key format the key was meant for, so the wallet knows which address to look for.

What are the fields of a WIF string?

Field Size Value
Version byte 1 byte 80 on mainnet, ef on testnet
Private key 32 bytes The raw key, unchanged
Compression flag 1 byte, optional 01 for a compressed public key. Absent for an uncompressed one.
Checksum 4 bytes The first 4 bytes of hash256 of all the fields above

The bytes are then encoded in Base58, an alphabet that leaves out 0, O, I and l so that characters are harder to confuse.

How do you encode a private key as WIF?

  1. Start with the 32-byte private key in hex.
  2. Put the version byte in front: 80 for mainnet, ef for testnet.
  3. Add 01 at the end if the key is for a compressed public key. Most keys in use today are compressed.
  4. Hash the data twice with SHA-256. Keep the first 4 bytes and append them as the checksum.
  5. Base58 encode the whole result.
A 32-byte private key gets a version byte, a compression byte and a checksum, then is Base58 encoded. version byte: 80 means mainnet 80 32 bytes: the private key ef235aacf90d9f4aadd8c92e4b2562e1 d9eb97f0df9ba3b508258739cb013db2 optional: 01 means compressed 01 4 bytes: checksum of the above 66557e53 Base58 encode WIF private key L5EZftvrYaSudiozVRzTqLcHLN DoVn7H5HSfM9BAN6tMJX8oTWz6
The example key from this page. Do not use it with real coins.

This page uses its own example key, ef235aacf90d9f4aadd8c92e4b2562e1d9eb97f0df9ba3b508258739cb013db2. It is not the key from the private key page, which starts dbf34ebb, so the two examples do not mix. Here is this key worked through the steps. The bytes before the checksum are 80, then the key, then 01. The checksum is 66557e53, and the WIF string is L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6. The key is published, so anyone can spend coins sent to it. Do not use it.

This Python function does the same steps. It needs no library beyond the standard one.

import hashlib

BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"

def base58(data: bytes) -> str:
    n = int.from_bytes(data, "big")
    out = ""
    while n > 0:
        n, r = divmod(n, 58)
        out = BASE58[r] + out
    pad = len(data) - len(data.lstrip(b"\x00"))  # each leading zero byte becomes a "1"
    return "1" * pad + out

def hash256(data: bytes) -> bytes:
    return hashlib.sha256(hashlib.sha256(data).digest()).digest()

def wif_encode(key_hex: str, testnet=False, compressed=True) -> str:
    data = (b"\xef" if testnet else b"\x80") + bytes.fromhex(key_hex)
    if compressed:
        data += b"\x01"
    data += hash256(data)[:4]
    return base58(data)

print(wif_encode("ef235aacf90d9f4aadd8c92e4b2562e1d9eb97f0df9ba3b508258739cb013db2"))
# L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6

Check any encoder against this example before you trust it with a real key.

How do you decode a WIF string?

A WIF string decodes to 38 bytes: a version byte, the 32-byte private key, an optional flag and a checksum. WIF private key L5EZftvrYaSudiozVRzTqLcHLN DoVn7H5HSfM9BAN6tMJX8oTWz6 Base58 decode byte 0 version: 80, mainnet bytes 1 to 32 the private key ef235aacf90d9f4aadd8c92e4b2562e1 d9eb97f0df9ba3b508258739cb013db2 byte 33 compression: 01, optional bytes 34 to 37 checksum, to catch typos
Byte positions count from zero. An uncompressed key has 37 bytes and no flag byte.

Decoding runs the steps in reverse:

  1. Base58 decode the string. A compressed WIF gives 38 bytes, and an uncompressed one gives 37.
  2. Hash everything except the last 4 bytes, and compare the first 4 bytes of that hash with the checksum. A mismatch means a typo.
  3. Read the version byte to find the network.
  4. Take bytes 1 to 32, counting from zero, as the private key.
  5. If byte 33 is present, it is the compression flag.

The same logic in Python:

def wif_decode(wif: str) -> str:
    n = 0
    for c in wif:
        n = n * 58 + BASE58.index(c)
    raw = n.to_bytes((n.bit_length() + 7) // 8, "big")
    payload, checksum = raw[:-4], raw[-4:]
    if hash256(payload)[:4] != checksum:
        raise ValueError("checksum mismatch: the string has a typo")
    return payload[1:33].hex()  # bytes 1 to 32: the private key

Run it on the example string and you get the example key back.

Which first character shows the network and key type?

The first character changes with the version byte and the compression flag. These are the four cases:

Starts with Network Public key
5 Mainnet Uncompressed
K or L Mainnet Compressed
9 Testnet Uncompressed
c Testnet Compressed

Most mainnet keys you will meet start with K or L, because almost every wallet uses compressed public keys today.

Where is WIF used?

WIF is mainly used to move a single private key between programs. Bitcoin Core's importprivkey RPC takes a WIF string, and dumpprivkey returns one. The dumpprivkey command arrived in late 2011, in pull request 574. Many wallets ask for WIF when you import a key, rather than raw hex.

WIF is not encrypted. BIP 38 defines a passphrase-protected form that starts with 6P. A plain WIF string is not protected that way, so treat it as the key itself.

How do you keep a WIF string safe?

  • A WIF string is not encrypted. Anyone who sees it can spend the coins, so guard it like the raw key.
  • Do not paste it into a website or app you do not control. A site that asks for one, or generates one for you, can keep a copy.
  • Check any tool against known examples before you use it, or write your own encoder as shown above.

The format is documented in the Bitcoin Wiki's Wallet import format page, which describes the prefixes and the flag used here.

The next page, public key, shows how the same private key gives a public key.

In this section: Keys and addresses

All of Bitcoin from zero

Something look wrong? Tell us