UNPKG

@alephium/web3-wallet

Version:
136 lines 6.53 kB
import { isGroupedAccount, publicKeyFromPrivateKey, utils } from '@alephium/web3'; import { addressFromPublicKey } from '@alephium/web3'; import { groupOfAddress } from '@alephium/web3'; import { groupOfPrivateKey, SignerProviderWithCachedAccounts, TOTAL_NUMBER_OF_GROUPS, web3 } from '@alephium/web3'; import { generateMnemonic as _generateMnemonic, mnemonicToSeedSync } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; import { HDKey } from '@scure/bip32'; import { PrivateKeyWallet } from './privatekey-wallet.js'; export function generateMnemonic(wordLength) { return _generateMnemonic(wordlist, wordLength === 12 ? 128 : 256); } export function deriveHDWalletPrivateKey(mnemonic, keyType, _fromAddressIndex, passphrase) { const seed = mnemonicToSeedSync(mnemonic, passphrase); const masterKey = HDKey.fromMasterSeed(seed); const fromAddressIndex = _fromAddressIndex ?? 0; const child = masterKey.derive(getHDWalletPath(keyType, fromAddressIndex)); if (!child.privateKey) throw new Error('Missing private key'); return utils.binToHex(child.privateKey); } export function deriveSecp256K1PrivateKey(mnemonic, fromAddressIndex, passphrase) { return deriveHDWalletPrivateKey(mnemonic, 'default', fromAddressIndex, passphrase); } export function deriveSchnorrPrivateKey(mnemonic, fromAddressIndex, passphrase) { return deriveHDWalletPrivateKey(mnemonic, 'bip340-schnorr', fromAddressIndex, passphrase); } export function deriveHDWalletPrivateKeyForGroup(mnemonic, targetGroup, keyType, _fromAddressIndex, passphrase) { if (targetGroup < 0 || targetGroup > TOTAL_NUMBER_OF_GROUPS) { throw Error(`Invalid target group for HD wallet derivation: ${targetGroup}`); } const fromAddressIndex = _fromAddressIndex ?? 0; const privateKey = deriveHDWalletPrivateKey(mnemonic, keyType, fromAddressIndex, passphrase); if (groupOfPrivateKey(privateKey, keyType) === targetGroup) { return [privateKey, fromAddressIndex]; } else { return deriveHDWalletPrivateKeyForGroup(mnemonic, targetGroup, keyType, fromAddressIndex + 1, passphrase); } } export function deriveSecp256K1PrivateKeyForGroup(mnemonic, targetGroup, _fromAddressIndex, passphrase) { return deriveHDWalletPrivateKeyForGroup(mnemonic, targetGroup, 'default', _fromAddressIndex, passphrase); } export function deriveSchnorrPrivateKeyForGroup(mnemonic, targetGroup, _fromAddressIndex, passphrase) { return deriveHDWalletPrivateKeyForGroup(mnemonic, targetGroup, 'bip340-schnorr', _fromAddressIndex, passphrase); } export function getHDWalletPath(keyType, addressIndex) { if (addressIndex < 0 || !Number.isInteger(addressIndex) || addressIndex.toString().includes('e')) { throw new Error('Invalid address index path level'); } const coinType = "1234'"; const keyTypeNum = (() => { switch (keyType) { case 'default': return 0; case 'bip340-schnorr': return 1; case 'gl-secp256k1': return 2; case 'gl-secp256r1': return 3; case 'gl-ed25519': return 4; case 'gl-webauthn': return 5; default: throw new Error(`Unsupported key type: ${keyType}`); } })(); return `m/44'/${coinType}/${keyTypeNum}'/0/${addressIndex}`; } export function getSecp259K1Path(addressIndex) { return getHDWalletPath('default', addressIndex); } export function getSchnorrPath(addressIndex) { return getHDWalletPath('bip340-schnorr', addressIndex); } export function getGlSecp256K1Path(addressIndex) { return getHDWalletPath('gl-secp256k1', addressIndex); } export function getGlSecp256R1Path(addressIndex) { return getHDWalletPath('gl-secp256r1', addressIndex); } export function getGlEd25519Path(addressIndex) { return getHDWalletPath('gl-ed25519', addressIndex); } export function getGlWebauthnPath(addressIndex) { return getHDWalletPath('gl-webauthn', addressIndex); } export class HDWallet extends SignerProviderWithCachedAccounts { constructor({ mnemonic, keyType, nodeProvider, explorerProvider, passphrase }) { super(); this.mnemonic = mnemonic; this.keyType = keyType ?? 'default'; if (this.keyType !== 'default' && this.keyType !== 'bip340-schnorr' && this.keyType !== 'gl-secp256k1') { throw new Error(`Invalid key type ${keyType}`); } this.passphrase = passphrase; this.nodeProvider = nodeProvider ?? web3.getCurrentNodeProvider(); this.explorerProvider = explorerProvider ?? web3.getCurrentExplorerProvider(); } getNextFromAddressIndex(targetGroup) { let usedAddressIndex = -1; for (const account of this._accounts.values()) { const accountGroup = isGroupedAccount(account) ? account.group : groupOfAddress(account.address); if ((targetGroup === undefined || accountGroup == targetGroup) && account.addressIndex > usedAddressIndex) { usedAddressIndex = account.addressIndex; } } return usedAddressIndex + 1; } deriveAndAddNewAccount(targetGroup) { const fromAddressIndex = this.getNextFromAddressIndex(targetGroup); let priKey; let addressIndex = fromAddressIndex; if (targetGroup !== undefined) { const [_priKey, _addressIndex] = deriveHDWalletPrivateKeyForGroup(this.mnemonic, targetGroup, this.keyType, fromAddressIndex, this.passphrase); priKey = _priKey; addressIndex = _addressIndex; } else { priKey = deriveHDWalletPrivateKey(this.mnemonic, this.keyType, fromAddressIndex, this.passphrase); } const publicKey = publicKeyFromPrivateKey(priKey, this.keyType); const address = addressFromPublicKey(publicKey, this.keyType); const group = groupOfAddress(address); const account = { keyType: this.keyType, address, group, publicKey, addressIndex }; this._accounts.set(account.address, account); return account; } async signRaw(signerAddress, hexString) { const account = await this.getAccount(signerAddress); const privateKey = deriveHDWalletPrivateKey(this.mnemonic, account.keyType, account.addressIndex, this.passphrase); return PrivateKeyWallet.sign(privateKey, hexString, this.keyType); } } //# sourceMappingURL=hd-wallet.js.map