Skip to main content

Generating Keys and Addresses

BIP32 Keys

There are two main categories of keys in this library. There are the raw PublicKey and PrivateKey which are used for cryptographically signing/verifying, and BIP32PrivateKey/BIP32PublicKey which in addition to this have the ability to derive additional keys from them following the BIP32 derivation scheme variant called BIP32-Ed25519, which will be referred to as BIP32 for brevity. We use the BIP44 spec variant for Ed25519 as well for the derivation paths using 1852 or 44 as the purpose consant and 1815 for the coin type depending on address type. See this doc for more details.

This is demonstrated with the below code

function harden(num: number): number {
return 0x80000000 + num;
}


const rootKey = CardanoWasm.BIP32PrivateKey.from_bech32("xprv17qx9vxm6060qjn5fgazfue9nwyf448w7upk60c3epln82vumg9r9kxzsud9uv5rfscxp382j2aku254zj3qfx9fx39t6hjwtmwq85uunsd8x0st3j66lzf5yn30hwq5n75zeuplepx8vxc502txx09ygjgx06n0p");
const accountKey = rootKey
.derive(harden(1852)) // purpose
.derive(harden(1815)) // coin type
.derive(harden(0)); // account #0

const utxoPubKey = accountKey
.derive(0) // external
.derive(0)
.to_public();

const stakeKey = accountKey
.derive(2) // chimeric
.derive(0)
.to_public();

BIP39 Entropy

To generate a BIP32PrivateKey from a BIP39 recovery phrase it must be first converted to entropy following the BIP39 protocol(). This library does not directly handle that, but once entropy is created it is possible to use Bip32PrivateKey.from_bip39_entropy(entropy, password). For more information see the CIP3 Cardano improvement proposal. The code below uses the bip39 npm package to generate a root BIP32PrivateKey from a BIP39 mnemonic.

import { mnemonicToEntropy } from 'bip39';

const entropy = mnemonicToEntropy(
[ "test", "walk", "nut", "penalty", "hip", "pave", "soap", "entry", "language", "right", "filter", "choice" ].join(' ')
);

const rootKey = CardanoWasm.Bip32PrivateKey.from_bip39_entropy(
Buffer.from(entropy, 'hex'),
Buffer.from(''),
);

Use in Addresses

Once we have reached the desired derivation path, we must convert the BIP32PrivateKey or BIP32PublicKey to a PrivateKey or PublicKey by calling .to_raw_key() on them with the exception of Byron addresses. For example, to create an address using the utxoPubKey and stakeKey in the first example, we can do:

// base address with staking key
const baseAddr = CardanoWasm.BaseAddress.new(
CardanoWasm.NetworkInfo.mainnet().network_id(),
CardanoWasm.StakeCredential.from_keyhash(utxoPubKey.to_raw_key().hash()),
CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash()),
);

// enterprise address without staking ability, for use by exchanges/etc
const enterpriseAddr = CardanoWasm.EnterpriseAddress.new(
CardanoWasm.NetworkInfo.mainnet().network_id(),
CardanoWasm.StakeCredential.from_keyhash(utxoPubKey.to_raw_key().hash())
);

// pointer address - similar to Base address but can be shorter, see formal spec for explanation
const ptrAddr = CardanoWasm.PointerAddress.new(
CardanoWasm.NetworkInfo.mainnet().network_id(),
CardanoWasm.StakeCredential.from_keyhash(utxoPubKey.to_raw_key().hash()),
CardanoWasm.Pointer.new(
100, // slot
2, // tx index in slot
0 // cert indiex in tx
)
);

// reward address - used for withdrawing accumulated staking rewards
const rewardAddr = CardanoWasm.RewardAddress.new(
CardanoWasm.NetworkInfo.mainnet().network_id(),
CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_raw_key().hash())
);

// bootstrap address - byron-era addresses with no staking rights
const byronAddr = CardanoWasm.ByronAddress.icarus_from_key(
utxoPubKey, // Ae2* style icarus address
CardanoWasm.NetworkInfo.mainnet().protocol_magic()
);

Note that the byron-era address can only be created in this library from icarus-style addresses that start in Ae2 and that Daedalus-style addresses starting in Dd are not directly supported.

These are all address variant types with information specific to its address type. There is also an Address type which represents any of those variants, which is the type use in most parts of the library. For example to create a TransactionOutput manually we would have to first convert from one of the address variants by doing:

const address = baseAddress.to_address();

const output = CardanoWasm.TransactionOutput(address, BigNum.from_str("365"));

If the address is already a Shelley address in raw bytes or a bech32 string we can create it directly via:

const addr = CardanoWasm.Address.from_bech32("addr1vyt3w9chzut3w9chzut3w9chzut3w9chzut3w9chzut3w9cj43ltf");

Other Key Types

Conversion between cardano-cli 128-byte XPrv keys and BIP32PrivateKey is also supported:

const bip32PrivateKey = CardanoWasm.BIP32PrivateKey.from_128_xprv(xprvBytes);
assert(xprvBytes == CardanoWasm.BIP32PrivateKey.to_128_xprv());

96-byte XPrv keys are identical to BIP32PrivateKeys byte-wise and no conversion is needed. For more details see this document regarding legacy keys.

There is also LegacyDaedalusPrivateKey which is used for creating witnesses for legacy Daedalus Dd-type addresses.