Skip to content

Instantly share code, notes, and snippets.

@adept
Created September 4, 2025 19:30
Show Gist options
  • Select an option

  • Save adept/2ca330b8192fda8a447221cee2bcf55e to your computer and use it in GitHub Desktop.

Select an option

Save adept/2ca330b8192fda8a447221cee2bcf55e to your computer and use it in GitHub Desktop.
Derive XTZ addresses and private keys from seed phrase

Why

I had a small amount of XTZ left from various experiments in the address created via Briskett.app. I wanted to stop delegating and get rid of the coins, but Briskett.app did not have an option to undelegate.

I wanted to import the seed phrase into another wallet, but it turns out that Briskett uses m/44'/1729'/0' as a key derivation path, when pretty much any other software wallet under the sun uses m/44'/1729'/0'/0', so from the same seed phrase they derive a different private key and therefore a different address as well.

Solution

  1. Write a small python script to derive the key the same way Briskett.app does
  2. Import the private key into Temple wallet

Example

$ python3 xtz.py
Mnemonic string: clarify flash mansion forward turtle dinner drill turkey dry draw kidney brain fruit tiny aspect
Passphrase: 
Derivation path: m/44'/1729'/0' (briskett.app)
  Address (XTZ): tz1VAGBCxbuXZb78XqjgPCGhTo4CiU3NCvg9
  Derived private key (base58) - could be imported into Temple wallet: edsk4P9dYvTbH4bQJU9DRKLkFdGgDdmHM9KZ4PiT4L4D3fS2b4woJ4
Derivation path: m/44'/1729'/0'/0' (Temple Wallet, Ledger, etc)
  Address (XTZ): tz1XZzisKx1JUCcdojghd7yv7dBLCpzXRpxB
  Derived private key (base58) - could be imported into Temple wallet: edsk3DZ2rM9ycFhJbZFEbKsdSzBQmwnhXPrfX4m26Qk75VSE6H1BXt

Inspriation

Inspired by:

"""Example of keys derivation using BIP32 (ed25519 curve based on SLIP-0010)."""
from bip_utils import Bip32Slip10Ed25519, Bip39MnemonicGenerator, Bip39SeedGenerator, Bip39WordsNum, XtzAddrEncoder, XtzAddrPrefixes, Base58Alphabets, Base58Decoder, Base58Encoder
from bip_utils.utils.crypto import DoubleSha256
def encode_tezos_private_key(private_key_bytes):
"""
Encode a 32-byte private key to Tezos edsk format using Base58Check encoding
"""
# Tezos Ed25519 secret key prefix (edsk)
TEZOS_EDSK_PREFIX = bytes([13, 15, 58, 7]) # 0x0d0f3a07
prefixed_key = TEZOS_EDSK_PREFIX + private_key_bytes
# Compute double SHA256 hash for checksum (Base58Check standard)
hash = DoubleSha256.QuickDigest(prefixed_key)
# Take first 4 bytes as checksum, and append to data
checksum = hash[:4]
data_with_checksum = prefixed_key + checksum
return Base58Encoder.Encode(data_with_checksum)
mnemonic=input(f"Mnemonic string: ")
passphrase=input("Passphrase: ")
# Generate seed from mnemonic
seed_bytes = Bip39SeedGenerator(mnemonic).Generate(passphrase=passphrase)
# Construct private key from seed, using ed25519 curve for key derivation
bip32_mst_ctx = Bip32Slip10Ed25519.FromSeed(seed_bytes)
# Print master key
# print(f"Master key (bytes): {bip32_mst_ctx.PrivateKey().Raw().ToHex()}")
# print(f"Master key (base58): {encode_tezos_private_key(bip32_mst_ctx.PrivateKey().Raw().ToBytes())}")
# Derive a path
for (path, path_desc) in [("m/44'/1729'/0'","briskett.app"), ("m/44'/1729'/0'/0'", "Temple Wallet, Ledger, etc")]:
print(f"Derivation path: {path} ({path_desc})")
bip32_der_ctx = bip32_mst_ctx.DerivePath(path)
# Print address in XTZ encoding
# The BIP32 elliptic curve shall be the same one expected by XTZ (ed25519 in this case)
xtz_addr = XtzAddrEncoder.EncodeKey(bip32_der_ctx.PublicKey().KeyObject(),prefix=XtzAddrPrefixes.TZ1)
print(f" Address (XTZ): {xtz_addr}")
# Print key
# print(f" Derived private key (bytes): {bip32_der_ctx.PrivateKey().Raw().ToHex()}")
# print(f" Derived public key (bytes): {bip32_der_ctx.PublicKey().RawCompressed().ToHex()}")
print(f" Derived private key (base58) - could be imported into Temple wallet: {encode_tezos_private_key(bip32_der_ctx.PrivateKey().Raw().ToBytes())}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment