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.
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).
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 install
ed 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: 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.
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
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