Skip to content

Mnemonic seeds

Mnemonic implements BIP39. A recovery phrase of 12 to 24 words encodes the entropy behind an HD wallet. See the guide’s Mnemonics page for how the words, the checksum and the passphrase fit together. Word lists for English, French, Italian, Japanese, Korean, Spanish and both Chinese scripts are included (e.g. Mnemonic(wordList: Wordlist.FRENCH)).

Generating and validating both load the word list, so both are async.

final mnemonic = Mnemonic(); // English word list by default
final String words = await mnemonic.generateMnemonic(); // 12 words (128 bits of entropy)
final String longer = await mnemonic.generateMnemonic(strength: 256); // 24 words
final bool ok = await mnemonic.validateMnemonic(words); // true

Validation checks every word against the list, as well as the checksum built into the last word:

final mnemonic = Mnemonic();
// Twelve valid words, but the last word does not match the checksum.
await mnemonic.validateMnemonic(
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon'); // false

toSeedHex (or toSeed, for bytes) stretches the phrase and an optional passphrase into a 64-byte seed. Here is the BIP39 test vector:

final mnemonic = Mnemonic();
const words = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
final seedHex = mnemonic.toSeedHex(words, 'TREZOR'); // the passphrase is optional
// c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04

We feed the seed to HDPrivateKey.fromSeed, and derive along our wallet’s path. BIP44 paths for Bitcoin SV use coin type 236.

final seedHex = Mnemonic().toSeedHex(words);
final master = HDPrivateKey.fromSeed(seedHex, NetworkType.MAIN);
// BIP44 path: purpose 44', coin type 236' (Bitcoin SV), account 0', external chain 0, index 0.
final firstKey = master.deriveChildKey("m/44'/236'/0'/0/0").privateKey;
final firstAddress = firstKey.toAddress(networkType: NetworkType.MAIN);

generateMnemonic takes a randomBytes function, which is useful for a hardware random source or for repeatable tests:

// We supply our own entropy source: a function that returns `size` random bytes.
Uint8List allZeros(int size) => Uint8List(size);
final words = await Mnemonic().generateMnemonic(randomBytes: allZeros);
// abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about

generateMnemonic and validateMnemonic read the word list from the package’s files through a package: URI. That works on the Dart VM. Where package files are not available at run time (a Flutter app, Flutter web), we bundle the word list as an asset and pass a loader to generateMnemonic2 and validateMnemonic2. Neither toSeed nor toSeedHex needs a word list.

// The loader receives the word list and its file name ("english", "french", ...)
// and returns the list's contents, one word per line. In a Flutter app,
// we would read the file from our bundled assets instead of the file system.
Future<String> loadWords(Wordlist? wordlist, String name) =>
File('test/data/bip39/$name.txt').readAsString();
final mnemonic = Mnemonic();
final words = await mnemonic.generateMnemonic2(loadWords);
final ok = await mnemonic.validateMnemonic2(words, loadWords);

In Flutter the body of the loader becomes rootBundle.loadString('assets/bip39/$name.txt'), with the word list files copied from the dartsv package’s lib/src/bip39/wordlists/ folder into your assets.