solana-labs / solana-web3.js

Solana JavaScript SDK
https://solana-labs.github.io/solana-web3.js
MIT License
2.09k stars 841 forks source link

(code included) Add getHDKeypairs() #1601

Open mikemaccana opened 1 year ago

mikemaccana commented 1 year ago

Motivation

Solana CLI includes a function to get HD keypairs from a seed. web3.js should do the same.

Example use case

A user wishes to get HD keypairs. They hunt around, and end up getting a code snippet from Solana cookbook or similar. The cookbook asks users to use various magic numbers (44, 501 and 0), with no explanation so the user isn't confident in the process.

Rather than ask every user to copypaste the same snippet, it's better we include this in web3.js.

This code is for both browsers and node so belongs in the main web3.js.

Details

As with the new getProgramDerivedAddress we can use get here - avoiding the need to for users to remember a more specific verb like derive.

import { derivePath } from "ed25519-hd-key";

// See https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki 
const PURPOSE = 44;
const SOLANA_COIN_TYPE = 501;
const NON_CHANGE_ADDRESS = 0;

export const getHDKeypairs = async (seed: UInt8Array, count: number): Array<Keypair> => {
  // The seed is the parent to many wallets
  // See https://github.com/solana-labs/solana/blob/master/web3.js/examples/get_account_info.js
  const seed = await bip39.mnemonicToSeed(mnemonic, password);

  const keyPairs: Array<Keypair> = [];

  for (let accountIndex = 0; accountIndex < count; accountIndex++) {
    const path = `m/${PURPOSE}'/${SOLANA_COIN_TYPE}'/${accountIndex}'/${NON_CHANGE_ADDRESS}'`;
    const keypair = Keypair.fromSeed(derivePath(path, seed.toString("hex")).key);
    keyPairs.push(keypair);
  }
  return keyPairs;
};
nickfrosty commented 12 months ago

This would be useful, but needs to clarify where derivePath comes from and the other packages

mikemaccana commented 11 months ago

Good point Nick. I've updated the example to include the derivePath() dependency. @steveluscher how do you feel about ed25519-hd-key being a web3.js dependency? It's small but depends on Tweetnacl so we might actually want to rebuild ed25519-hd-key using webcrypto per the rest of the new web3.js.

nickfrosty commented 11 months ago

I remember there were loads of efforts to move away from the tweetnacl package, so I don't think anyone wants to add it back in. Do the @noble/curves have the functionality to do this instead?

steveluscher commented 11 months ago

…how do you feel about ed25519-hd-key being a web3.js dependency?

Definitely, definitely not, but someone should absolutely release a package that creates the seed as you've outlined above! You can then use that with crypto.subtle.importKey(). No web3.js required.

What we will likely include in web3.js:

Given those two things, anyone can easily build an HD key generator that produces CryptoKey instances compatible with the new web3.js.

steveluscher commented 11 months ago

If, on the other hand, we can build an HD key generator from the ground up using the SubtleCrypto builtins, then maybe we should consider rolling the whole thing into @solana/keys.