Public Key to Shelly Address

I have a Cardano Public Key. I want to derive a Shelly address(starts with addr1) from the public key. Any help in terms of code will be helpful.

Public Key: 2adf6929fdfd736fd41ed8680d57ea9e8d55aa75529feb053c1a4a8fc0735250

How addresses are constructed is described in https://cips.cardano.org/cips/cip19/.

Basically, it is the Bech32 encoding of the blake2b-224 hash digest of the public key.

For typical base addresses, you would also need the public key of the stake key pair. Addresses with just a payment part are shorter and are usually called “enterprise” addresses.

Code in Python:

import hashlib

BECH32_CHARSET = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l'
BECH32_GENERATOR = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3]


def bech32_encode(readable: str, data: bytes) -> str:
    """Encode data with BECH32 using given human-readable part.

    Algorithm is specified in:
    https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
    """
    # Recode bytes into 5-bit integers:
    data_int5 = []
    bits = 0
    current = 0
    for byte in data:
        bits += 8
        current = (current << 8) + byte
        while bits >= 5:
            bits -= 5
            int5 = current >> bits
            data_int5.append(int5)
            current -= int5 << bits
    if bits:
        int5 = current << (5 - bits)
        data_int5.append(int5)
    # Calculate checksum:
    to_check = [ord(char) >> 5 for char in readable] + [0]
    to_check += [ord(char) & 31 for char in readable]
    to_check += data_int5
    to_check += [0, 0, 0, 0, 0, 0]
    checksum = 1
    for int5 in to_check:
        top = checksum >> 25
        checksum = (checksum & 0x1ffffff) << 5 ^ int5
        for i in range(5):
            checksum ^= BECH32_GENERATOR[i] if ((top >> i) & 1) else 0
    checksum ^= 1
    # Compose and encode 5-bit integers into characters:
    data_int5 += [(checksum >> 5 * (5 - i)) & 31 for i in range(6)]
    bech32 = readable + '1'
    bech32 += ''.join([BECH32_CHARSET[int5] for int5 in data_int5])
    return bech32


public_key = bytes.fromhex('2adf6929fdfd736fd41ed8680d57ea9e'
                           '8d55aa75529feb053c1a4a8fc0735250')
public_key_hash = hashlib.blake2b(public_key, digest_size=28).digest()
enterprise_bytes = bytes([0b01100001]) + public_key_hash
enterprise_bech32 = bech32_encode('addr', enterprise_bytes)
assert enterprise_bech32 == ('addr1v9xaw2syjeuxp5njzeq26dkvx5a7vacz5mdrzdz'
                             'wlrfjxyc67a2zn')
base_bytes = bytes([0b00000001]) + public_key_hash + public_key_hash
base_bech32 = bech32_encode('addr', base_bytes)
assert base_bech32 == ('addr1q9xaw2syjeuxp5njzeq26dkvx5a7vacz5mdrzdzwlrfjx'
                       'y6d6u4qf9ncvrf8y9jq45mvcdfmuems9fk6xy6ya7xnyvfsm5j'
                       'wj3')

The generated addresses on Cardanoscan:
https://cardanoscan.io/address/addr1v9xaw2syjeuxp5njzeq26dkvx5a7vacz5mdrzdzwlrfjxyc67a2zn
Enterprise address, Cardanoscan tells us that the there is no stake key, no delegation part, header byte is 61. … And this address has actually been used to receive 3.5 million ADA from another enterprise address on 23rd December just to send them back to that same other address on 2nd January.
https://cardanoscan.io/address/addr1q9xaw2syjeuxp5njzeq26dkvx5a7vacz5mdrzdzwlrfjxy6d6u4qf9ncvrf8y9jq45mvcdfmuems9fk6xy6ya7xnyvfsm5jwj3
Base address, header byte is 01, used same public key for payment and delegation part here (this will typically not be the case, you have separate key pairs for that, different derivation paths in the case of CIP-1852 wallets and accounts).

1 Like