@alephium/web3-wallet
Version:
Simple wallets for Alephium
136 lines • 6.53 kB
JavaScript
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