ADD: Import private keys in hex or base64 formats

This commit is contained in:
Ojok Emmanuel Nsubuga 2026-05-19 15:18:31 +03:00 committed by GitHub
parent 151dbbbc67
commit 1da0414474
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 149 additions and 2 deletions

View File

@ -319,6 +319,7 @@ const startImport = (
}
yield { progress: 'wif' };
const segwitWallet = new SegwitP2SHWallet();
segwitWallet.setSecret(text);
if (segwitWallet.getAddress()) {
@ -382,6 +383,89 @@ const startImport = (
yield { wallet: legacyWallet };
}
yield { progress: 'Private key in hex/base64' };
// check if text is in hex or base64 format
const isHexKey = /^[0-9a-fA-F]{64}$/.test(text);
const isBase64Key = /^[A-Za-z0-9+/=]{43,44}$/.test(text);
let rawKeyBuffer;
let privateKey;
if (isHexKey) {
rawKeyBuffer = Buffer.from(text, 'hex');
} else if (isBase64Key) {
rawKeyBuffer = Buffer.from(text, 'base64');
}
if (rawKeyBuffer && rawKeyBuffer.length === 32) {
let walletFound = false;
// convert the bytes to Wallet import format, 0x80 for mainnet,
// start with uncompressed p2pkh
privateKey = wif.encode(0x80, rawKeyBuffer, false);
yield { progress: 'p2pkh uncompressed' };
const legacyWalletUncompressed = new LegacyWallet('Legacy (P2PKH) - Uncompressed');
legacyWalletUncompressed.setSecret(privateKey);
if (await wasUsed(legacyWalletUncompressed)) {
await fetch(legacyWalletUncompressed, true);
walletFound = true;
yield { wallet: legacyWalletUncompressed };
}
// compressed is true for other wallet types
privateKey = wif.encode(0x80, rawKeyBuffer, true);
yield { progress: 'p2wpkh' };
const segwitBech32Wallet = new SegwitBech32Wallet();
segwitBech32Wallet.setSecret(privateKey);
if (await wasUsed(segwitBech32Wallet)) {
await fetch(segwitBech32Wallet, true);
walletFound = true;
yield { wallet: segwitBech32Wallet };
}
yield { progress: 'p2tr' };
const taprootWallet = new TaprootWallet();
taprootWallet.setSecret(privateKey);
if (await wasUsed(taprootWallet)) {
await fetch(taprootWallet, true);
walletFound = true;
yield { wallet: taprootWallet };
}
yield { progress: 'p2wpkh-p2sh' };
segwitWallet.setSecret(privateKey);
if (await wasUsed(segwitWallet)) {
await fetch(segwitWallet, true);
walletFound = true;
yield { wallet: segwitWallet };
}
yield { progress: 'p2pkh compressed' };
const legacyWalletCompressed = new LegacyWallet('Legacy (P2PKH) - Compressed');
legacyWalletCompressed.setSecret(privateKey);
if (await wasUsed(legacyWalletCompressed)) {
await fetch(legacyWalletCompressed, true);
walletFound = true;
yield { wallet: legacyWalletCompressed };
}
if (!walletFound) {
yield { wallet: segwitBech32Wallet };
yield { wallet: segwitWallet };
yield { wallet: legacyWalletCompressed };
yield { wallet: taprootWallet };
yield { wallet: legacyWalletUncompressed };
}
}
// maybe its a watch-only address?
yield { progress: 'watch only' };
const wo1 = new WatchOnlyWallet();

View File

@ -21,15 +21,21 @@ bitcoin.initEccLib(ecc);
*/
export class LegacyWallet extends AbstractWallet {
static readonly type = 'legacy';
static readonly typeReadable = 'Legacy (P2PKH)';
static readonly defaultTypeReadable = 'Legacy (P2PKH)';
// @ts-ignore: override
public readonly type = LegacyWallet.type;
// @ts-ignore: override
public readonly typeReadable = LegacyWallet.typeReadable;
public readonly typeReadable: string;
_txs_by_external_index: Transaction[] = [];
_txs_by_internal_index: Transaction[] = [];
constructor(typeReadable?: string) {
super();
this.typeReadable = typeReadable ?? LegacyWallet.defaultTypeReadable;
}
/**
* Simple function which says that we havent tried to fetch balance
* for a long time

View File

@ -18,6 +18,7 @@ import { SLIP39SegwitBech32Wallet, SLIP39SegwitP2SHWallet } from '../../class/wa
import { WatchOnlyWallet } from '../../class/wallets/watch-only-wallet';
import startImport from '../../class/wallet-import';
import { TWallet } from '../../class/wallets/types';
import { TaprootWallet } from '../../class/wallets/taproot-wallet';
jest.setTimeout(90 * 1000);
@ -710,4 +711,60 @@ describe('import procedure', () => {
'arkade://abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
);
});
it('can import private key in hex format', async () => {
const store = createStore();
const { promise } = startImport(
'D556170609D43CAAC751EB81D5400425E029A86C2718CD33EC4D7D43CE1BB306',
false,
true,
false,
...store.callbacks,
);
await promise;
assert.strictEqual(store.state.wallets.length > 3, true);
assert.strictEqual(store.state.wallets[0].type, SegwitBech32Wallet.type);
assert.strictEqual(store.state.wallets[1].type, SegwitP2SHWallet.type);
assert.strictEqual(store.state.wallets[2].type, LegacyWallet.type);
assert.strictEqual(store.state.wallets[3].type, TaprootWallet.type);
assert.strictEqual(store.state.wallets[4].type, LegacyWallet.type);
assert.strictEqual(store.state.wallets[0].getSecret(), 'L4NQfHXE9xQ11neMH9MMjRX5fwiT2nsQYz96LxbMJpk3UU8yUyEC');
assert.strictEqual(store.state.wallets[1].getSecret(), 'L4NQfHXE9xQ11neMH9MMjRX5fwiT2nsQYz96LxbMJpk3UU8yUyEC');
assert.strictEqual(store.state.wallets[2].getSecret(), 'L4NQfHXE9xQ11neMH9MMjRX5fwiT2nsQYz96LxbMJpk3UU8yUyEC');
assert.strictEqual(store.state.wallets[3].getSecret(), 'L4NQfHXE9xQ11neMH9MMjRX5fwiT2nsQYz96LxbMJpk3UU8yUyEC');
assert.strictEqual(store.state.wallets[4].getSecret(), '5KSEyFcrCYakxTev5Zhs1YqW9pjvUbQexjQ8ZARYnj5mWqVmQRk');
assert.strictEqual(store.state.wallets[0].getAddress(), 'bc1qxscvu3w04nj9k2eukx30897xldfygzqqkmtdav');
assert.strictEqual(store.state.wallets[1].getAddress(), '38z3svUJKMFj4G3rGrRzcARp2N4vdqV8NK');
assert.strictEqual(store.state.wallets[2].getAddress(), '15kxc4PEBR8yXqpgEkb83YijfjSkd2xrKV');
assert.strictEqual(store.state.wallets[3].getAddress(), 'bc1pxnmhjaug5jqm3xqz5fekme8f8dax869z96ufqyllgz5g3wpw2gpqvqxqqy');
assert.strictEqual(store.state.wallets[4].getAddress(), '1AynZS85C27HqxGEyQphhkoWedYF1bdBnY');
});
it('can import private key in base64 format', async () => {
const store = createStore();
const { promise } = startImport('1VYXBgnUPKrHUeuB1UAEJeApqGwnGM0z7E19Q84bswY=', false, true, false, ...store.callbacks);
await promise;
assert.strictEqual(store.state.wallets.length > 3, true);
assert.strictEqual(store.state.wallets[0].type, SegwitBech32Wallet.type);
assert.strictEqual(store.state.wallets[1].type, SegwitP2SHWallet.type);
assert.strictEqual(store.state.wallets[2].type, LegacyWallet.type);
assert.strictEqual(store.state.wallets[3].type, TaprootWallet.type);
assert.strictEqual(store.state.wallets[4].type, LegacyWallet.type);
assert.strictEqual(store.state.wallets[0].getSecret(), 'L4NQfHXE9xQ11neMH9MMjRX5fwiT2nsQYz96LxbMJpk3UU8yUyEC');
assert.strictEqual(store.state.wallets[1].getSecret(), 'L4NQfHXE9xQ11neMH9MMjRX5fwiT2nsQYz96LxbMJpk3UU8yUyEC');
assert.strictEqual(store.state.wallets[2].getSecret(), 'L4NQfHXE9xQ11neMH9MMjRX5fwiT2nsQYz96LxbMJpk3UU8yUyEC');
assert.strictEqual(store.state.wallets[3].getSecret(), 'L4NQfHXE9xQ11neMH9MMjRX5fwiT2nsQYz96LxbMJpk3UU8yUyEC');
assert.strictEqual(store.state.wallets[4].getSecret(), '5KSEyFcrCYakxTev5Zhs1YqW9pjvUbQexjQ8ZARYnj5mWqVmQRk');
assert.strictEqual(store.state.wallets[0].getAddress(), 'bc1qxscvu3w04nj9k2eukx30897xldfygzqqkmtdav');
assert.strictEqual(store.state.wallets[1].getAddress(), '38z3svUJKMFj4G3rGrRzcARp2N4vdqV8NK');
assert.strictEqual(store.state.wallets[2].getAddress(), '15kxc4PEBR8yXqpgEkb83YijfjSkd2xrKV');
assert.strictEqual(store.state.wallets[3].getAddress(), 'bc1pxnmhjaug5jqm3xqz5fekme8f8dax869z96ufqyllgz5g3wpw2gpqvqxqqy');
assert.strictEqual(store.state.wallets[4].getAddress(), '1AynZS85C27HqxGEyQphhkoWedYF1bdBnY');
});
});