@alephium/web3-wallet
Version:
Simple wallets for Alephium
158 lines (154 loc) • 8.11 kB
JavaScript
;
/*
Copyright 2018 - 2022 The Alephium Authors
This file is part of the alephium project.
The library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with the library. If not, see <http://www.gnu.org/licenses/>.
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HDWallet = exports.getSchnorrPath = exports.getSecp259K1Path = exports.getHDWalletPath = exports.deriveSchnorrPrivateKeyForGroup = exports.deriveSecp256K1PrivateKeyForGroup = exports.deriveHDWalletPrivateKeyForGroup = exports.deriveSchnorrPrivateKey = exports.deriveSecp256K1PrivateKey = exports.deriveHDWalletPrivateKey = exports.generateMnemonic = void 0;
const web3_1 = require("@alephium/web3");
const web3_2 = require("@alephium/web3");
const web3_3 = require("@alephium/web3");
const web3_4 = require("@alephium/web3");
const bip39 = __importStar(require("bip39"));
const noble_wrapper_1 = require("./noble-wrapper");
const privatekey_wallet_1 = require("./privatekey-wallet");
function generateMnemonic(wordLength) {
return bip39.generateMnemonic(wordLength === 12 ? 128 : 256);
}
exports.generateMnemonic = generateMnemonic;
function deriveHDWalletPrivateKey(mnemonic, keyType, _fromAddressIndex, passphrase) {
const seed = bip39.mnemonicToSeedSync(mnemonic, passphrase);
const masterKey = noble_wrapper_1.bip32.fromSeed(seed);
const fromAddressIndex = _fromAddressIndex ?? 0;
const keyPair = masterKey.derivePath(getHDWalletPath(keyType, fromAddressIndex));
if (!keyPair.privateKey)
throw new Error('Missing private key');
return keyPair.privateKey.toString('hex');
}
exports.deriveHDWalletPrivateKey = deriveHDWalletPrivateKey;
function deriveSecp256K1PrivateKey(mnemonic, fromAddressIndex, passphrase) {
return deriveHDWalletPrivateKey(mnemonic, 'default', fromAddressIndex, passphrase);
}
exports.deriveSecp256K1PrivateKey = deriveSecp256K1PrivateKey;
function deriveSchnorrPrivateKey(mnemonic, fromAddressIndex, passphrase) {
return deriveHDWalletPrivateKey(mnemonic, 'bip340-schnorr', fromAddressIndex, passphrase);
}
exports.deriveSchnorrPrivateKey = deriveSchnorrPrivateKey;
function deriveHDWalletPrivateKeyForGroup(mnemonic, targetGroup, keyType, _fromAddressIndex, passphrase) {
if (targetGroup < 0 || targetGroup > web3_4.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 ((0, web3_4.groupOfPrivateKey)(privateKey, keyType) === targetGroup) {
return [privateKey, fromAddressIndex];
}
else {
return deriveHDWalletPrivateKeyForGroup(mnemonic, targetGroup, keyType, fromAddressIndex + 1, passphrase);
}
}
exports.deriveHDWalletPrivateKeyForGroup = deriveHDWalletPrivateKeyForGroup;
function deriveSecp256K1PrivateKeyForGroup(mnemonic, targetGroup, _fromAddressIndex, passphrase) {
return deriveHDWalletPrivateKeyForGroup(mnemonic, targetGroup, 'default', _fromAddressIndex, passphrase);
}
exports.deriveSecp256K1PrivateKeyForGroup = deriveSecp256K1PrivateKeyForGroup;
function deriveSchnorrPrivateKeyForGroup(mnemonic, targetGroup, _fromAddressIndex, passphrase) {
return deriveHDWalletPrivateKeyForGroup(mnemonic, targetGroup, 'bip340-schnorr', _fromAddressIndex, passphrase);
}
exports.deriveSchnorrPrivateKeyForGroup = deriveSchnorrPrivateKeyForGroup;
function getHDWalletPath(keyType, addressIndex) {
if (addressIndex < 0 || !Number.isInteger(addressIndex) || addressIndex.toString().includes('e')) {
throw new Error('Invalid address index path level');
}
// Being explicit: we always use coinType 1234 no matter the network.
const coinType = "1234'";
const keyTypeNum = keyType === 'default' ? 0 : 1;
return `m/44'/${coinType}/${keyTypeNum}'/0/${addressIndex}`;
}
exports.getHDWalletPath = getHDWalletPath;
function getSecp259K1Path(addressIndex) {
return getHDWalletPath('default', addressIndex);
}
exports.getSecp259K1Path = getSecp259K1Path;
function getSchnorrPath(addressIndex) {
return getHDWalletPath('bip340-schnorr', addressIndex);
}
exports.getSchnorrPath = getSchnorrPath;
// In-memory HDWallet for simple use cases. Advanced wallet better used the derivation functions above.
class HDWallet extends web3_4.SignerProviderWithCachedAccounts {
constructor({ mnemonic, keyType, nodeProvider, explorerProvider, passphrase }) {
super();
this.mnemonic = mnemonic;
this.keyType = keyType ?? 'default';
this.passphrase = passphrase;
this.nodeProvider = nodeProvider ?? web3_4.web3.getCurrentNodeProvider();
this.explorerProvider = explorerProvider ?? web3_4.web3.getCurrentExplorerProvider();
}
getNextFromAddressIndex(targetGroup) {
let usedAddressIndex = -1;
for (const account of this._accounts.values()) {
if ((targetGroup === undefined || account.group == 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 = (0, web3_1.publicKeyFromPrivateKey)(priKey, this.keyType);
const address = (0, web3_2.addressFromPublicKey)(publicKey, this.keyType);
const group = (0, web3_3.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 privatekey_wallet_1.PrivateKeyWallet.sign(privateKey, hexString, this.keyType);
}
}
exports.HDWallet = HDWallet;