# Wallet entropy using bip39 - derive seed and build wallet using nodejs

**URL:** <https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325>\
**Category:** Technical Questions\
**Tags:** best-practices\
**Created:** [August 28, 2025, 1:12pm UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325 "2025-08-28T13:12:39Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![fosgate29](https://avatars.discourse-cdn.com/v4/letter/f/74df32/32.png) [@fosgate29](https://forum.midnight.network/u/fosgate29)\
**Post date:** [August 28, 2025, 1:12pm UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/1 "2025-08-28T13:12:39Z")

</div>

I created a new wallet using Lace wallet. And I got 24 words. Now I would like to know how I can derive the seed from 24 words in nodejs. I was using bip39 and was able to build my wallet. Here is my code:  
`const entropy = bip39.mnemonicToEntropy(my_mnemonic, wordlist);`  
` seed = Buffer.from(entropy).toString('hex');`

But right now the code is not working anymore. It returns a different wallet address. It worked fine in the past.

Thanks

---

<div class="post-metadata">

**Author:** ![sylwek](https://avatars.discourse-cdn.com/v4/letter/s/0ea827/32.png) [@sylwek](https://forum.midnight.network/u/sylwek)\
**Post date:** [October 10, 2025, 10:45am UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/2 "2025-10-10T10:45:07Z")

</div>

I encounter the same problem. How to get seed from 24 words that were generate by Lace wallet preview. I want to pass this seed to `WalletBuilder.build` function from `@midnight-ntwrk/wallet`

---

<div class="post-metadata">

**Author:** ![fosgate29](https://avatars.discourse-cdn.com/v4/letter/f/74df32/32.png) [@fosgate29](https://forum.midnight.network/u/fosgate29)\
**Post date:** [October 10, 2025, 6:45pm UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/3 "2025-10-10T18:45:02Z")

</div>

I was able to share this issue with someone from Midnight. He isn’t a dev, but will try to get some help for this issue (derive seed from mnemonic words)

---

<div class="post-metadata">

**Author:** ![bochaco](https://yyz2.discourse-cdn.com/flex010/user_avatar/forum.midnight.network/bochaco/32/112_2.png) [@bochaco](https://forum.midnight.network/u/bochaco)\
**Post date:** [October 13, 2025, 3:03pm UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/4 "2025-10-13T15:03:32Z")

</div>

I was investigating this some weeks ago, I’m not fully sure if this is a valid way of doing it, and I couldn’t get it to properly derive the correct seed, but this is what I was attempting without success:

```auto
import * as bip39 from '@scure/bip39';
import { WalletBuilder } from '@midnight-ntwrk/wallet';
import { createHash, createHmac } from 'crypto';

const seed64 = await bip39.mnemonicToSeed(mnemonic, '');
const hmac = createHmac('sha512', seed64);
hmac.update(Buffer.from(''));
const hmacResult = hmac.digest();
const masterPrivateKey = hmacResult.slice(0, 32);
const seed = masterPrivateKey.toString('hex');
const wallet = await WalletBuilder.build(.., seed, ...);

```

---

<div class="post-metadata">

**Author:** ![fosgate29](https://avatars.discourse-cdn.com/v4/letter/f/74df32/32.png) [@fosgate29](https://forum.midnight.network/u/fosgate29)\
**Post date:** [October 20, 2025, 11:39pm UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/5 "2025-10-20T23:39:26Z")

</div>

I found this:  
[https://www.npmjs.com/package/@midnight-ntwrk/wallet-sdk-hd](https://www.npmjs.com/package/@midnight-ntwrk/wallet-sdk-hd)

---

<div class="post-metadata">

**Author:** ![bochaco](https://yyz2.discourse-cdn.com/flex010/user_avatar/forum.midnight.network/bochaco/32/112_2.png) [@bochaco](https://forum.midnight.network/u/bochaco)\
**Post date:** [October 21, 2025, 3:10am UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/6 "2025-10-21T03:10:23Z")

</div>

I don’t see an API in that lib to get the seed from a given mnemonic.

---

<div class="post-metadata">

**Author:** ![fosgate29](https://avatars.discourse-cdn.com/v4/letter/f/74df32/32.png) [@fosgate29](https://forum.midnight.network/u/fosgate29)\
**Post date:** [November 3, 2025, 3:11pm UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/7 "2025-11-03T15:11:50Z")

</div>

With the help from Midnight team and Lace, I learned how to derive it:  
Use BIP39 library: [https://www.npmjs.com/package/bip39](https://www.npmjs.com/package/bip39)

```javascript
const mnemonic = ''abandon car water …. “; // your mnemonic
const seed = bip39.mnemonicToSeedSync(mnemonic);
const generatedWallet = HDWallet.fromSeed(seed);

let finalSeed;
if (generatedWallet.type == 'seedOk') {

  const zswapKey = generatedWallet.hdWallet.selectAccount(0).selectRole(Roles.Zswap).deriveKeyAt(0);

  if (zswapKey.type === 'keyDerived') {
     finalSeed = Buffer.from(zswapKey.key).toString('hex')
  } else {
    console.error('Error deriving key');
  }

} else {
  console.error('Error generating HDWallet');
}

await WalletBuilder.build(
this.indexerUrl,
this.indexerWsUrl,
this.proofServer,
this.node,
finalSeed || '',
getZswapNetworkId(),
'info',
 );

```

---

<div class="post-metadata">

**Author:** ![luislucena](https://yyz2.discourse-cdn.com/flex010/user_avatar/forum.midnight.network/luislucena/32/20_2.png) [@luislucena](https://forum.midnight.network/u/luislucena)\
**Post date:** [November 3, 2025, 5:25pm UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/9 "2025-11-03T17:25:05Z")

</div>

I was going to comment on something, but then I realised that this solution is effective! great job guys 🙂

---

<div class="post-metadata">

**Author:** ![cryptophonic](https://avatars.discourse-cdn.com/v4/letter/c/3d9bf3/32.png) [@cryptophonic](https://forum.midnight.network/u/cryptophonic)\
**Post date:** [January 18, 2026, 4:12am UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/10 "2026-01-18T04:12:05Z")

</div>

This doesn’t appear to work for generating mn\_dust\_\*\*\* addresses that the latest lace midnight preview generates. i.e. as opposed to mn\_shielded and mn\_unshielded. I’m trying to programmatically generate a mn\_dust address from the mnemonic generated from Lace wallet and nothing works.

---

<div class="post-metadata">

**Author:** ![norm](https://yyz2.discourse-cdn.com/flex010/user_avatar/forum.midnight.network/norm/32/408_2.png) [@norm](https://forum.midnight.network/u/norm)\
**Post date:** [February 4, 2026, 1:08am UTC](https://forum.midnight.network/t/wallet-entropy-using-bip39-derive-seed-and-build-wallet-using-nodejs/325/11 "2026-02-04T01:08:19Z")

</div>

there was a gap in the documentation. it was fixed in the following issue:

> <https://github.com/input-output-hk/lace/issues/2133>
>
> \## Summary
> 
> Lace v2 generates \*\*different Midnight addresses\*\* from the same mne…monic compared to the official \`@midnight-ntwrk/wallet-sdk-hd\` package. This prevents wallet interoperability between Lace and other wallets built using the standard Midnight SDK.
> 
> \## Background
> 
> I'm developing an Android wallet for Midnight blockchain. To ensure users can restore wallets between Lace and my application, I need to understand Lace's exact key derivation implementation.
> 
> \## Test Case
> 
> \*\*Mnemonic (BIP-39 standard test vector):\*\*
> \`\`\`
> abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art
> \`\`\`
> 
> \*\*BIP-39 Passphrase:\*\* \`""\` (empty string)
> 
> \### Result Comparison
> 
> | Implementation | Address |
> |---|---|
> | \*\*Lace v2\*\* | \`mn\_addr\_preview15jlkezafp4mju3v7cdh3ywre2y2s3szgpqrkw8p4tzxjqhuaqhlshsa9pv\` |
> | \*\*Midnight SDK\*\* | \`mn\_addr\_preview19kxg8sxrsty37elmm6yd68tuy7prryjst2r48eapf2fdtd8z4gpq8xczf2\` |
> 
> ❌ \*\*Addresses do not match\*\*
> 
> \## My Implementation (Using Official Midnight SDK)
> 
> \`\`\`javascript
> import { mnemonicToSeed } from '@scure/bip39';
> import { wordlist } from '@scure/bip39/wordlists/english.js';
> import { HDWallet, Roles } from '@midnight-ntwrk/wallet-sdk-hd';
> import \* as ledger from '@midnight-ntwrk/ledger-v6';
> import { MidnightBech32m } from '@midnight-ntwrk/wallet-sdk-address-format';
> 
> const mnemonic = "abandon abandon abandon ... art"; // 24 words
> const masterSeed = await mnemonicToSeed(mnemonic, '', wordlist); // BIP-39
> 
> const hdWalletResult = HDWallet.fromSeed(masterSeed); // BIP-32
> const account = hdWalletResult.hdWallet.selectAccount(0);
> 
> // Derivation path: m/44'/2400'/0'/0/0 (NightExternal role)
> const nightExternalKey = account.selectRole(Roles.NightExternal).deriveKeyAt(0);
> const privateKey = nightExternalKey.key;
> 
> // Public key and address generation
> const privateKeyHex = bytesToHex(privateKey);
> const publicKey = ledger.signatureVerifyingKey(privateKeyHex);
> const userAddress = ledger.addressFromKey(publicKey);
> const addressBuffer = Buffer.from(userAddress, 'hex');
> const bech32Address = new MidnightBech32m('addr', 'preview', addressBuffer).asString();
> 
> console.log(bech32Address);
> // Output: mn\_addr\_preview19kxg8sxrsty37elmm6yd68tuy7prryjst2r48eapf2fdtd8z4gpq8xczf2
> // Expected (from Lace): mn\_addr\_preview15jlkezafp4mju3v7cdh3ywre2y2s3szgpqrkw8p4tzxjqhuaqhlshsa9pv
> \`\`\`
> 
> \## Exhaustive Search Conducted
> 
> Tested \*\*400+ addresses\*\* with no matches:
> \- ✅ Accounts: 0-4
> \- ✅ Roles: NightExternal (0), NightInternal (1), Dust (2), Zswap (3)
> \- ✅ Indices: 0-19 per role
> \- ✅ Passphrase: empty string
> 
> \## Decoded Address Comparison
> 
> \*\*Lace Address:\*\*
> \`\`\`
> Type: addr
> Network: preview
> Data (hex): a4bf6c8ba90d772e459ec36f123879511508c0480807671c35588d205f9d05ff
> Length: 32 bytes
> \`\`\`
> 
> \*\*Midnight SDK Address:\*\*
> \`\`\`
> Type: addr
> Network: preview
> Data (hex): 2d8c83c0c382c91f67fbde88dd1d7c27823192505a8753e7a14a92d5b4e2aa02
> Length: 32 bytes
> \`\`\`
> 
> The underlying public key data is completely different.
> 
> \## Hypothesis
> 
> Since Lace is primarily a \*\*Cardano wallet\*\* (with Midnight support added in v2), I suspect Lace may be using:
> 
> 1. \*\*Cardano's CIP-3 or CIP-1852\*\* key derivation instead of standard BIP-32
> 2. \*\*Different derivation path\*\* than Midnight's \`m/44'/2400'/account'/role/index\`
> 3. \*\*Cardano-specific cryptographic primitives\*\* (Ed25519 instead of secp256k1?)
> 
> \## Questions for Lace Development Team
> 
> 1. \*\*Does Lace v2 use the standard \`@midnight-ntwrk/wallet-sdk-hd\` package for Midnight addresses?\*\*
> 
> 2. \*\*If not, what key derivation method does Lace use?\*\*
> - CIP-3 (Cardano's Icarus derivation)?
> - CIP-1852 (Cardano HD wallets)?
> - Custom implementation?
> 
> 3. \*\*What is the exact derivation path Lace uses for Midnight addresses?\*\*
> - Is it \`m/44'/2400'/0'/0/0\` as per Midnight docs?
> - Or something else?
> 
> 4. \*\*What cryptographic curve does Lace use for Midnight?\*\*
> - secp256k1 (Midnight standard)?
> - Ed25519 (Cardano standard)?
> 
> 5. \*\*Is there technical documentation for Lace v2's Midnight implementation?\*\*
> 
> \## Why This Matters
> 
> \*\*Wallet interoperability is critical for the Midnight ecosystem.\*\* Users should be able to:
> \- ✅ Create a wallet in Lace → Restore in any Midnight wallet
> \- ✅ Create a wallet in any Midnight wallet → Restore in Lace
> 
> Currently, this is \*\*not possible\*\* due to different address generation methods.
> 
> \## Request
> 
> Could the Lace development team either:
> 1. \*\*Provide technical documentation\*\* on Lace v2's Midnight key derivation
> 2. \*\*Point to the relevant source code\*\* in this repository
> 3. \*\*Confirm if Lace plans to standardize\*\* on the official Midnight SDK
> 
> This information is essential for building interoperable wallets in the Midnight ecosystem.
> 
> \## Environment
> 
> \- \*\*Lace Version:\*\* v2.x (with Midnight Preview support)
> \- \*\*Midnight SDK Versions:\*\*
> - \`@midnight-ntwrk/wallet-sdk-hd\`: 3.0.0-beta.9
> - \`@midnight-ntwrk/ledger-v6\`: 6.1.0-alpha.6
> - \`@midnight-ntwrk/wallet-sdk-address-format\`: 3.0.0-beta.9
> \- \*\*Test Network:\*\* Midnight Preview (testnet)
> 
> \## Related Documentation
> 
> \- \[Midnight Wallet Developer Guide\](https://docs.midnight.network/develop/guides/wallet-dev-guide)
> \- \[Midnight HD Wallet Package\](https://github.com/midnightntwrk/midnight-wallet/tree/main/packages/hd)
> \- \[BIP-32 Standard\](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
> \- \[BIP-39 Standard\](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki)
> 
> \---
> 
> \*\*Thank you for your help!\*\* Understanding Lace's implementation will help build a more interoperable Midnight ecosystem.
