Skip to main content

PySeedRecover

This is a Python script to recover a BIP-39 mnemonic seed phrase if words are missing, were written down in a wrong or unknown order, or there is a typo in them, especially when used on the Cardano crypto currency network.

This script will probably not help you, when you get an empty wallet after restoring/importing with a seed phrase. Since seed phrases contain a checksum, it is very unlikely to get to another valid seed phrase (that opens an empty wallet) by wrong orders or typos. Empty wallets rather indicate that the restored/imported seed phrase is the wrong one altogether – saved from a trial that was never really used or something like that.

Crypto Currency Disclaimer

I do not endorse the use of crypto currencies or the ecosystems built around them, right now. Proof-of-work networks like Bitcoin are ecologically harmful on an unprecedented scale. Proof-of-stake networks like Cardano do not have that problem, but still have to prove their utility for real use cases.

Do not invest money that you cannot afford to completely lose into crypto currencies (let alone NFTs and other strange tokens without any real value)! Their future is totally unclear.

The technology behind them is interesting, though. And this script does explore a part of this technology (and hopefully helps some people get their wallets back).

Installation

You need a working Python 3 installation on your machine. I do development on the newest stable version (3.10), but test it also on the oldest still supported version (3.7).

Installation instructions for Windows can be found at: https://docs.microsoft.com/en-us/windows/python/beginners (That guide links to Python 3.7, Python 3.10 can be found at https://www.microsoft.com/de-de/p/python-310/9pjpw5ldxlz5.)

For APT-based Linux distributions apt install python3 python3-pip should be enough and for Arch-based Linux distributions pacman -S python python-pip will install everything that is needed.

This script is then installed by pip install PySeedRecover (or pip3 install PySeedRecover if the standard Python on your operating system is still Python 2).

After that, seedrecover -h should show you the usage information of the script.

If for some reason pip installed scripts are not on the path for executables on your system and you do not want to bother what is wrong, it can also be executed by python -m seedrecover -h (or python3 -m seedrecover -h if Python 3 is not the standard on your system).

Usage

usage: seedrecover [-h] [-w FILE] [-s EDIT DISTANCE] [-o]
                   [-l LENGTH] [-m POSITION [POSITION ...]]
                   [-a ADDRESS [ADDRESS ...]] [-k]
                   [WORD ...]

Recover Cardano seed phrases.

positional arguments:
  WORD                  known words of seed phrase

options:
  -h, --help            show this help message and exit
  -w FILE, --wordlist FILE
                        wordlist to use (default: english)
  -s EDIT DISTANCE, --similar EDIT DISTANCE
                        try similar words up to edit distance
  -o, --order           try different orders of seed words
  -l LENGTH, --length LENGTH
                        length of seed phrase
  -m POSITION [POSITION ...], --missing POSITION [POSITION ...]
                        missing word positions
  -a ADDRESS [ADDRESS ...], --address ADDRESS [ADDRESS ...]
                        check for stake addresses
  -k, --koios           check on koios.rest

WARNING: If you give your (partial knowledge of your) seed phrase to this script on the command line, this information will be in the history of the shell you are using. It is also visible on screen and can perhaps be scrolled back. For security, clear the history and close the terminal after using this script.

We are using a test wallet that I generated with the following seed phrase:

ladder   long     kangaroo inherit  unknown  prize
else     second   enter    addict   mystery  valve
riot     attitude area     blind    fabric   symbol
skill    sunset   goose    shock    gasp     grape

The stake address for this test wallet is stake1u9t04dtwptk5776eluj6ruyd782k66npnf55tdrp6dvwnzs24r8yq.

The simplest way to get something wrong are just small typos, some of which may lead to other valid words, some of which not. If we suspect that we did that, we can choose with the -s/--similar option, up to which edit distance we want to search for valid seed phrases:

$ seedrecover -s 1 ladder long kangaroo inherit unknown price else second \
  enter addict mystery valve riot altitude area blind fabric symbol skill \
  sunset goose shock gap grape
ladder => ladder
long => long, song
kangaroo => kangaroo
inherit => inherit
unknown => unknown
price => price, pride, prize, rice
else => else
second => second
enter => enter
addict => addict
mystery => mystery
valve => valve
riot => riot
'altitude' not in wordlist!
altitude => attitude
area => area, arena
blind => bind, blind
fabric => fabric
symbol => symbol
skill => skill, skull, still
sunset => sunset
goose => goose
shock => shock, sock, stock
gap => gap, gas, gasp
grape => grace, grape
Length not set. Using smallest length for given phrase.
0 of 24 words missing.
Seed phrases checked:          6 total,          1 fulfilled checksum,          1 without repetitions
stake1uy5gjrvr3kql0t8j4vsn99w6y4h8zc95e22m4edjjg894kcg644qn: ladder long kangaroo inherit unknown price else second enter addict mystery valve riot attitude area bind fabric symbol skill sunset goose shock gasp grape
[…]
Seed phrases checked:        492 total,          3 fulfilled checksum,          3 without repetitions
stake1u9t04dtwptk5776eluj6ruyd782k66npnf55tdrp6dvwnzs24r8yq: ladder long kangaroo inherit unknown prize else second enter addict mystery valve riot attitude area blind fabric symbol skill sunset goose shock gasp grape
[…]
Seed phrases checked:      1_728 total,          8 fulfilled checksum,          8 without repetitions

seedrecover first reports, which words are gonna be checked for the given words (due to -s and to words missing in the wordlist). It then tells us, which total seed words length it is considering and how many words are missing. During the checking phase, progress and found stake addresses with their seed phrases are reported.

If a word is missing from your seed phrase and you know, at which position it is missing, you can give the position (or several possible positions) with the -m/--missing option (as usual on Unix systems, the list of options can be terminated with -- to stop the list of positions and start the list of known words of the seed phrase):

$ seedrecover -m 1 24 -- ladder long kangaroo inherit unknown prize else \
  second enter addict mystery valve riot attitude area blind fabric symbol \
  skill sunset goose shock gasp
ladder => ladder
[…]
gasp => gasp
Length not set. Using smallest length for given phrase.
1 of 24 words missing.
Seed phrases checked:        155 total,          1 fulfilled checksum,          1 without repetitions
stake1uy23h76c4pad8hpluvhrfzvx5ll837epvppprk6wfazvjmcu9j0fn: battle ladder long kangaroo inherit unknown prize else second enter addict mystery valve riot attitude area blind fabric symbol skill sunset goose shock gasp
[…]
Seed phrases checked:      2_863 total,         12 fulfilled checksum,         12 without repetitions
stake1u9t04dtwptk5776eluj6ruyd782k66npnf55tdrp6dvwnzs24r8yq: ladder long kangaroo inherit unknown prize else second enter addict mystery valve riot attitude area blind fabric symbol skill sunset goose shock gasp grape
[…]
Seed phrases checked:      4_096 total,         16 fulfilled checksum,         16 without repetitions

If you do not know, at which position a word is missing (or if several words are missing), the possibilities become too many to manually check. With -a/--address, we can give one or several stake addresses to search for:

$ seedrecover -a stake1u9t04dtwptk5776eluj6ruyd782k66npnf55tdrp6dvwnzs24r8yq \
  stake1u9vm3pq6f3a5hyvu4z80jyetuk8wt9kvdv648a6804zh0vscalg0n -- ladder long \
  kangaroo inherit unknown prize else second enter mystery valve riot \
  attitude area blind fabric symbol skill sunset goose shock gasp grape
ladder => ladder
[...]
enter => enter
mystery => mystery
[...]
grape => grape
Length not set. Using smallest length for given phrase.
1 of 24 words missing.
Seed phrases checked:     18_459 total,         74 fulfilled checksum,         74 without repetitions
Searched stake address found:
stake1u9t04dtwptk5776eluj6ruyd782k66npnf55tdrp6dvwnzs24r8yq: ladder long kangaroo inherit unknown prize else second enter addict mystery valve riot attitude area blind fabric symbol skill sunset goose shock gasp grape
Seed phrases checked:     49_152 total,        195 fulfilled checksum,        195 without repetitions

This check already takes almost a minute, but it only gives us the searched stake address and its seed phrase.

Note: It is a good idea to give the stake addresses of all your wallets and accounts to -a. You never know if you mixed up the seed phrases and the one you are currently looking at is maybe for a different wallet than you think.

It is also possible to abbreviate the searched stake address(es) by ... in the middle:

$ seedrecover -a stake1u9...24r8yq stake1u9...calg0n -- ladder long \
  kangaroo inherit unknown prize else second enter mystery valve riot \
  attitude area blind fabric symbol skill sunset goose shock gasp grape
ladder => ladder
[...]
enter => enter
mystery => mystery
[...]
grape => grape
Length not set. Using smallest length for given phrase.
1 of 24 words missing.
Seed phrases checked:     18_459 total,         74 fulfilled checksum,         74 without repetitions
Searched stake address found:
stake1u9t04dtwptk5776eluj6ruyd782k66npnf55tdrp6dvwnzs24r8yq: ladder long kangaroo inherit unknown prize else second enter addict mystery valve riot attitude area blind fabric symbol skill sunset goose shock gasp grape
Seed phrases checked:     49_152 total,        195 fulfilled checksum,        195 without repetitions

If the searched stake address is unknown, the stake addresses can be checked via koios.rest for previous activity.

$ seedrecover -k ladder long kangaroo inherit unknown prize else second \
  enter mystery valve riot attitude area blind fabric symbol skill sunset \
  goose shock gasp grape
ladder => ladder
[...]
enter => enter
mystery => mystery
[...]
grape => grape
Length not set. Using smallest length for given phrase.
1 of 24 words missing.
Seed phrases checked:     18_459 total,         74 fulfilled checksum,         74 without repetitions
Active stake address found:
stake1u9t04dtwptk5776eluj6ruyd782k66npnf55tdrp6dvwnzs24r8yq: ladder long kangaroo inherit unknown prize else second enter addict mystery valve riot attitude area blind fabric symbol skill sunset goose shock gasp grape
Seed phrases checked:     49_152 total,        195 fulfilled checksum,        195 without repetitions

In this case, 195 requests to Koios were made. This number will be much higher with more missing words or in combination with other checks. Koios does a rate limit of 100 requests per 10 seconds.

If you are unsure about the order, the -o/--order option allows to check certain plausible reorderings (exchanges of rows and columns in a rectangular notation of the seed phrase):

$ seedrecover -o -a stake1u9...24r8yq stake1u9...calg0n -- ladder else riot \
  skill long second attitude sunset kangaroo enter area goose inherit \
  addict blind shock unknown mystery fabric gasp prize valve symbol grape
ladder => ladder
[...]
grape => grape
Length not set. Using smallest length for given phrase.
0 of 24 words missing.
Seed phrases checked:          4 total,          1 fulfilled checksum,          1 without repetitions
Searched stake address found:
stake1u9t04dtwptk5776eluj6ruyd782k66npnf55tdrp6dvwnzs24r8yq: ladder long kangaroo inherit unknown prize else second enter addict mystery valve riot attitude area blind fabric symbol skill sunset goose shock gasp grape
Seed phrases checked:         23 total,          1 fulfilled checksum,          1 without repetitions

A check of all 24! = 6.2×10^23 permutations (for the 24 word seed phrase case) is not feasible.

When combining the options (typos, missing worders, order), the number of possible seed phrases explodes pretty quickly. The search for a stake address can take hours or even days.

Development

To set up a development environment for this project, just clone it, create a virtual environment and install it with pip in editable mode.

$ git clone https://github.com/HeptaSean/PySeedRecover.git
$ cd PySeedRecover
$ python3 -m venv venv
$ source venv/bin/activate
$ pip install -U pip setuptools wheel
$ pip install -e .

I am using pydocstyle, pycodestyle and mypy to lint my code and doctest to test it with tests embedded in the docstrings. The script lint.sh in the root of the project runs them all for individual modules.

$ ./lint.sh seedrecover/<module>.py

The test with Python 3.7 is done using pyenv.

$ PYENV_VERSION="3.7.13" python -m venv /tmp/venv
$ source /tmp/venv/bin/activate
$ pip install -U pip setuptools wheel
$ pip install .
$ ./lint.sh seedrecover/<module>.py (for all modules)
$ (test invocations from README.md)

Upload to PyPI is done with flit.

$ flit publish

Changelog

For versions >= 1.9.0, a SOURCE_DATE_EPOCH is given to enable flit's reproducible builds.

  • 2022-02-12 – 1.0.0: Initial release.
  • 2022-02-12 – 1.0.1: Bugfix: English wordlist was not included in install.
  • 2022-02-19 – 1.0.2: Also allow execution by python -m seedrecover.
  • 2022-10-24 – 1.9.0: Change build system from setuptools to flit. Replace Blockfrost with Koios. SOURCE_DATE_EPOCH=1666601000

Metadata

Release files for PySeedRecover 1.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for PySeedRecover 1.9.0
File Size Uploaded Provenance Actions
PySeedRecover-1.9.0.tar.gz 28.0 kB Without provenance Details for PySeedRecover-1.9.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for PySeedRecover 1.9.0
File Interpreter ABI Platform Actions
pyseedrecover-1.9.0-py2.py3-none-any.whl Python 3, Python 2 none any Details for pyseedrecover-1.9.0-py2.py3-none-any.whl

Total release size: 54.3 kB

Release files / PySeedRecover-1.9.0.tar.gz

Download URL PySeedRecover-1.9.0.tar.gz
Size 28.0 kB
Tags Source
SHA-256 checksum
How to use checksums
53baf60d6363dfb755c8268d0dd659de4c18cbcfd58875db74ffe34c94a63075
BLAKE2b-256 checksum
How to use checksums
3e1ff3c4d7899dcb8d719e1b0f57ba51e942b7dc964bc2c270225897fc16a5fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.28.1

Release files / pyseedrecover-1.9.0-py2.py3-none-any.whl

Download URL pyseedrecover-1.9.0-py2.py3-none-any.whl
Size 26.3 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
2b04297f74b2e7e5a392b2b4bb9b9a5c7ef4488f4df009509570e2af9efbca33
BLAKE2b-256 checksum
How to use checksums
8deec9c04725833f6a3ed3228fe58ee9e2130b546c9120636e9ca152e34d8987
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via python-requests/2.28.1

Security for PySeedRecover 1.9.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page