mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-22 15:04:50 +01:00
Merge branch 'master' into renovate/react-navigation-monorepo
This commit is contained in:
commit
b6599e7d47
28 changed files with 566 additions and 369 deletions
|
@ -7,7 +7,6 @@ import WidgetCommunication from './WidgetCommunication';
|
|||
import presentAlert from '../components/Alert';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const ElectrumClient = require('electrum-client');
|
||||
const reverse = require('buffer-reverse');
|
||||
const BigNumber = require('bignumber.js');
|
||||
|
||||
const net = require('net');
|
||||
|
@ -333,7 +332,7 @@ module.exports.getBalanceByAddress = async function (address) {
|
|||
if (!mainClient) throw new Error('Electrum client is not connected');
|
||||
const script = bitcoin.address.toOutputScript(address);
|
||||
const hash = bitcoin.crypto.sha256(script);
|
||||
const reversedHash = Buffer.from(reverse(hash));
|
||||
const reversedHash = Buffer.from(hash).reverse();
|
||||
const balance = await mainClient.blockchainScripthash_getBalance(reversedHash.toString('hex'));
|
||||
balance.addr = address;
|
||||
return balance;
|
||||
|
@ -362,7 +361,7 @@ module.exports.getTransactionsByAddress = async function (address) {
|
|||
if (!mainClient) throw new Error('Electrum client is not connected');
|
||||
const script = bitcoin.address.toOutputScript(address);
|
||||
const hash = bitcoin.crypto.sha256(script);
|
||||
const reversedHash = Buffer.from(reverse(hash));
|
||||
const reversedHash = Buffer.from(hash).reverse();
|
||||
const history = await mainClient.blockchainScripthash_getHistory(reversedHash.toString('hex'));
|
||||
for (const h of history || []) {
|
||||
if (h.tx_hash) txhashHeightCache[h.tx_hash] = h.height; // cache tx height
|
||||
|
@ -380,7 +379,7 @@ module.exports.getMempoolTransactionsByAddress = async function (address) {
|
|||
if (!mainClient) throw new Error('Electrum client is not connected');
|
||||
const script = bitcoin.address.toOutputScript(address);
|
||||
const hash = bitcoin.crypto.sha256(script);
|
||||
const reversedHash = Buffer.from(reverse(hash));
|
||||
const reversedHash = Buffer.from(hash).reverse();
|
||||
return mainClient.blockchainScripthash_getMempool(reversedHash.toString('hex'));
|
||||
};
|
||||
|
||||
|
@ -477,7 +476,7 @@ module.exports.multiGetBalanceByAddress = async function (addresses, batchsize)
|
|||
for (const addr of chunk) {
|
||||
const script = bitcoin.address.toOutputScript(addr);
|
||||
const hash = bitcoin.crypto.sha256(script);
|
||||
let reversedHash = Buffer.from(reverse(hash));
|
||||
let reversedHash = Buffer.from(hash).reverse();
|
||||
reversedHash = reversedHash.toString('hex');
|
||||
scripthashes.push(reversedHash);
|
||||
scripthash2addr[reversedHash] = addr;
|
||||
|
@ -523,7 +522,7 @@ module.exports.multiGetUtxoByAddress = async function (addresses, batchsize) {
|
|||
for (const addr of chunk) {
|
||||
const script = bitcoin.address.toOutputScript(addr);
|
||||
const hash = bitcoin.crypto.sha256(script);
|
||||
let reversedHash = Buffer.from(reverse(hash));
|
||||
let reversedHash = Buffer.from(hash).reverse();
|
||||
reversedHash = reversedHash.toString('hex');
|
||||
scripthashes.push(reversedHash);
|
||||
scripthash2addr[reversedHash] = addr;
|
||||
|
@ -566,7 +565,7 @@ module.exports.multiGetHistoryByAddress = async function (addresses, batchsize)
|
|||
for (const addr of chunk) {
|
||||
const script = bitcoin.address.toOutputScript(addr);
|
||||
const hash = bitcoin.crypto.sha256(script);
|
||||
let reversedHash = Buffer.from(reverse(hash));
|
||||
let reversedHash = Buffer.from(hash).reverse();
|
||||
reversedHash = reversedHash.toString('hex');
|
||||
scripthashes.push(reversedHash);
|
||||
scripthash2addr[reversedHash] = addr;
|
||||
|
@ -1039,7 +1038,7 @@ function txhexToElectrumTransaction(txhex) {
|
|||
if (inn.witness[1]) txinwitness.push(inn.witness[1].toString('hex'));
|
||||
|
||||
ret.vin.push({
|
||||
txid: reverse(inn.hash).toString('hex'),
|
||||
txid: Buffer.from(inn.hash).reverse().toString('hex'),
|
||||
vout: inn.index,
|
||||
scriptSig: { hex: inn.script.toString('hex'), asm: '' },
|
||||
txinwitness,
|
||||
|
|
|
@ -2,7 +2,6 @@ import { HDSegwitBech32Wallet } from './wallets/hd-segwit-bech32-wallet';
|
|||
import { SegwitBech32Wallet } from './wallets/segwit-bech32-wallet';
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const BlueElectrum = require('../blue_modules/BlueElectrum');
|
||||
const reverse = require('buffer-reverse');
|
||||
const BigNumber = require('bignumber.js');
|
||||
|
||||
/**
|
||||
|
@ -150,7 +149,7 @@ export class HDSegwitBech32Transaction {
|
|||
|
||||
const prevInputs = [];
|
||||
for (const inp of this._txDecoded.ins) {
|
||||
let reversedHash = Buffer.from(reverse(inp.hash));
|
||||
let reversedHash = Buffer.from(inp.hash).reverse();
|
||||
reversedHash = reversedHash.toString('hex');
|
||||
prevInputs.push(reversedHash);
|
||||
}
|
||||
|
@ -161,7 +160,7 @@ export class HDSegwitBech32Transaction {
|
|||
let wentIn = 0;
|
||||
const utxos = [];
|
||||
for (const inp of this._txDecoded.ins) {
|
||||
let reversedHash = Buffer.from(reverse(inp.hash));
|
||||
let reversedHash = Buffer.from(inp.hash).reverse();
|
||||
reversedHash = reversedHash.toString('hex');
|
||||
if (prevTransactions[reversedHash] && prevTransactions[reversedHash].vout && prevTransactions[reversedHash].vout[inp.index]) {
|
||||
let value = prevTransactions[reversedHash].vout[inp.index].value;
|
||||
|
@ -228,7 +227,7 @@ export class HDSegwitBech32Transaction {
|
|||
|
||||
const spentUtxos = this._wallet.getDerivedUtxoFromOurTransaction(true);
|
||||
for (const inp of this._txDecoded.ins) {
|
||||
const txidInUtxo = reverse(inp.hash).toString('hex');
|
||||
const txidInUtxo = Buffer.from(inp.hash).reverse().toString('hex');
|
||||
|
||||
let found = false;
|
||||
for (const spentU of spentUtxos) {
|
||||
|
|
|
@ -1,27 +1,24 @@
|
|||
/* eslint react/prop-types: "off", @typescript-eslint/ban-ts-comment: "off", camelcase: "off" */
|
||||
import * as bip39 from 'bip39';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import b58 from 'bs58check';
|
||||
import BIP32Factory, { BIP32Interface } from 'bip32';
|
||||
|
||||
import { ECPairInterface } from 'ecpair/src/ecpair';
|
||||
import { Psbt, Transaction as BTransaction } from 'bitcoinjs-lib';
|
||||
import { CoinSelectReturnInput, CoinSelectTarget } from 'coinselect';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
|
||||
import BIP47Factory, { BIP47Interface } from '@spsina/bip47';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import BIP32Factory, { BIP32Interface } from 'bip32';
|
||||
import * as bip39 from 'bip39';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import { Transaction as BTransaction, Psbt } from 'bitcoinjs-lib';
|
||||
import b58 from 'bs58check';
|
||||
import { CoinSelectReturnInput, CoinSelectTarget } from 'coinselect';
|
||||
import { ECPairFactory } from 'ecpair';
|
||||
import { ECPairInterface } from 'ecpair/src/ecpair';
|
||||
|
||||
import type BlueElectrumNs from '../../blue_modules/BlueElectrum';
|
||||
import { ElectrumHistory } from '../../blue_modules/BlueElectrum';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { randomBytes } from '../rng';
|
||||
import { AbstractHDWallet } from './abstract-hd-wallet';
|
||||
import { CreateTransactionResult, CreateTransactionUtxo, Transaction, Utxo } from './types';
|
||||
import { ElectrumHistory } from '../../blue_modules/BlueElectrum';
|
||||
import type BlueElectrumNs from '../../blue_modules/BlueElectrum';
|
||||
|
||||
const ECPair = ECPairFactory(ecc);
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const BlueElectrum: typeof BlueElectrumNs = require('../../blue_modules/BlueElectrum');
|
||||
const reverse = require('buffer-reverse');
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
const bip47 = BIP47Factory(ecc);
|
||||
|
||||
|
@ -858,7 +855,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
this._lastBalanceFetch = +new Date();
|
||||
}
|
||||
|
||||
async fetchUtxo() {
|
||||
async fetchUtxo(): Promise<void> {
|
||||
// fetching utxo of addresses that only have some balance
|
||||
let addressess = [];
|
||||
|
||||
|
@ -914,17 +911,15 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
this._utxo = this._utxo.concat(arr);
|
||||
}
|
||||
|
||||
// backward compatibility TODO: remove when we make sure `.utxo` is not used
|
||||
this.utxo = this._utxo;
|
||||
// this belongs in `.getUtxo()`
|
||||
for (const u of this.utxo) {
|
||||
for (const u of this._utxo) {
|
||||
u.txid = u.txId;
|
||||
u.amount = u.value;
|
||||
u.wif = this._getWifForAddress(u.address);
|
||||
if (!u.confirmations && u.height) u.confirmations = BlueElectrum.estimateCurrentBlockheight() - u.height;
|
||||
}
|
||||
|
||||
this.utxo = this.utxo.sort((a, b) => Number(a.amount) - Number(b.amount));
|
||||
this._utxo = this._utxo.sort((a, b) => Number(a.amount) - Number(b.amount));
|
||||
// more consistent, so txhex in unit tests wont change
|
||||
}
|
||||
|
||||
|
@ -1169,7 +1164,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
let masterFingerprintHex = Number(masterFingerprint).toString(16);
|
||||
if (masterFingerprintHex.length < 8) masterFingerprintHex = '0' + masterFingerprintHex; // conversion without explicit zero might result in lost byte
|
||||
const hexBuffer = Buffer.from(masterFingerprintHex, 'hex');
|
||||
masterFingerprintBuffer = Buffer.from(reverse(hexBuffer));
|
||||
masterFingerprintBuffer = Buffer.from(hexBuffer).reverse();
|
||||
} else {
|
||||
masterFingerprintBuffer = Buffer.from([0x00, 0x00, 0x00, 0x00]);
|
||||
}
|
||||
|
@ -1195,7 +1190,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
let masterFingerprintHex = Number(masterFingerprint).toString(16);
|
||||
if (masterFingerprintHex.length < 8) masterFingerprintHex = '0' + masterFingerprintHex; // conversion without explicit zero might result in lost byte
|
||||
const hexBuffer = Buffer.from(masterFingerprintHex, 'hex');
|
||||
masterFingerprintBuffer = Buffer.from(reverse(hexBuffer));
|
||||
masterFingerprintBuffer = Buffer.from(hexBuffer).reverse();
|
||||
} else {
|
||||
masterFingerprintBuffer = Buffer.from([0x00, 0x00, 0x00, 0x00]);
|
||||
}
|
||||
|
@ -1243,6 +1238,9 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
throw new Error('Internal error: pubkey or path are invalid');
|
||||
}
|
||||
const p2wpkh = bitcoin.payments.p2wpkh({ pubkey });
|
||||
if (!p2wpkh.output) {
|
||||
throw new Error('Internal error: could not create p2wpkh output during _addPsbtInput');
|
||||
}
|
||||
|
||||
psbt.addInput({
|
||||
// @ts-ignore
|
||||
|
@ -1293,15 +1291,27 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
* Creates Segwit Bech32 Bitcoin address
|
||||
*/
|
||||
_nodeToBech32SegwitAddress(hdNode: BIP32Interface): string {
|
||||
return bitcoin.payments.p2wpkh({
|
||||
const { address } = bitcoin.payments.p2wpkh({
|
||||
pubkey: hdNode.publicKey,
|
||||
}).address;
|
||||
});
|
||||
|
||||
if (!address) {
|
||||
throw new Error('Could not create address in _nodeToBech32SegwitAddress');
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
_nodeToLegacyAddress(hdNode: BIP32Interface): string {
|
||||
return bitcoin.payments.p2pkh({
|
||||
const { address } = bitcoin.payments.p2pkh({
|
||||
pubkey: hdNode.publicKey,
|
||||
}).address;
|
||||
});
|
||||
|
||||
if (!address) {
|
||||
throw new Error('Could not create address in _nodeToLegacyAddress');
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1311,6 +1321,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
const { address } = bitcoin.payments.p2sh({
|
||||
redeem: bitcoin.payments.p2wpkh({ pubkey: hdNode.publicKey }),
|
||||
});
|
||||
|
||||
if (!address) {
|
||||
throw new Error('Could not create address in _nodeToP2shSegwitAddress');
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
|
@ -1441,7 +1456,7 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
|
|||
* @param mnemonic {string} Mnemonic phrase (12 or 24 words)
|
||||
* @returns {string} Hex fingerprint
|
||||
*/
|
||||
static mnemonicToFingerprint(mnemonic: string, passphrase: string) {
|
||||
static mnemonicToFingerprint(mnemonic: string, passphrase?: string) {
|
||||
const seed = bip39.mnemonicToSeedSync(mnemonic, passphrase);
|
||||
return AbstractHDElectrumWallet.seedToFingerprint(seed);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ export class AbstractWallet {
|
|||
balance: number;
|
||||
unconfirmed_balance: number;
|
||||
_address: string | false;
|
||||
utxo: Utxo[];
|
||||
_utxo: Utxo[];
|
||||
_lastTxFetch: number;
|
||||
_lastBalanceFetch: number;
|
||||
preferredBalanceUnit: BitcoinUnit;
|
||||
|
@ -63,7 +63,7 @@ export class AbstractWallet {
|
|||
this.balance = 0;
|
||||
this.unconfirmed_balance = 0;
|
||||
this._address = false; // cache
|
||||
this.utxo = [];
|
||||
this._utxo = [];
|
||||
this._lastTxFetch = 0;
|
||||
this._lastBalanceFetch = 0;
|
||||
this.preferredBalanceUnit = BitcoinUnit.BTC;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||
import b58 from 'bs58check';
|
||||
import { CipherSeed } from 'aezeed';
|
||||
import BIP32Factory from 'bip32';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import b58 from 'bs58check';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const { CipherSeed } = require('aezeed');
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,9 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet {
|
|||
static segwitType = 'p2wpkh';
|
||||
static derivationPath = "m/84'/0'/0'";
|
||||
|
||||
setSecret(newSecret) {
|
||||
private _entropyHex?: string;
|
||||
|
||||
setSecret(newSecret: string): this {
|
||||
this.secret = newSecret.trim();
|
||||
this.secret = this.secret.replace(/[^a-zA-Z0-9]/g, ' ').replace(/\s+/g, ' ');
|
||||
return this;
|
||||
|
@ -55,7 +57,7 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet {
|
|||
return this._xpub;
|
||||
}
|
||||
|
||||
validateMnemonic() {
|
||||
validateMnemonic(): boolean {
|
||||
throw new Error('Use validateMnemonicAsync()');
|
||||
}
|
||||
|
||||
|
@ -75,7 +77,7 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet {
|
|||
try {
|
||||
const cipherSeed1 = await CipherSeed.fromMnemonic(this.secret, passphrase);
|
||||
this._entropyHex = cipherSeed1.entropy.toString('hex'); // save cache
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
return error.message === 'Invalid Password';
|
||||
}
|
||||
return false;
|
||||
|
@ -97,7 +99,7 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet {
|
|||
return node.derive(1);
|
||||
}
|
||||
|
||||
_getInternalAddressByIndex(index) {
|
||||
_getInternalAddressByIndex(index: number): string {
|
||||
index = index * 1; // cast to int
|
||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||
|
||||
|
@ -106,11 +108,14 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet {
|
|||
const address = bitcoin.payments.p2wpkh({
|
||||
pubkey: this._node1.derive(index).publicKey,
|
||||
}).address;
|
||||
if (!address) {
|
||||
throw new Error('Internal error: no address in _getInternalAddressByIndex');
|
||||
}
|
||||
|
||||
return (this.internal_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
_getExternalAddressByIndex(index) {
|
||||
_getExternalAddressByIndex(index: number): string {
|
||||
index = index * 1; // cast to int
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
|
||||
|
@ -119,11 +124,14 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet {
|
|||
const address = bitcoin.payments.p2wpkh({
|
||||
pubkey: this._node0.derive(index).publicKey,
|
||||
}).address;
|
||||
if (!address) {
|
||||
throw new Error('Internal error: no address in _getExternalAddressByIndex');
|
||||
}
|
||||
|
||||
return (this.external_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
_getWIFByIndex(internal, index) {
|
||||
_getWIFByIndex(internal: boolean, index: number): string | false {
|
||||
if (!this.secret) return false;
|
||||
const root = bip32.fromSeed(this._getEntropyCached());
|
||||
const path = `m/84'/0'/0'/${internal ? 1 : 0}/${index}`;
|
||||
|
@ -132,7 +140,7 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet {
|
|||
return child.toWIF();
|
||||
}
|
||||
|
||||
_getNodePubkeyByIndex(node, index) {
|
||||
_getNodePubkeyByIndex(node: number, index: number) {
|
||||
index = index * 1; // cast to int
|
||||
|
||||
if (node === 0 && !this._node0) {
|
||||
|
@ -143,13 +151,15 @@ export class HDAezeedWallet extends AbstractHDElectrumWallet {
|
|||
this._node1 = this._getNode1();
|
||||
}
|
||||
|
||||
if (node === 0) {
|
||||
if (node === 0 && this._node0) {
|
||||
return this._node0.derive(index).publicKey;
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
if (node === 1 && this._node1) {
|
||||
return this._node1.derive(index).publicKey;
|
||||
}
|
||||
|
||||
throw new Error('Internal error: this._node0 or this._node1 is undefined');
|
||||
}
|
||||
|
||||
getIdentityPubkey() {
|
|
@ -1,13 +1,17 @@
|
|||
import { HDLegacyP2PKHWallet } from './hd-legacy-p2pkh-wallet';
|
||||
import BIP32Factory from 'bip32';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import * as mn from 'electrum-mnemonic';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { HDLegacyP2PKHWallet } from './hd-legacy-p2pkh-wallet';
|
||||
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const mn = require('electrum-mnemonic');
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
|
||||
const PREFIX = mn.PREFIXES.standard;
|
||||
|
||||
type SeedOpts = {
|
||||
prefix?: string;
|
||||
passphrase?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* ElectrumSeed means that instead of BIP39 seed format it works with the format invented by Electrum wallet. Otherwise
|
||||
* its a regular HD wallet that has all the properties of parent class.
|
||||
|
@ -35,14 +39,14 @@ export class HDLegacyElectrumSeedP2PKHWallet extends HDLegacyP2PKHWallet {
|
|||
if (this._xpub) {
|
||||
return this._xpub; // cache hit
|
||||
}
|
||||
const args = { prefix: PREFIX };
|
||||
const args: SeedOpts = { prefix: PREFIX };
|
||||
if (this.passphrase) args.passphrase = this.passphrase;
|
||||
const root = bip32.fromSeed(mn.mnemonicToSeedSync(this.secret, args));
|
||||
this._xpub = root.neutered().toBase58();
|
||||
return this._xpub;
|
||||
}
|
||||
|
||||
_getInternalAddressByIndex(index) {
|
||||
_getInternalAddressByIndex(index: number) {
|
||||
index = index * 1; // cast to int
|
||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||
|
||||
|
@ -50,11 +54,14 @@ export class HDLegacyElectrumSeedP2PKHWallet extends HDLegacyP2PKHWallet {
|
|||
const address = bitcoin.payments.p2pkh({
|
||||
pubkey: node.derive(1).derive(index).publicKey,
|
||||
}).address;
|
||||
if (!address) {
|
||||
throw new Error('Internal error: no address in _getInternalAddressByIndex');
|
||||
}
|
||||
|
||||
return (this.internal_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
_getExternalAddressByIndex(index) {
|
||||
_getExternalAddressByIndex(index: number) {
|
||||
index = index * 1; // cast to int
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
|
||||
|
@ -62,13 +69,16 @@ export class HDLegacyElectrumSeedP2PKHWallet extends HDLegacyP2PKHWallet {
|
|||
const address = bitcoin.payments.p2pkh({
|
||||
pubkey: node.derive(0).derive(index).publicKey,
|
||||
}).address;
|
||||
if (!address) {
|
||||
throw new Error('Internal error: no address in _getExternalAddressByIndex');
|
||||
}
|
||||
|
||||
return (this.external_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
_getWIFByIndex(internal, index) {
|
||||
_getWIFByIndex(internal: boolean, index: number): string | false {
|
||||
if (!this.secret) return false;
|
||||
const args = { prefix: PREFIX };
|
||||
const args: SeedOpts = { prefix: PREFIX };
|
||||
if (this.passphrase) args.passphrase = this.passphrase;
|
||||
const root = bip32.fromSeed(mn.mnemonicToSeedSync(this.secret, args));
|
||||
const path = `m/${internal ? 1 : 0}/${index}`;
|
||||
|
@ -77,7 +87,7 @@ export class HDLegacyElectrumSeedP2PKHWallet extends HDLegacyP2PKHWallet {
|
|||
return child.toWIF();
|
||||
}
|
||||
|
||||
_getNodePubkeyByIndex(node, index) {
|
||||
_getNodePubkeyByIndex(node: number, index: number) {
|
||||
index = index * 1; // cast to int
|
||||
|
||||
if (node === 0 && !this._node0) {
|
||||
|
@ -92,12 +102,14 @@ export class HDLegacyElectrumSeedP2PKHWallet extends HDLegacyP2PKHWallet {
|
|||
this._node1 = hdNode.derive(node);
|
||||
}
|
||||
|
||||
if (node === 0) {
|
||||
if (node === 0 && this._node0) {
|
||||
return this._node0.derive(index).publicKey;
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
if (node === 1 && this._node1) {
|
||||
return this._node1.derive(index).publicKey;
|
||||
}
|
||||
|
||||
throw new Error('Internal error: this._node0 or this._node1 is undefined');
|
||||
}
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||
import BIP32Factory from 'bip32';
|
||||
import BIP32Factory, { BIP32Interface } from 'bip32';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import { CoinSelectReturnInput } from 'coinselect';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
|
||||
|
@ -46,17 +48,20 @@ export class HDLegacyP2PKHWallet extends AbstractHDElectrumWallet {
|
|||
const root = bip32.fromSeed(seed);
|
||||
|
||||
const path = this.getDerivationPath();
|
||||
if (!path) {
|
||||
throw new Error('Internal error: no path');
|
||||
}
|
||||
const child = root.derivePath(path).neutered();
|
||||
this._xpub = child.toBase58();
|
||||
|
||||
return this._xpub;
|
||||
}
|
||||
|
||||
_hdNodeToAddress(hdNode) {
|
||||
_hdNodeToAddress(hdNode: BIP32Interface): string {
|
||||
return this._nodeToLegacyAddress(hdNode);
|
||||
}
|
||||
|
||||
async fetchUtxo() {
|
||||
async fetchUtxo(): Promise<void> {
|
||||
await super.fetchUtxo();
|
||||
// now we need to fetch txhash for each input as required by PSBT
|
||||
const txhexes = await BlueElectrum.multiGetTransactionByTxid(
|
||||
|
@ -65,18 +70,20 @@ export class HDLegacyP2PKHWallet extends AbstractHDElectrumWallet {
|
|||
false,
|
||||
);
|
||||
|
||||
const newUtxos = [];
|
||||
for (const u of this.getUtxo()) {
|
||||
if (txhexes[u.txid]) u.txhex = txhexes[u.txid];
|
||||
newUtxos.push(u);
|
||||
}
|
||||
|
||||
return newUtxos;
|
||||
}
|
||||
|
||||
_addPsbtInput(psbt, input, sequence, masterFingerprintBuffer) {
|
||||
_addPsbtInput(psbt: Psbt, input: CoinSelectReturnInput, sequence: number, masterFingerprintBuffer: Buffer) {
|
||||
if (!input.address) {
|
||||
throw new Error('Internal error: no address on Utxo during _addPsbtInput()');
|
||||
}
|
||||
const pubkey = this._getPubkeyByAddress(input.address);
|
||||
const path = this._getDerivationPathByAddress(input.address, 44);
|
||||
const path = this._getDerivationPathByAddress(input.address);
|
||||
if (!pubkey || !path) {
|
||||
throw new Error('Internal error: pubkey or path are invalid');
|
||||
}
|
||||
|
||||
if (!input.txhex) throw new Error('UTXO is missing txhex of the input, which is required by PSBT for non-segwit input');
|
||||
|
|
@ -1,14 +1,18 @@
|
|||
import b58 from 'bs58check';
|
||||
import { HDSegwitBech32Wallet } from './hd-segwit-bech32-wallet';
|
||||
import BIP32Factory from 'bip32';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import b58 from 'bs58check';
|
||||
import * as mn from 'electrum-mnemonic';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { HDSegwitBech32Wallet } from './hd-segwit-bech32-wallet';
|
||||
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const mn = require('electrum-mnemonic');
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
|
||||
const PREFIX = mn.PREFIXES.segwit;
|
||||
|
||||
type SeedOpts = {
|
||||
prefix?: string;
|
||||
passphrase?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* ElectrumSeed means that instead of BIP39 seed format it works with the format invented by Electrum wallet. Otherwise
|
||||
* its a regular HD wallet that has all the properties of parent class.
|
||||
|
@ -36,7 +40,7 @@ export class HDSegwitElectrumSeedP2WPKHWallet extends HDSegwitBech32Wallet {
|
|||
if (this._xpub) {
|
||||
return this._xpub; // cache hit
|
||||
}
|
||||
const args = { prefix: PREFIX };
|
||||
const args: SeedOpts = { prefix: PREFIX };
|
||||
if (this.passphrase) args.passphrase = this.passphrase;
|
||||
const root = bip32.fromSeed(mn.mnemonicToSeedSync(this.secret, args));
|
||||
const xpub = root.derivePath("m/0'").neutered().toBase58();
|
||||
|
@ -50,7 +54,7 @@ export class HDSegwitElectrumSeedP2WPKHWallet extends HDSegwitBech32Wallet {
|
|||
return this._xpub;
|
||||
}
|
||||
|
||||
_getInternalAddressByIndex(index) {
|
||||
_getInternalAddressByIndex(index: number) {
|
||||
index = index * 1; // cast to int
|
||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||
|
||||
|
@ -59,11 +63,14 @@ export class HDSegwitElectrumSeedP2WPKHWallet extends HDSegwitBech32Wallet {
|
|||
const address = bitcoin.payments.p2wpkh({
|
||||
pubkey: node.derive(1).derive(index).publicKey,
|
||||
}).address;
|
||||
if (!address) {
|
||||
throw new Error('Internal error: no address in _getInternalAddressByIndex');
|
||||
}
|
||||
|
||||
return (this.internal_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
_getExternalAddressByIndex(index) {
|
||||
_getExternalAddressByIndex(index: number) {
|
||||
index = index * 1; // cast to int
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
|
||||
|
@ -72,13 +79,16 @@ export class HDSegwitElectrumSeedP2WPKHWallet extends HDSegwitBech32Wallet {
|
|||
const address = bitcoin.payments.p2wpkh({
|
||||
pubkey: node.derive(0).derive(index).publicKey,
|
||||
}).address;
|
||||
if (!address) {
|
||||
throw new Error('Internal error: no address in _getExternalAddressByIndex');
|
||||
}
|
||||
|
||||
return (this.external_addresses_cache[index] = address);
|
||||
}
|
||||
|
||||
_getWIFByIndex(internal, index) {
|
||||
_getWIFByIndex(internal: boolean, index: number): string | false {
|
||||
if (!this.secret) return false;
|
||||
const args = { prefix: PREFIX };
|
||||
const args: SeedOpts = { prefix: PREFIX };
|
||||
if (this.passphrase) args.passphrase = this.passphrase;
|
||||
const root = bip32.fromSeed(mn.mnemonicToSeedSync(this.secret, args));
|
||||
const path = `m/0'/${internal ? 1 : 0}/${index}`;
|
||||
|
@ -87,7 +97,7 @@ export class HDSegwitElectrumSeedP2WPKHWallet extends HDSegwitBech32Wallet {
|
|||
return child.toWIF();
|
||||
}
|
||||
|
||||
_getNodePubkeyByIndex(node, index) {
|
||||
_getNodePubkeyByIndex(node: number, index: number) {
|
||||
index = index * 1; // cast to int
|
||||
|
||||
if (node === 0 && !this._node0) {
|
||||
|
@ -102,13 +112,15 @@ export class HDSegwitElectrumSeedP2WPKHWallet extends HDSegwitBech32Wallet {
|
|||
this._node1 = hdNode.derive(node);
|
||||
}
|
||||
|
||||
if (node === 0) {
|
||||
if (node === 0 && this._node0) {
|
||||
return this._node0.derive(index).publicKey;
|
||||
}
|
||||
|
||||
if (node === 1) {
|
||||
if (node === 1 && this._node1) {
|
||||
return this._node1.derive(index).publicKey;
|
||||
}
|
||||
|
||||
throw new Error('Internal error: this._node0 or this._node1 is undefined');
|
||||
}
|
||||
|
||||
isSegwit() {
|
|
@ -1,9 +1,12 @@
|
|||
import BIP32Factory, { BIP32Interface } from 'bip32';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
import b58 from 'bs58check';
|
||||
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||
import BIP32Factory from 'bip32';
|
||||
import { CoinSelectReturnInput } from 'coinselect';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
|
||||
/**
|
||||
* HD Wallet (BIP39).
|
||||
|
@ -40,7 +43,7 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
|||
return true;
|
||||
}
|
||||
|
||||
_hdNodeToAddress(hdNode) {
|
||||
_hdNodeToAddress(hdNode: BIP32Interface): string {
|
||||
return this._nodeToP2shSegwitAddress(hdNode);
|
||||
}
|
||||
|
||||
|
@ -59,6 +62,9 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
|||
const root = bip32.fromSeed(seed);
|
||||
|
||||
const path = this.getDerivationPath();
|
||||
if (!path) {
|
||||
throw new Error('Internal error: no path');
|
||||
}
|
||||
const child = root.derivePath(path).neutered();
|
||||
const xpub = child.toBase58();
|
||||
|
||||
|
@ -71,11 +77,20 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
|||
return this._xpub;
|
||||
}
|
||||
|
||||
_addPsbtInput(psbt, input, sequence, masterFingerprintBuffer) {
|
||||
_addPsbtInput(psbt: Psbt, input: CoinSelectReturnInput, sequence: number, masterFingerprintBuffer: Buffer) {
|
||||
if (!input.address) {
|
||||
throw new Error('Internal error: no address on Utxo during _addPsbtInput()');
|
||||
}
|
||||
const pubkey = this._getPubkeyByAddress(input.address);
|
||||
const path = this._getDerivationPathByAddress(input.address);
|
||||
if (!pubkey || !path) {
|
||||
throw new Error('Internal error: pubkey or path are invalid');
|
||||
}
|
||||
const p2wpkh = bitcoin.payments.p2wpkh({ pubkey });
|
||||
const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh });
|
||||
if (!p2sh.output) {
|
||||
throw new Error('Internal error: no p2sh.output during _addPsbtInput()');
|
||||
}
|
||||
|
||||
psbt.addInput({
|
||||
hash: input.txid,
|
||||
|
@ -90,7 +105,7 @@ export class HDSegwitP2SHWallet extends AbstractHDElectrumWallet {
|
|||
],
|
||||
witnessUtxo: {
|
||||
script: p2sh.output,
|
||||
value: input.amount || input.value,
|
||||
value: input.value,
|
||||
},
|
||||
redeemScript: p2wpkh.output,
|
||||
});
|
|
@ -73,10 +73,6 @@ export class LegacyWallet extends AbstractWallet {
|
|||
} while (true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
getAddress(): string | false {
|
||||
if (this._address) return this._address;
|
||||
let address;
|
||||
|
@ -131,26 +127,26 @@ export class LegacyWallet extends AbstractWallet {
|
|||
const address = this.getAddress();
|
||||
if (!address) throw new Error('LegacyWallet: Invalid address');
|
||||
const utxos = await BlueElectrum.multiGetUtxoByAddress([address]);
|
||||
this.utxo = [];
|
||||
this._utxo = [];
|
||||
for (const arr of Object.values(utxos)) {
|
||||
this.utxo = this.utxo.concat(arr);
|
||||
this._utxo = this._utxo.concat(arr);
|
||||
}
|
||||
|
||||
// now we need to fetch txhash for each input as required by PSBT
|
||||
if (LegacyWallet.type !== this.type) return; // but only for LEGACY single-address wallets
|
||||
const txhexes = await BlueElectrum.multiGetTransactionByTxid(
|
||||
this.utxo.map(u => u.txId),
|
||||
this._utxo.map(u => u.txId),
|
||||
50,
|
||||
false,
|
||||
);
|
||||
|
||||
const newUtxos = [];
|
||||
for (const u of this.utxo) {
|
||||
for (const u of this._utxo) {
|
||||
if (txhexes[u.txId]) u.txhex = txhexes[u.txId];
|
||||
newUtxos.push(u);
|
||||
}
|
||||
|
||||
this.utxo = newUtxos;
|
||||
this._utxo = newUtxos;
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
|
@ -173,7 +169,7 @@ export class LegacyWallet extends AbstractWallet {
|
|||
*/
|
||||
getUtxo(respectFrozen = false): Utxo[] {
|
||||
let ret: Utxo[] = [];
|
||||
for (const u of this.utxo) {
|
||||
for (const u of this._utxo) {
|
||||
if (u.txId) u.txid = u.txId;
|
||||
if (!u.confirmations && u.height) u.confirmations = BlueElectrum.estimateCurrentBlockheight() - u.height;
|
||||
ret.push(u);
|
||||
|
@ -430,11 +426,12 @@ export class LegacyWallet extends AbstractWallet {
|
|||
const values: Record<number, number> = {};
|
||||
let keyPair: Signer | null = null;
|
||||
|
||||
if (!skipSigning) {
|
||||
// skiping signing related stuff
|
||||
keyPair = ECPair.fromWIF(this.secret); // secret is WIF
|
||||
}
|
||||
|
||||
inputs.forEach(input => {
|
||||
if (!skipSigning) {
|
||||
// skiping signing related stuff
|
||||
keyPair = ECPair.fromWIF(this.secret); // secret is WIF
|
||||
}
|
||||
values[c] = input.value;
|
||||
c++;
|
||||
|
||||
|
|
|
@ -564,6 +564,7 @@ export class LightningLdkWallet extends LightningCustodianWallet {
|
|||
await wallet.fetchUtxo();
|
||||
console.log(wallet.getBalance(), wallet.getUtxo());
|
||||
console.log('creating transation...');
|
||||
// @ts-ignore wtf wallet.getUtxo() and first arg of createTransaction are not compatible
|
||||
const { tx } = wallet.createTransaction(wallet.getUtxo(), [{ address }], 2, address, 0, false, 0);
|
||||
if (!tx) throw new Error('claimCoins: could not create transaction');
|
||||
console.log('broadcasting...');
|
||||
|
|
|
@ -1,24 +1,53 @@
|
|||
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||
import BIP32Factory, { BIP32Interface } from 'bip32';
|
||||
import * as bip39 from 'bip39';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import { Psbt, Transaction } from 'bitcoinjs-lib';
|
||||
import b58 from 'bs58check';
|
||||
import { decodeUR } from '../../blue_modules/ur';
|
||||
import createHash from 'create-hash';
|
||||
import { ECPairFactory } from 'ecpair';
|
||||
import BIP32Factory from 'bip32';
|
||||
import * as mn from 'electrum-mnemonic';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { decodeUR } from '../../blue_modules/ur';
|
||||
import { AbstractHDElectrumWallet } from './abstract-hd-electrum-wallet';
|
||||
import { CoinSelectReturnInput, CoinSelectTarget } from 'coinselect';
|
||||
import { CreateTransactionResult, CreateTransactionUtxo } from './types';
|
||||
|
||||
const ECPair = ECPairFactory(ecc);
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
const bip32 = BIP32Factory(ecc);
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const createHash = require('create-hash');
|
||||
const reverse = require('buffer-reverse');
|
||||
const mn = require('electrum-mnemonic');
|
||||
|
||||
const electrumSegwit = passphrase => ({
|
||||
type SeedOpts = {
|
||||
prefix: string;
|
||||
passphrase?: string;
|
||||
};
|
||||
|
||||
type TBip32Derivation = {
|
||||
masterFingerprint: Buffer;
|
||||
path: string;
|
||||
pubkey: Buffer;
|
||||
}[];
|
||||
|
||||
type TOutputData =
|
||||
| {
|
||||
bip32Derivation: TBip32Derivation;
|
||||
redeemScript: Buffer;
|
||||
}
|
||||
| {
|
||||
bip32Derivation: TBip32Derivation;
|
||||
witnessScript: Buffer;
|
||||
}
|
||||
| {
|
||||
bip32Derivation: TBip32Derivation;
|
||||
redeemScript: Buffer;
|
||||
witnessScript: Buffer;
|
||||
};
|
||||
|
||||
const electrumSegwit = (passphrase?: string): SeedOpts => ({
|
||||
prefix: mn.PREFIXES.segwit,
|
||||
...(passphrase ? { passphrase } : {}),
|
||||
});
|
||||
|
||||
const electrumStandart = passphrase => ({
|
||||
const electrumStandart = (passphrase?: string): SeedOpts => ({
|
||||
prefix: mn.PREFIXES.standard,
|
||||
...(passphrase ? { passphrase } : {}),
|
||||
});
|
||||
|
@ -38,19 +67,17 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
static PATH_WRAPPED_SEGWIT = "m/48'/0'/0'/1'";
|
||||
static PATH_LEGACY = "m/45'";
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this._m = 0; // minimum required signatures so spend (m out of n)
|
||||
this._cosigners = []; // array of xpubs or mnemonic seeds
|
||||
this._cosignersFingerprints = []; // array of according fingerprints (if any provided)
|
||||
this._cosignersCustomPaths = []; // array of according paths (if any provided)
|
||||
this._cosignersPassphrases = []; // array of according passphrases (if any provided)
|
||||
this._derivationPath = '';
|
||||
this._isNativeSegwit = false;
|
||||
this._isWrappedSegwit = false;
|
||||
this._isLegacy = false;
|
||||
this.gap_limit = 10;
|
||||
}
|
||||
private _m: number = 0; // minimum required signatures so spend (m out of n)
|
||||
private _cosigners: string[] = []; // array of xpubs or mnemonic seeds
|
||||
private _cosignersFingerprints: string[] = []; // array of according fingerprints (if any provided)
|
||||
private _cosignersCustomPaths: string[] = []; // array of according paths (if any provided)
|
||||
private _cosignersPassphrases: (string | undefined)[] = []; // array of according passphrases (if any provided)
|
||||
private _isNativeSegwit: boolean = false;
|
||||
private _isWrappedSegwit: boolean = false;
|
||||
private _isLegacy: boolean = false;
|
||||
private _nodes: BIP32Interface[][] = [];
|
||||
public _derivationPath: string = '';
|
||||
public gap_limit: number = 10;
|
||||
|
||||
isLegacy() {
|
||||
return this._isLegacy;
|
||||
|
@ -76,25 +103,25 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
this._isLegacy = true;
|
||||
}
|
||||
|
||||
setM(m) {
|
||||
setM(m: number) {
|
||||
this._m = m;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} How many minumim signatures required to authorize a spend
|
||||
*/
|
||||
getM() {
|
||||
getM(): number {
|
||||
return this._m;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number} Total count of cosigners
|
||||
*/
|
||||
getN() {
|
||||
getN(): number {
|
||||
return this._cosigners.length;
|
||||
}
|
||||
|
||||
setDerivationPath(path) {
|
||||
setDerivationPath(path: string) {
|
||||
this._derivationPath = path;
|
||||
switch (this._derivationPath) {
|
||||
case "m/48'/0'/0'/2'":
|
||||
|
@ -112,33 +139,33 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
}
|
||||
}
|
||||
|
||||
getCustomDerivationPathForCosigner(index) {
|
||||
getCustomDerivationPathForCosigner(index: number): string | false {
|
||||
if (index === 0) throw new Error('cosigners indexation starts from 1');
|
||||
if (index > this.getN()) return false;
|
||||
return this._cosignersCustomPaths[index - 1] || this.getDerivationPath();
|
||||
return this._cosignersCustomPaths[index - 1] || this.getDerivationPath()!;
|
||||
}
|
||||
|
||||
getCosigner(index) {
|
||||
getCosigner(index: number) {
|
||||
if (index === 0) throw new Error('cosigners indexation starts from 1');
|
||||
return this._cosigners[index - 1];
|
||||
}
|
||||
|
||||
getFingerprint(index) {
|
||||
getFingerprint(index: number) {
|
||||
if (index === 0) throw new Error('cosigners fingerprints indexation starts from 1');
|
||||
return this._cosignersFingerprints[index - 1];
|
||||
}
|
||||
|
||||
getCosignerForFingerprint(fp) {
|
||||
getCosignerForFingerprint(fp: string) {
|
||||
const index = this._cosignersFingerprints.indexOf(fp);
|
||||
return this._cosigners[index];
|
||||
}
|
||||
|
||||
getPassphrase(index) {
|
||||
getCosignerPassphrase(index: number) {
|
||||
if (index === 0) throw new Error('cosigners indexation starts from 1');
|
||||
return this._cosignersPassphrases[index - 1];
|
||||
}
|
||||
|
||||
static isXpubValid(key) {
|
||||
static isXpubValid(key: string): boolean {
|
||||
let xpub;
|
||||
|
||||
try {
|
||||
|
@ -151,7 +178,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return false;
|
||||
}
|
||||
|
||||
static isXprvValid(xprv) {
|
||||
static isXprvValid(xprv: string): boolean {
|
||||
try {
|
||||
xprv = MultisigHDWallet.convertMultisigXprvToRegularXprv(xprv);
|
||||
bip32.fromBase58(xprv);
|
||||
|
@ -168,12 +195,12 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
* @param path {string} Custom path (if any) for cosigner that is added as mnemonics
|
||||
* @param passphrase {string} BIP38 Passphrase (if any)
|
||||
*/
|
||||
addCosigner(key, fingerprint, path, passphrase) {
|
||||
addCosigner(key: string, fingerprint?: string, path?: string, passphrase?: string) {
|
||||
if (MultisigHDWallet.isXpubString(key) && !fingerprint) {
|
||||
throw new Error('fingerprint is required when adding cosigner as xpub (watch-only)');
|
||||
}
|
||||
|
||||
if (path && !this.constructor.isPathValid(path)) {
|
||||
if (path && !MultisigHDWallet.isPathValid(path)) {
|
||||
throw new Error('path is not valid');
|
||||
}
|
||||
|
||||
|
@ -214,13 +241,13 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
if (passphrase) this._cosignersPassphrases[index] = passphrase;
|
||||
}
|
||||
|
||||
static convertMultisigXprvToRegularXprv(Zprv) {
|
||||
static convertMultisigXprvToRegularXprv(Zprv: string) {
|
||||
let data = b58.decode(Zprv);
|
||||
data = data.slice(4);
|
||||
return b58.encode(Buffer.concat([Buffer.from('0488ade4', 'hex'), data]));
|
||||
}
|
||||
|
||||
static convertXprvToXpub(xprv) {
|
||||
static convertXprvToXpub(xprv: string) {
|
||||
const restored = bip32.fromBase58(MultisigHDWallet.convertMultisigXprvToRegularXprv(xprv));
|
||||
return restored.neutered().toBase58();
|
||||
}
|
||||
|
@ -232,7 +259,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
* @returns {string} xpub
|
||||
* @private
|
||||
*/
|
||||
_getXpubFromCosigner(cosigner) {
|
||||
_getXpubFromCosigner(cosigner: string) {
|
||||
if (MultisigHDWallet.isXprvString(cosigner)) cosigner = MultisigHDWallet.convertXprvToXpub(cosigner);
|
||||
let xpub = cosigner;
|
||||
if (!MultisigHDWallet.isXpubString(cosigner)) {
|
||||
|
@ -246,7 +273,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return this._zpubToXpub(xpub);
|
||||
}
|
||||
|
||||
_getExternalAddressByIndex(index) {
|
||||
_getExternalAddressByIndex(index: number) {
|
||||
if (!this._m) throw new Error('m is not set');
|
||||
index = +index;
|
||||
if (this.external_addresses_cache[index]) return this.external_addresses_cache[index]; // cache hit
|
||||
|
@ -256,10 +283,9 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return address;
|
||||
}
|
||||
|
||||
_getAddressFromNode(nodeIndex, index) {
|
||||
_getAddressFromNode(nodeIndex: number, index: number) {
|
||||
const pubkeys = [];
|
||||
for (const [cosignerIndex, cosigner] of this._cosigners.entries()) {
|
||||
this._nodes = this._nodes || [];
|
||||
this._nodes[nodeIndex] = this._nodes[nodeIndex] || [];
|
||||
let _node;
|
||||
|
||||
|
@ -281,18 +307,27 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||
}),
|
||||
});
|
||||
if (!address) {
|
||||
throw new Error('Internal error: could not make p2sh address');
|
||||
}
|
||||
|
||||
return address;
|
||||
} else if (this.isNativeSegwit()) {
|
||||
const { address } = bitcoin.payments.p2wsh({
|
||||
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||
});
|
||||
if (!address) {
|
||||
throw new Error('Internal error: could not make p2wsh address');
|
||||
}
|
||||
|
||||
return address;
|
||||
} else if (this.isLegacy()) {
|
||||
const { address } = bitcoin.payments.p2sh({
|
||||
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||
});
|
||||
if (!address) {
|
||||
throw new Error('Internal error: could not make p2sh address');
|
||||
}
|
||||
|
||||
return address;
|
||||
} else {
|
||||
|
@ -300,7 +335,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
}
|
||||
}
|
||||
|
||||
_getInternalAddressByIndex(index) {
|
||||
_getInternalAddressByIndex(index: number) {
|
||||
if (!this._m) throw new Error('m is not set');
|
||||
index = +index;
|
||||
if (this.internal_addresses_cache[index]) return this.internal_addresses_cache[index]; // cache hit
|
||||
|
@ -310,7 +345,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return address;
|
||||
}
|
||||
|
||||
static seedToXpub(mnemonic, path, passphrase) {
|
||||
static seedToXpub(mnemonic: string, path: string, passphrase?: string): string {
|
||||
let seed;
|
||||
if (mnemonic.startsWith(ELECTRUM_SEED_PREFIX)) {
|
||||
seed = MultisigHDWallet.convertElectrumMnemonicToSeed(mnemonic, passphrase);
|
||||
|
@ -331,7 +366,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
* @param xpub {string} Any kind of xpub, including zpub etc since we are only swapping the prefix bytes
|
||||
* @returns {string}
|
||||
*/
|
||||
convertXpubToMultisignatureXpub(xpub) {
|
||||
convertXpubToMultisignatureXpub(xpub: string): string {
|
||||
let data = b58.decode(xpub);
|
||||
data = data.slice(4);
|
||||
if (this.isNativeSegwit()) {
|
||||
|
@ -343,7 +378,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return xpub;
|
||||
}
|
||||
|
||||
convertXprvToMultisignatureXprv(xpub) {
|
||||
convertXprvToMultisignatureXprv(xpub: string): string {
|
||||
let data = b58.decode(xpub);
|
||||
data = data.slice(4);
|
||||
if (this.isNativeSegwit()) {
|
||||
|
@ -355,11 +390,11 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return xpub;
|
||||
}
|
||||
|
||||
static isXpubString(xpub) {
|
||||
static isXpubString(xpub: string): boolean {
|
||||
return ['xpub', 'ypub', 'zpub', 'Ypub', 'Zpub'].includes(xpub.substring(0, 4));
|
||||
}
|
||||
|
||||
static isXprvString(xpub) {
|
||||
static isXprvString(xpub: string): boolean {
|
||||
return ['xprv', 'yprv', 'zprv', 'Yprv', 'Zprv'].includes(xpub.substring(0, 4));
|
||||
}
|
||||
|
||||
|
@ -369,7 +404,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
* @param xfp {number} For example 64392470
|
||||
* @returns {string} For example 168DD603
|
||||
*/
|
||||
static ckccXfp2fingerprint(xfp) {
|
||||
static ckccXfp2fingerprint(xfp: string | number): string {
|
||||
let masterFingerprintHex = Number(xfp).toString(16);
|
||||
while (masterFingerprintHex.length < 8) masterFingerprintHex = '0' + masterFingerprintHex; // conversion without explicit zero might result in lost byte
|
||||
|
||||
|
@ -400,15 +435,15 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
ret += 'Policy: ' + this.getM() + ' of ' + this.getN() + '\n';
|
||||
|
||||
let hasCustomPaths = 0;
|
||||
const customPaths = {};
|
||||
const customPaths: Record<string, number> = {};
|
||||
for (let index = 0; index < this.getN(); index++) {
|
||||
if (this._cosignersCustomPaths[index]) hasCustomPaths++;
|
||||
if (this._cosignersCustomPaths[index]) customPaths[this._cosignersCustomPaths[index]] = 1;
|
||||
}
|
||||
|
||||
let printedGlobalDerivation = false;
|
||||
|
||||
if (this.getDerivationPath()) customPaths[this.getDerivationPath()] = 1;
|
||||
const derivationPath = this.getDerivationPath();
|
||||
if (derivationPath) customPaths[derivationPath] = 1;
|
||||
if (Object.keys(customPaths).length === 1) {
|
||||
// we have exactly one path, for everyone. lets just print it
|
||||
for (const path of Object.keys(customPaths)) {
|
||||
|
@ -442,7 +477,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
// if we printed global derivation and this cosigned _has_ derivation and its different from global - we print it ;
|
||||
// or we print it if cosigner _has_ some derivation set and we did not print global
|
||||
}
|
||||
if (this.constructor.isXpubString(this._cosigners[index])) {
|
||||
if (MultisigHDWallet.isXpubString(this._cosigners[index])) {
|
||||
ret += this._cosignersFingerprints[index] + ': ' + this._cosigners[index] + '\n';
|
||||
} else {
|
||||
if (coordinationSetup) {
|
||||
|
@ -468,9 +503,9 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return ret;
|
||||
}
|
||||
|
||||
setSecret(secret) {
|
||||
setSecret(secret: string) {
|
||||
if (secret.toUpperCase().startsWith('UR:BYTES')) {
|
||||
const decoded = decodeUR([secret]);
|
||||
const decoded = decodeUR([secret]) as string;
|
||||
const b = Buffer.from(decoded, 'hex');
|
||||
secret = b.toString();
|
||||
}
|
||||
|
@ -482,7 +517,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
} catch (_) {}
|
||||
if (json && json.xfp && json.p2wsh_deriv && json.p2wsh) {
|
||||
this.addCosigner(json.p2wsh, json.xfp); // technically we dont need deriv (json.p2wsh_deriv), since cosigner is already an xpub
|
||||
return;
|
||||
return this;
|
||||
}
|
||||
|
||||
// is it electrum json?
|
||||
|
@ -513,7 +548,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
}
|
||||
|
||||
// coldcard & cobo txt format:
|
||||
let customPathForCurrentCosigner = false;
|
||||
let customPathForCurrentCosigner: string | undefined;
|
||||
for (const line of secret.split('\n')) {
|
||||
const [key, value] = line.split(':');
|
||||
|
||||
|
@ -552,7 +587,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
customPathForCurrentCosigner = value.trim();
|
||||
} else if (key === 'seed') {
|
||||
const [seed, passphrase] = value.split(' - ');
|
||||
this.addCosigner(seed.trim(), false, customPathForCurrentCosigner, passphrase);
|
||||
this.addCosigner(seed.trim(), undefined, customPathForCurrentCosigner, passphrase);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -648,15 +683,17 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
}
|
||||
|
||||
for (const pk of json.extendedPublicKeys) {
|
||||
const path = this.constructor.isPathValid(json.bip32Path) ? json.bip32Path : "m/1'";
|
||||
const path = MultisigHDWallet.isPathValid(json.bip32Path) ? json.bip32Path : "m/1'";
|
||||
this.addCosigner(pk.xpub, pk.xfp ?? '00000000', path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.getLabel()) this.setLabel('Multisig vault');
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
_getDerivationPathByAddressWithCustomPath(address, customPathPrefix) {
|
||||
_getDerivationPathByAddressWithCustomPath(address: string, customPathPrefix: string | undefined) {
|
||||
const path = customPathPrefix || this._derivationPath;
|
||||
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
|
||||
if (this._getExternalAddressByIndex(c) === address) return path + '/0/' + c;
|
||||
|
@ -668,22 +705,26 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return false;
|
||||
}
|
||||
|
||||
_getWifForAddress(address) {
|
||||
_getWifForAddress(address: string): string {
|
||||
// @ts-ignore not applicable in multisig
|
||||
return false;
|
||||
}
|
||||
|
||||
_getPubkeyByAddress(address) {
|
||||
_getPubkeyByAddress(address: string): false | Buffer {
|
||||
throw new Error('Not applicable in multisig');
|
||||
}
|
||||
|
||||
_getDerivationPathByAddress(address) {
|
||||
_getDerivationPathByAddress(address: string): string {
|
||||
throw new Error('Not applicable in multisig');
|
||||
}
|
||||
|
||||
_addPsbtInput(psbt, input, sequence, masterFingerprintBuffer) {
|
||||
_addPsbtInput(psbt: Psbt, input: CoinSelectReturnInput, sequence: number, masterFingerprintBuffer?: Buffer) {
|
||||
const bip32Derivation = []; // array per each pubkey thats gona be used
|
||||
const pubkeys = [];
|
||||
for (const [cosignerIndex, cosigner] of this._cosigners.entries()) {
|
||||
if (!input.address) {
|
||||
throw new Error('Could not find address in input');
|
||||
}
|
||||
const path = this._getDerivationPathByAddressWithCustomPath(
|
||||
input.address,
|
||||
this._cosignersCustomPaths[cosignerIndex] || this._derivationPath,
|
||||
|
@ -691,6 +732,10 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
// ^^ path resembles _custom path_, if provided by user during setup, otherwise default path for wallet type gona be used
|
||||
const masterFingerprint = Buffer.from(this._cosignersFingerprints[cosignerIndex], 'hex');
|
||||
|
||||
if (!path) {
|
||||
throw new Error('Could not find derivation path for address ' + input.address);
|
||||
}
|
||||
|
||||
const xpub = this._getXpubFromCosigner(cosigner);
|
||||
const hdNode0 = bip32.fromBase58(xpub);
|
||||
const splt = path.split('/');
|
||||
|
@ -707,16 +752,22 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
});
|
||||
}
|
||||
|
||||
if (!input.txhex) {
|
||||
throw new Error('Electrum server didnt provide txhex to properly create PSBT transaction');
|
||||
}
|
||||
|
||||
if (this.isNativeSegwit()) {
|
||||
const p2wsh = bitcoin.payments.p2wsh({
|
||||
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||
});
|
||||
if (!p2wsh.redeem || !p2wsh.output) {
|
||||
throw new Error('Could not create p2wsh output');
|
||||
}
|
||||
const witnessScript = p2wsh.redeem.output;
|
||||
|
||||
if (!input.txhex) throw new Error('Electrum server didnt provide txhex to properly create PSBT transaction');
|
||||
|
||||
psbt.addInput({
|
||||
hash: input.txId,
|
||||
// @ts-ignore: fix me txid || txId issue
|
||||
hash: input.txid || input.txId,
|
||||
index: input.vout,
|
||||
sequence,
|
||||
bip32Derivation,
|
||||
|
@ -735,11 +786,15 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||
}),
|
||||
});
|
||||
if (!p2shP2wsh?.redeem?.redeem?.output || !p2shP2wsh?.redeem?.output || !p2shP2wsh.output) {
|
||||
throw new Error('Could not create p2sh-p2wsh output');
|
||||
}
|
||||
|
||||
const witnessScript = p2shP2wsh.redeem.redeem.output;
|
||||
const redeemScript = p2shP2wsh.redeem.output;
|
||||
|
||||
psbt.addInput({
|
||||
hash: input.txId,
|
||||
hash: input.txid,
|
||||
index: input.vout,
|
||||
sequence,
|
||||
bip32Derivation,
|
||||
|
@ -757,9 +812,12 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
const p2sh = bitcoin.payments.p2sh({
|
||||
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||
});
|
||||
if (!p2sh?.redeem?.output) {
|
||||
throw new Error('Could not create p2sh output');
|
||||
}
|
||||
const redeemScript = p2sh.redeem.output;
|
||||
psbt.addInput({
|
||||
hash: input.txId,
|
||||
hash: input.txid,
|
||||
index: input.vout,
|
||||
sequence,
|
||||
bip32Derivation,
|
||||
|
@ -773,17 +831,21 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return psbt;
|
||||
}
|
||||
|
||||
_getOutputDataForChange(outputData) {
|
||||
const bip32Derivation = []; // array per each pubkey thats gona be used
|
||||
_getOutputDataForChange(address: string): TOutputData {
|
||||
const bip32Derivation: TBip32Derivation = []; // array per each pubkey thats gona be used
|
||||
const pubkeys = [];
|
||||
for (const [cosignerIndex, cosigner] of this._cosigners.entries()) {
|
||||
const path = this._getDerivationPathByAddressWithCustomPath(
|
||||
outputData.address,
|
||||
address,
|
||||
this._cosignersCustomPaths[cosignerIndex] || this._derivationPath,
|
||||
);
|
||||
// ^^ path resembles _custom path_, if provided by user during setup, otherwise default path for wallet type gona be used
|
||||
const masterFingerprint = Buffer.from(this._cosignersFingerprints[cosignerIndex], 'hex');
|
||||
|
||||
if (!path) {
|
||||
throw new Error('Could not find derivation path for address ' + address);
|
||||
}
|
||||
|
||||
const xpub = this._getXpubFromCosigner(cosigner);
|
||||
const hdNode0 = bip32.fromBase58(xpub);
|
||||
const splt = path.split('/');
|
||||
|
@ -800,30 +862,51 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
});
|
||||
}
|
||||
|
||||
outputData.bip32Derivation = bip32Derivation;
|
||||
|
||||
if (this.isLegacy()) {
|
||||
const p2sh = bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) });
|
||||
outputData.redeemScript = p2sh.output;
|
||||
} else if (this.isWrappedSegwit()) {
|
||||
if (!p2sh.output) {
|
||||
throw new Error('Could not create redeemScript');
|
||||
}
|
||||
return {
|
||||
bip32Derivation,
|
||||
redeemScript: p2sh.output,
|
||||
};
|
||||
}
|
||||
|
||||
if (this.isWrappedSegwit()) {
|
||||
const p2shP2wsh = bitcoin.payments.p2sh({
|
||||
redeem: bitcoin.payments.p2wsh({
|
||||
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||
}),
|
||||
});
|
||||
outputData.witnessScript = p2shP2wsh.redeem.redeem.output;
|
||||
outputData.redeemScript = p2shP2wsh.redeem.output;
|
||||
} else if (this.isNativeSegwit()) {
|
||||
const witnessScript = p2shP2wsh?.redeem?.redeem?.output;
|
||||
const redeemScript = p2shP2wsh?.redeem?.output;
|
||||
if (!witnessScript || !redeemScript) {
|
||||
throw new Error('Could not create redeemScript or witnessScript');
|
||||
}
|
||||
return {
|
||||
bip32Derivation,
|
||||
witnessScript,
|
||||
redeemScript,
|
||||
};
|
||||
}
|
||||
|
||||
if (this.isNativeSegwit()) {
|
||||
// not needed by coldcard, apparently..?
|
||||
const p2wsh = bitcoin.payments.p2wsh({
|
||||
redeem: bitcoin.payments.p2ms({ m: this._m, pubkeys: MultisigHDWallet.sortBuffers(pubkeys) }),
|
||||
});
|
||||
outputData.witnessScript = p2wsh.redeem.output;
|
||||
} else {
|
||||
throw new Error('dont know how to add change output');
|
||||
const witnessScript = p2wsh?.redeem?.output;
|
||||
if (!witnessScript) {
|
||||
throw new Error('Could not create witnessScript');
|
||||
}
|
||||
return {
|
||||
bip32Derivation,
|
||||
witnessScript,
|
||||
};
|
||||
}
|
||||
|
||||
return outputData;
|
||||
throw new Error('dont know how to add change output');
|
||||
}
|
||||
|
||||
howManySignaturesCanWeMake() {
|
||||
|
@ -838,19 +921,36 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
createTransaction(utxos, targets, feeRate, changeAddress, sequence, skipSigning = false, masterFingerprint) {
|
||||
createTransaction(
|
||||
utxos: CreateTransactionUtxo[],
|
||||
targets: CoinSelectTarget[],
|
||||
feeRate: number,
|
||||
changeAddress: string,
|
||||
sequence: number,
|
||||
skipSigning = false,
|
||||
masterFingerprint: number,
|
||||
): CreateTransactionResult {
|
||||
if (targets.length === 0) throw new Error('No destination provided');
|
||||
if (this.howManySignaturesCanWeMake() === 0) skipSigning = true;
|
||||
|
||||
// overriding script length for proper vbytes calculation
|
||||
for (const u of utxos) {
|
||||
u.script = u.script || {};
|
||||
if (u.script?.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.isNativeSegwit()) {
|
||||
u.script.length = u.script.length || Math.ceil((8 + this.getM() * 74 + this.getN() * 34) / 4);
|
||||
u.script = {
|
||||
length: Math.ceil((8 + this.getM() * 74 + this.getN() * 34) / 4),
|
||||
};
|
||||
} else if (this.isWrappedSegwit()) {
|
||||
u.script.length = u.script.length || 35 + Math.ceil((8 + this.getM() * 74 + this.getN() * 34) / 4);
|
||||
u.script = {
|
||||
length: 35 + Math.ceil((8 + this.getM() * 74 + this.getN() * 34) / 4),
|
||||
};
|
||||
} else {
|
||||
u.script.length = u.script.length || 9 + this.getM() * 74 + this.getN() * 34;
|
||||
u.script = {
|
||||
length: 9 + this.getM() * 74 + this.getN() * 34,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -868,18 +968,23 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
outputs.forEach(output => {
|
||||
// if output has no address - this is change output
|
||||
let change = false;
|
||||
if (!output.address) {
|
||||
let address: string | undefined = output.address;
|
||||
if (!address) {
|
||||
change = true;
|
||||
output.address = changeAddress;
|
||||
address = changeAddress;
|
||||
}
|
||||
|
||||
let outputData = {
|
||||
address: output.address,
|
||||
let outputData: Parameters<typeof psbt.addOutput>[0] = {
|
||||
address,
|
||||
value: output.value,
|
||||
};
|
||||
|
||||
if (change) {
|
||||
outputData = this._getOutputDataForChange(outputData);
|
||||
outputData = {
|
||||
...outputData,
|
||||
...this._getOutputDataForChange(address),
|
||||
};
|
||||
}
|
||||
|
||||
psbt.addOutput(outputData);
|
||||
|
@ -915,7 +1020,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return { tx, inputs, outputs, fee, psbt };
|
||||
}
|
||||
|
||||
static convertElectrumMnemonicToSeed(cosigner, passphrase) {
|
||||
static convertElectrumMnemonicToSeed(cosigner: string, passphrase?: string) {
|
||||
let seed;
|
||||
try {
|
||||
seed = mn.mnemonicToSeedSync(cosigner.replace(ELECTRUM_SEED_PREFIX, ''), electrumSegwit(passphrase));
|
||||
|
@ -931,20 +1036,18 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
|
||||
/**
|
||||
* @see https://github.com/bitcoin/bips/blob/master/bip-0067.mediawiki
|
||||
*
|
||||
* @param bufArr {Array.<Buffer>}
|
||||
* @returns {Array.<Buffer>}
|
||||
*/
|
||||
static sortBuffers(bufArr) {
|
||||
static sortBuffers(bufArr: Buffer[]): Buffer[] {
|
||||
return bufArr.sort(Buffer.compare);
|
||||
}
|
||||
|
||||
prepareForSerialization() {
|
||||
// deleting structures that cant be serialized
|
||||
// @ts-ignore I dont want to make it optional
|
||||
delete this._nodes;
|
||||
}
|
||||
|
||||
static isPathValid(path) {
|
||||
static isPathValid(path: string): boolean {
|
||||
const root = bip32.fromSeed(Buffer.alloc(32));
|
||||
try {
|
||||
root.derivePath(path);
|
||||
|
@ -984,9 +1087,9 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return createHash('sha256').update(string2hash).digest().toString('hex');
|
||||
}
|
||||
|
||||
calculateFeeFromPsbt(psbt) {
|
||||
calculateFeeFromPsbt(psbt: Psbt) {
|
||||
let goesIn = 0;
|
||||
const cacheUtxoAmounts = {};
|
||||
const cacheUtxoAmounts: { [key: string]: number } = {};
|
||||
for (const inp of psbt.data.inputs) {
|
||||
if (inp.witnessUtxo && inp.witnessUtxo.value) {
|
||||
// segwit input
|
||||
|
@ -994,7 +1097,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
} else if (inp.nonWitnessUtxo) {
|
||||
// non-segwit input
|
||||
// lets parse this transaction and cache how much each input was worth
|
||||
const inputTx = bitcoin.Transaction.fromHex(inp.nonWitnessUtxo);
|
||||
const inputTx = bitcoin.Transaction.fromBuffer(inp.nonWitnessUtxo);
|
||||
let index = 0;
|
||||
for (const out of inputTx.outs) {
|
||||
cacheUtxoAmounts[inputTx.getId() + ':' + index] = out.value;
|
||||
|
@ -1007,7 +1110,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
// means we failed to get amounts that go in previously, so lets use utxo amounts cache we've build
|
||||
// from non-segwit inputs
|
||||
for (const inp of psbt.txInputs) {
|
||||
const cacheKey = reverse(inp.hash).toString('hex') + ':' + inp.index;
|
||||
const cacheKey = Buffer.from(inp.hash).reverse().toString('hex') + ':' + inp.index;
|
||||
if (cacheUtxoAmounts[cacheKey]) goesIn += cacheUtxoAmounts[cacheKey];
|
||||
}
|
||||
}
|
||||
|
@ -1020,7 +1123,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
return goesIn - goesOut;
|
||||
}
|
||||
|
||||
calculateHowManySignaturesWeHaveFromPsbt(psbt) {
|
||||
calculateHowManySignaturesWeHaveFromPsbt(psbt: Psbt) {
|
||||
let sigsHave = 0;
|
||||
for (const inp of psbt.data.inputs) {
|
||||
sigsHave = Math.max(sigsHave, inp.partialSig?.length || 0);
|
||||
|
@ -1034,11 +1137,8 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
/**
|
||||
* Tries to signs passed psbt object (by reference). If there are enough signatures - tries to finalize psbt
|
||||
* and returns Transaction (ready to extract hex)
|
||||
*
|
||||
* @param psbt {Psbt}
|
||||
* @returns {{ tx: Transaction }}
|
||||
*/
|
||||
cosignPsbt(psbt) {
|
||||
cosignPsbt(psbt: Psbt): { tx: Transaction | false } {
|
||||
for (let cc = 0; cc < psbt.inputCount; cc++) {
|
||||
for (const [cosignerIndex, cosigner] of this._cosigners.entries()) {
|
||||
if (MultisigHDWallet.isXpubString(cosigner)) continue;
|
||||
|
@ -1080,7 +1180,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
// ^^^ we assume that counterparty has Zpub for specified derivation path
|
||||
// if hdRoot.depth !== 0 than this hdnode was recovered from xprv and it already has been set to root path
|
||||
const child = hdRoot.derivePath(path);
|
||||
if (psbt.inputHasPubkey(cc, child.publicKey)) {
|
||||
if (child.privateKey && psbt.inputHasPubkey(cc, child.publicKey)) {
|
||||
const keyPair = ECPair.fromPrivateKey(child.privateKey);
|
||||
try {
|
||||
psbt.signInput(cc, keyPair);
|
||||
|
@ -1091,22 +1191,18 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
}
|
||||
}
|
||||
|
||||
let tx = false;
|
||||
if (this.calculateHowManySignaturesWeHaveFromPsbt(psbt) >= this.getM()) {
|
||||
tx = psbt.finalizeAllInputs().extractTransaction();
|
||||
const tx = psbt.finalizeAllInputs().extractTransaction();
|
||||
return { tx };
|
||||
}
|
||||
|
||||
return { tx };
|
||||
return { tx: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up xpub cosigner by index, and repalces it with seed + passphrase
|
||||
*
|
||||
* @param externalIndex {number}
|
||||
* @param mnemonic {string}
|
||||
* @param passphrase {string}
|
||||
*/
|
||||
replaceCosignerXpubWithSeed(externalIndex, mnemonic, passphrase) {
|
||||
replaceCosignerXpubWithSeed(externalIndex: number, mnemonic: string, passphrase?: string) {
|
||||
const index = externalIndex - 1;
|
||||
const fingerprint = this._cosignersFingerprints[index];
|
||||
if (!MultisigHDWallet.isXpubValid(this._cosigners[index])) throw new Error('This cosigner doesnt contain valid xpub');
|
||||
|
@ -1120,10 +1216,8 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
|
||||
/**
|
||||
* Looks up cosigner with seed by index, and repalces it with xpub
|
||||
*
|
||||
* @param externalIndex {number}
|
||||
*/
|
||||
replaceCosignerSeedWithXpub(externalIndex) {
|
||||
replaceCosignerSeedWithXpub(externalIndex: number) {
|
||||
const index = externalIndex - 1;
|
||||
const mnemonics = this._cosigners[index];
|
||||
if (!bip39.validateMnemonic(mnemonics)) throw new Error('This cosigner doesnt contain valid xpub mnemonic phrase');
|
||||
|
@ -1134,7 +1228,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
this._cosignersPassphrases[index] = undefined;
|
||||
}
|
||||
|
||||
deleteCosigner(fp) {
|
||||
deleteCosigner(fp: string) {
|
||||
const foundIndex = this._cosignersFingerprints.indexOf(fp);
|
||||
if (foundIndex === -1) throw new Error('Cant find cosigner by fingerprint');
|
||||
|
||||
|
@ -1163,9 +1257,9 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
}
|
||||
|
||||
getFormat() {
|
||||
if (this.isNativeSegwit()) return this.constructor.FORMAT_P2WSH;
|
||||
if (this.isWrappedSegwit()) return this.constructor.FORMAT_P2SH_P2WSH;
|
||||
if (this.isLegacy()) return this.constructor.FORMAT_P2SH;
|
||||
if (this.isNativeSegwit()) return MultisigHDWallet.FORMAT_P2WSH;
|
||||
if (this.isWrappedSegwit()) return MultisigHDWallet.FORMAT_P2SH_P2WSH;
|
||||
if (this.isLegacy()) return MultisigHDWallet.FORMAT_P2SH;
|
||||
|
||||
throw new Error('This should never happen');
|
||||
}
|
||||
|
@ -1174,7 +1268,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
* @param fp {string} Exactly 8 chars of hex
|
||||
* @return {boolean}
|
||||
*/
|
||||
static isFpValid(fp) {
|
||||
static isFpValid(fp: string) {
|
||||
if (fp.length !== 8) return false;
|
||||
return /^[0-9A-F]{8}$/i.test(fp);
|
||||
}
|
||||
|
@ -1187,7 +1281,7 @@ export class MultisigHDWallet extends AbstractHDElectrumWallet {
|
|||
* @param xpub
|
||||
* @return {boolean}
|
||||
*/
|
||||
static isXpubForMultisig(xpub) {
|
||||
static isXpubForMultisig(xpub: string): boolean {
|
||||
return ['xpub', 'Ypub', 'Zpub'].includes(xpub.substring(0, 4));
|
||||
}
|
||||
|
|
@ -1,15 +1,18 @@
|
|||
import { LegacyWallet } from './legacy-wallet';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import { ECPairFactory } from 'ecpair';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { LegacyWallet } from './legacy-wallet';
|
||||
import { CreateTransactionResult, CreateTransactionUtxo } from './types';
|
||||
import { CoinSelectTarget } from 'coinselect';
|
||||
|
||||
const ECPair = ECPairFactory(ecc);
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
|
||||
export class SegwitBech32Wallet extends LegacyWallet {
|
||||
static type = 'segwitBech32';
|
||||
static typeReadable = 'P2 WPKH';
|
||||
static segwitType = 'p2wpkh';
|
||||
|
||||
getAddress() {
|
||||
getAddress(): string | false {
|
||||
if (this._address) return this._address;
|
||||
let address;
|
||||
try {
|
||||
|
@ -24,18 +27,20 @@ export class SegwitBech32Wallet extends LegacyWallet {
|
|||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
this._address = address;
|
||||
this._address = address ?? false;
|
||||
|
||||
return this._address;
|
||||
}
|
||||
|
||||
static witnessToAddress(witness) {
|
||||
static witnessToAddress(witness: string): string | false {
|
||||
try {
|
||||
const pubKey = Buffer.from(witness, 'hex');
|
||||
return bitcoin.payments.p2wpkh({
|
||||
pubkey: pubKey,
|
||||
network: bitcoin.networks.bitcoin,
|
||||
}).address;
|
||||
const pubkey = Buffer.from(witness, 'hex');
|
||||
return (
|
||||
bitcoin.payments.p2wpkh({
|
||||
pubkey,
|
||||
network: bitcoin.networks.bitcoin,
|
||||
}).address ?? false
|
||||
);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
|
@ -47,19 +52,29 @@ export class SegwitBech32Wallet extends LegacyWallet {
|
|||
* @param scriptPubKey
|
||||
* @returns {boolean|string} Either bech32 address or false
|
||||
*/
|
||||
static scriptPubKeyToAddress(scriptPubKey) {
|
||||
static scriptPubKeyToAddress(scriptPubKey: string): string | false {
|
||||
try {
|
||||
const scriptPubKey2 = Buffer.from(scriptPubKey, 'hex');
|
||||
return bitcoin.payments.p2wpkh({
|
||||
output: scriptPubKey2,
|
||||
network: bitcoin.networks.bitcoin,
|
||||
}).address;
|
||||
return (
|
||||
bitcoin.payments.p2wpkh({
|
||||
output: scriptPubKey2,
|
||||
network: bitcoin.networks.bitcoin,
|
||||
}).address ?? false
|
||||
);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
createTransaction(utxos, targets, feeRate, changeAddress, sequence, skipSigning = false, masterFingerprint) {
|
||||
createTransaction(
|
||||
utxos: CreateTransactionUtxo[],
|
||||
targets: CoinSelectTarget[],
|
||||
feeRate: number,
|
||||
changeAddress: string,
|
||||
sequence: number,
|
||||
skipSigning = false,
|
||||
masterFingerprint: number,
|
||||
): CreateTransactionResult {
|
||||
if (targets.length === 0) throw new Error('No destination provided');
|
||||
// compensating for coinselect inability to deal with segwit inputs, and overriding script length for proper vbytes calculation
|
||||
for (const u of utxos) {
|
||||
|
@ -69,19 +84,18 @@ export class SegwitBech32Wallet extends LegacyWallet {
|
|||
sequence = sequence || 0xffffffff; // disable RBF by default
|
||||
const psbt = new bitcoin.Psbt();
|
||||
let c = 0;
|
||||
const values = {};
|
||||
let keyPair;
|
||||
const values: Record<number, number> = {};
|
||||
const keyPair = ECPair.fromWIF(this.secret);
|
||||
|
||||
inputs.forEach(input => {
|
||||
if (!skipSigning) {
|
||||
// skiping signing related stuff
|
||||
keyPair = ECPair.fromWIF(this.secret); // secret is WIF
|
||||
}
|
||||
values[c] = input.value;
|
||||
c++;
|
||||
|
||||
const pubkey = keyPair.publicKey;
|
||||
const p2wpkh = bitcoin.payments.p2wpkh({ pubkey });
|
||||
if (!p2wpkh.output) {
|
||||
throw new Error('Internal error: no p2wpkh.output during createTransaction()');
|
||||
}
|
||||
|
||||
psbt.addInput({
|
||||
hash: input.txid,
|
|
@ -1,8 +1,11 @@
|
|||
import { LegacyWallet } from './legacy-wallet';
|
||||
import * as bitcoin from 'bitcoinjs-lib';
|
||||
import { CoinSelectTarget } from 'coinselect';
|
||||
import { ECPairFactory } from 'ecpair';
|
||||
import ecc from '../../blue_modules/noble_ecc';
|
||||
import { LegacyWallet } from './legacy-wallet';
|
||||
import { CreateTransactionResult, CreateTransactionUtxo } from './types';
|
||||
|
||||
const ECPair = ECPairFactory(ecc);
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
|
||||
/**
|
||||
* Creates Segwit P2SH Bitcoin address
|
||||
|
@ -10,13 +13,11 @@ const bitcoin = require('bitcoinjs-lib');
|
|||
* @param network
|
||||
* @returns {String}
|
||||
*/
|
||||
function pubkeyToP2shSegwitAddress(pubkey, network) {
|
||||
network = network || bitcoin.networks.bitcoin;
|
||||
function pubkeyToP2shSegwitAddress(pubkey: Buffer): string | false {
|
||||
const { address } = bitcoin.payments.p2sh({
|
||||
redeem: bitcoin.payments.p2wpkh({ pubkey, network }),
|
||||
network,
|
||||
redeem: bitcoin.payments.p2wpkh({ pubkey }),
|
||||
});
|
||||
return address;
|
||||
return address ?? false;
|
||||
}
|
||||
|
||||
export class SegwitP2SHWallet extends LegacyWallet {
|
||||
|
@ -24,7 +25,7 @@ export class SegwitP2SHWallet extends LegacyWallet {
|
|||
static typeReadable = 'SegWit (P2SH)';
|
||||
static segwitType = 'p2sh(p2wpkh)';
|
||||
|
||||
static witnessToAddress(witness) {
|
||||
static witnessToAddress(witness: string): string | false {
|
||||
try {
|
||||
const pubKey = Buffer.from(witness, 'hex');
|
||||
return pubkeyToP2shSegwitAddress(pubKey);
|
||||
|
@ -39,19 +40,21 @@ export class SegwitP2SHWallet extends LegacyWallet {
|
|||
* @param scriptPubKey
|
||||
* @returns {boolean|string} Either p2sh address or false
|
||||
*/
|
||||
static scriptPubKeyToAddress(scriptPubKey) {
|
||||
static scriptPubKeyToAddress(scriptPubKey: string): string | false {
|
||||
try {
|
||||
const scriptPubKey2 = Buffer.from(scriptPubKey, 'hex');
|
||||
return bitcoin.payments.p2sh({
|
||||
output: scriptPubKey2,
|
||||
network: bitcoin.networks.bitcoin,
|
||||
}).address;
|
||||
return (
|
||||
bitcoin.payments.p2sh({
|
||||
output: scriptPubKey2,
|
||||
network: bitcoin.networks.bitcoin,
|
||||
}).address ?? false
|
||||
);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
getAddress(): string | false {
|
||||
if (this._address) return this._address;
|
||||
let address;
|
||||
try {
|
||||
|
@ -81,7 +84,15 @@ export class SegwitP2SHWallet extends LegacyWallet {
|
|||
* @param masterFingerprint {number} Decimal number of wallet's master fingerprint
|
||||
* @returns {{outputs: Array, tx: Transaction, inputs: Array, fee: Number, psbt: Psbt}}
|
||||
*/
|
||||
createTransaction(utxos, targets, feeRate, changeAddress, sequence, skipSigning = false, masterFingerprint) {
|
||||
createTransaction(
|
||||
utxos: CreateTransactionUtxo[],
|
||||
targets: CoinSelectTarget[],
|
||||
feeRate: number,
|
||||
changeAddress: string,
|
||||
sequence: number,
|
||||
skipSigning = false,
|
||||
masterFingerprint: number,
|
||||
): CreateTransactionResult {
|
||||
if (targets.length === 0) throw new Error('No destination provided');
|
||||
// compensating for coinselect inability to deal with segwit inputs, and overriding script length for proper vbytes calculation
|
||||
for (const u of utxos) {
|
||||
|
@ -91,20 +102,19 @@ export class SegwitP2SHWallet extends LegacyWallet {
|
|||
sequence = sequence || 0xffffffff; // disable RBF by default
|
||||
const psbt = new bitcoin.Psbt();
|
||||
let c = 0;
|
||||
const values = {};
|
||||
let keyPair;
|
||||
const values: Record<number, number> = {};
|
||||
const keyPair = ECPair.fromWIF(this.secret);
|
||||
|
||||
inputs.forEach(input => {
|
||||
if (!skipSigning) {
|
||||
// skiping signing related stuff
|
||||
keyPair = ECPair.fromWIF(this.secret); // secret is WIF
|
||||
}
|
||||
values[c] = input.value;
|
||||
c++;
|
||||
|
||||
const pubkey = keyPair.publicKey;
|
||||
const p2wpkh = bitcoin.payments.p2wpkh({ pubkey });
|
||||
const p2sh = bitcoin.payments.p2sh({ redeem: p2wpkh });
|
||||
if (!p2sh.output) {
|
||||
throw new Error('Internal error: no p2sh.output during createTransaction()');
|
||||
}
|
||||
|
||||
psbt.addInput({
|
||||
hash: input.txid,
|
|
@ -91,7 +91,7 @@
|
|||
B4AB225E2B02AD12001F4328 /* XMLParserDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4AB225C2B02AD12001F4328 /* XMLParserDelegate.swift */; };
|
||||
B4EE583C226703320003363C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B40D4E35225841ED00428FCC /* Assets.xcassets */; };
|
||||
C59F90CE0D04D3E4BB39BC5D /* libPods-BlueWalletUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6F02C2F7CA3591E4E0B06EBA /* libPods-BlueWalletUITests.a */; };
|
||||
C978A716948AB7DEC5B6F677 /* (null) in Frameworks */ = {isa = PBXBuildFile; };
|
||||
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; };
|
||||
E5D4794B26781FC0007838C1 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = 6DD410AD266CAF1F0087DE03 /* fiatUnits.json */; };
|
||||
E5D4794C26781FC1007838C1 /* fiatUnits.json in Resources */ = {isa = PBXBuildFile; fileRef = 6DD410AD266CAF1F0087DE03 /* fiatUnits.json */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
@ -399,7 +399,7 @@
|
|||
files = (
|
||||
782F075B5DD048449E2DECE9 /* libz.tbd in Frameworks */,
|
||||
764B49B1420D4AEB8109BF62 /* libsqlite3.0.tbd in Frameworks */,
|
||||
C978A716948AB7DEC5B6F677 /* (null) in Frameworks */,
|
||||
C978A716948AB7DEC5B6F677 /* BuildFile in Frameworks */,
|
||||
773E382FE62E836172AAB98B /* libPods-BlueWallet.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
@ -1024,7 +1024,7 @@
|
|||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
packageReferences = (
|
||||
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */,
|
||||
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */,
|
||||
B41B76832B66B2FF002C48D5 /* XCRemoteSwiftPackageReference "bugsnag-cocoa" */,
|
||||
);
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
|
@ -2518,7 +2518,7 @@
|
|||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */ = {
|
||||
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/EFPrefix/EFQRCode.git";
|
||||
requirement = {
|
||||
|
@ -2539,7 +2539,7 @@
|
|||
/* Begin XCSwiftPackageProductDependency section */
|
||||
6DFC806F24EA0B6C007B8700 /* EFQRCode */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */;
|
||||
package = 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode.git" */;
|
||||
productName = EFQRCode;
|
||||
};
|
||||
B41B76842B66B2FF002C48D5 /* Bugsnag */ = {
|
||||
|
|
|
@ -332,7 +332,7 @@ PODS:
|
|||
- React
|
||||
- react-native-bw-file-access (1.0.0):
|
||||
- React-Core
|
||||
- react-native-document-picker (9.1.0):
|
||||
- react-native-document-picker (9.1.1):
|
||||
- React-Core
|
||||
- react-native-idle-timer (2.1.6):
|
||||
- React-Core
|
||||
|
@ -494,7 +494,7 @@ PODS:
|
|||
- React-Core
|
||||
- RNLocalize (3.0.6):
|
||||
- React-Core
|
||||
- RNPermissions (4.1.2):
|
||||
- RNPermissions (4.1.4):
|
||||
- React-Core
|
||||
- RNPrivacySnapshot (1.0.0):
|
||||
- React
|
||||
|
@ -814,7 +814,7 @@ SPEC CHECKSUMS:
|
|||
react-native-biometrics: 352e5a794bfffc46a0c86725ea7dc62deb085bdc
|
||||
react-native-blue-crypto: 23f1558ad3d38d7a2edb7e2f6ed1bc520ed93e56
|
||||
react-native-bw-file-access: b232fd1d902521ca046f3fc5990ab1465e1878d7
|
||||
react-native-document-picker: b4f4a23b73f864ce17965b284c0757648993805b
|
||||
react-native-document-picker: 3599b238843369026201d2ef466df53f77ae0452
|
||||
react-native-idle-timer: f7f651542b39dce9b9473e4578cb64a255075f17
|
||||
react-native-image-picker: 5e076db26cd81660cfb6db5bcf517cfa12054d45
|
||||
react-native-ios-context-menu: e529171ba760a1af7f2ef0729f5a7f4d226171c5
|
||||
|
@ -855,7 +855,7 @@ SPEC CHECKSUMS:
|
|||
RNHandoff: d3b0754cca3a6bcd9b25f544f733f7f033ccf5fa
|
||||
RNKeychain: f1b48665a4646f61191eb048c4c05c58d9a7596f
|
||||
RNLocalize: 4222a3756cdbe2dc9a5bdf445765a4d2572107cb
|
||||
RNPermissions: 0e72b28a9df5e3f0ae9cc122378d4b878ccb4f2f
|
||||
RNPermissions: 31223c0cf32b7623e6ec4cd13a7e4552fd43c71c
|
||||
RNPrivacySnapshot: 71919dde3c6a29dd332115409c2aec564afee8f4
|
||||
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
||||
RNRate: ef3bcff84f39bb1d1e41c5593d3eea4aab2bd73a
|
||||
|
|
|
@ -281,6 +281,7 @@
|
|||
"language": "Język",
|
||||
"last_updated": "Ostatnia aktualizacja",
|
||||
"language_isRTL": "Aby ustawienia dotyczące kierunku pisma wybranego języka zaczęły obowiązywać, BlueWallet musi być zrestartowany.",
|
||||
"license": "Licencja",
|
||||
"lightning_error_lndhub_uri": "Nieprawidłowy adres LNDHub",
|
||||
"lightning_saved": "Wprowadzone przez ciebie zmiany zostały pomyślnie zachowane.",
|
||||
"lightning_settings": "Ustawienia Lightning",
|
||||
|
@ -452,7 +453,6 @@
|
|||
"no_ln_wallet_error": "Musisz najpierw dodać portfel Lightning, zanim zapłacisz fakturę.",
|
||||
"looks_like_bip38": "To wygląda na klucz prywatny chroniony hasłem (BIP38).",
|
||||
"reorder_title": "Zmień kolejność portfeli",
|
||||
"reorder_instructions": "Stuknij i przytrzmaj portfel aby go przeciągnąć na liście.",
|
||||
"please_continue_scanning": "Proszę skanuj dalej.",
|
||||
"select_no_bitcoin": "Nie ma dostępnych portfeli Bitcoin.",
|
||||
"select_no_bitcoin_exp": "Portfel Bitcoin jest wymagany by uzupełnić portfel Lightning. Proszę utwórz lub zaimportuj.",
|
||||
|
@ -462,7 +462,8 @@
|
|||
"warning_do_not_disclose": "Uwaga! Nie ujawniać.",
|
||||
"add_ln_wallet_first": "Najpierw musisz dodać portfel Lightning.",
|
||||
"identity_pubkey": "Klucz publiczny tożsamości",
|
||||
"xpub_title": "XPUB portfela"
|
||||
"xpub_title": "XPUB portfela",
|
||||
"search_wallets": "Szukaj portfeli"
|
||||
},
|
||||
"multisig": {
|
||||
"multisig_vault": "Skarbiec",
|
||||
|
@ -571,6 +572,8 @@
|
|||
"sats": "satoshi"
|
||||
},
|
||||
"addresses": {
|
||||
"copy_private_key": "Skopiuj klucz prywatny",
|
||||
"sensitive_private_key": "Uwaga: klucz prywatne są skrajnie poufne. Kontynuować?",
|
||||
"sign_title": "Podpisz/Weryfikuj wiadomość",
|
||||
"sign_help": "Tutaj możesz stworzyć lub zweryfikować podpis kryptograficzny oparty o adres Bitcoin.",
|
||||
"sign_sign": "Podpisz",
|
||||
|
|
65
package-lock.json
generated
65
package-lock.json
generated
|
@ -38,11 +38,10 @@
|
|||
"bitcoinjs-message": "2.2.0",
|
||||
"bolt11": "1.4.1",
|
||||
"buffer": "6.0.3",
|
||||
"buffer-reverse": "1.0.1",
|
||||
"coinselect": "3.1.13",
|
||||
"crypto-js": "4.2.0",
|
||||
"dayjs": "1.11.10",
|
||||
"detox": "20.17.1",
|
||||
"detox": "20.18.3",
|
||||
"ecpair": "2.0.1",
|
||||
"ecurve": "1.0.6",
|
||||
"electrum-client": "github:BlueWallet/rn-electrum-client#1bfe3cc",
|
||||
|
@ -65,7 +64,7 @@
|
|||
"react-native-crypto": "2.2.0",
|
||||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "10.13.0",
|
||||
"react-native-document-picker": "https://github.com/BlueWallet/react-native-document-picker#0be5a70c3b456e35c2454aaf4dc8c2d40eb2ab47",
|
||||
"react-native-document-picker": "https://github.com/BlueWallet/react-native-document-picker#27fddb9d9a88fff09a41ce654f7008cfd33cb4c4",
|
||||
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#ebfddc4",
|
||||
"react-native-elements": "3.4.3",
|
||||
"react-native-fs": "2.20.0",
|
||||
|
@ -81,7 +80,7 @@
|
|||
"react-native-modal": "13.0.1",
|
||||
"react-native-obscure": "https://github.com/BlueWallet/react-native-obscure.git#f4b83b4a261e39b1f5ed4a45ac5bcabc8a59eadb",
|
||||
"react-native-passcode-auth": "https://github.com/BlueWallet/react-native-passcode-auth#a2ff977ba92b36f8d0a5567f59c05cc608e8bd12",
|
||||
"react-native-permissions": "4.1.2",
|
||||
"react-native-permissions": "4.1.4",
|
||||
"react-native-privacy-snapshot": "https://github.com/BlueWallet/react-native-privacy-snapshot#529e4627d93f67752a27e82a040ff7b64dca0783",
|
||||
"react-native-prompt-android": "https://github.com/BlueWallet/react-native-prompt-android#ed168d66fed556bc2ed07cf498770f058b78a376",
|
||||
"react-native-push-notification": "8.1.1",
|
||||
|
@ -8402,11 +8401,6 @@
|
|||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"node_modules/buffer-reverse": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz",
|
||||
"integrity": "sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg=="
|
||||
},
|
||||
"node_modules/buffer-xor": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||
|
@ -9646,9 +9640,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/detox": {
|
||||
"version": "20.17.1",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.17.1.tgz",
|
||||
"integrity": "sha512-10pey6CR9D5GSloRkH60ObBGZ8VS11H7iuBNY7qq6jO2swiqqckHhPLRXfH9+WGR7l3vDnfU+G/gQs7JxQkJwA==",
|
||||
"version": "20.18.3",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.18.3.tgz",
|
||||
"integrity": "sha512-ssC7pWLDOs48Rocu7TDdLaYWiDWF5A8EAf/YcCsseHIqAqr6ECX6Hve8MIh3wUkPGYWoZaJHQfT9cyZjklt8AQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"ajv": "^8.6.3",
|
||||
|
@ -12734,9 +12728,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/ip": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
|
||||
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
|
||||
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ=="
|
||||
},
|
||||
"node_modules/is-arguments": {
|
||||
"version": "1.1.1",
|
||||
|
@ -19596,9 +19590,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/react-native-document-picker": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-document-picker.git#0be5a70c3b456e35c2454aaf4dc8c2d40eb2ab47",
|
||||
"integrity": "sha512-74E4JnXExu4vu3W7NTDXUM1LF0FAjobWrIfC30ygSsxb4BV6eaIFRtnfrWOmKUuoY+Q9vlo+3dSrwqz5Xh2Ozw==",
|
||||
"version": "9.1.1",
|
||||
"resolved": "git+ssh://git@github.com/BlueWallet/react-native-document-picker.git#27fddb9d9a88fff09a41ce654f7008cfd33cb4c4",
|
||||
"integrity": "sha512-Bg6TD+xo9JbKhj8xsf1HwL++EPJw294hhWTWdABgfKhB79FPZjqrlEDG1U05XLXO1YYNNhVHULYC+JnbwqRkkw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"invariant": "^2.2.4"
|
||||
|
@ -19794,9 +19788,9 @@
|
|||
"license": "ISC"
|
||||
},
|
||||
"node_modules/react-native-permissions": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-4.1.2.tgz",
|
||||
"integrity": "sha512-dduzHcjYp1o52M8OLYhygQbGb/UnQLD3Gqhu+5cZz6MyWAb/4DQp5yYwKu6a2lY9Cz0QgbRd1Ejkmqn01cQqEg==",
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-4.1.4.tgz",
|
||||
"integrity": "sha512-el6u90VZNFDEtmHpSheirwRhGvs6M5rSZcoqIqB128IPO00f2c1q+PP4kEaM1u19Q0ldQ+R+S7C/l8Zrduy6JQ==",
|
||||
"peerDependencies": {
|
||||
"react": ">=18.1.0",
|
||||
"react-native": ">=0.70.0",
|
||||
|
@ -28971,11 +28965,6 @@
|
|||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
|
||||
},
|
||||
"buffer-reverse": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer-reverse/-/buffer-reverse-1.0.1.tgz",
|
||||
"integrity": "sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg=="
|
||||
},
|
||||
"buffer-xor": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||
|
@ -29910,9 +29899,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"detox": {
|
||||
"version": "20.17.1",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.17.1.tgz",
|
||||
"integrity": "sha512-10pey6CR9D5GSloRkH60ObBGZ8VS11H7iuBNY7qq6jO2swiqqckHhPLRXfH9+WGR7l3vDnfU+G/gQs7JxQkJwA==",
|
||||
"version": "20.18.3",
|
||||
"resolved": "https://registry.npmjs.org/detox/-/detox-20.18.3.tgz",
|
||||
"integrity": "sha512-ssC7pWLDOs48Rocu7TDdLaYWiDWF5A8EAf/YcCsseHIqAqr6ECX6Hve8MIh3wUkPGYWoZaJHQfT9cyZjklt8AQ==",
|
||||
"requires": {
|
||||
"ajv": "^8.6.3",
|
||||
"bunyan": "^1.8.12",
|
||||
|
@ -32131,9 +32120,9 @@
|
|||
}
|
||||
},
|
||||
"ip": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz",
|
||||
"integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg=="
|
||||
"version": "1.1.9",
|
||||
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
|
||||
"integrity": "sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ=="
|
||||
},
|
||||
"is-arguments": {
|
||||
"version": "1.1.1",
|
||||
|
@ -37434,9 +37423,9 @@
|
|||
"integrity": "sha512-AuZZXxZzQbv3JKE1bV0YPMXJVVTKQ3TJGpiBcYjJMOFZa0CE18kRgqHj3nKTM6KAD8CdCpjoEfuc128Rcs2TxA=="
|
||||
},
|
||||
"react-native-document-picker": {
|
||||
"version": "git+ssh://git@github.com/BlueWallet/react-native-document-picker.git#0be5a70c3b456e35c2454aaf4dc8c2d40eb2ab47",
|
||||
"integrity": "sha512-74E4JnXExu4vu3W7NTDXUM1LF0FAjobWrIfC30ygSsxb4BV6eaIFRtnfrWOmKUuoY+Q9vlo+3dSrwqz5Xh2Ozw==",
|
||||
"from": "react-native-document-picker@https://github.com/BlueWallet/react-native-document-picker#0be5a70c3b456e35c2454aaf4dc8c2d40eb2ab47",
|
||||
"version": "git+ssh://git@github.com/BlueWallet/react-native-document-picker.git#27fddb9d9a88fff09a41ce654f7008cfd33cb4c4",
|
||||
"integrity": "sha512-Bg6TD+xo9JbKhj8xsf1HwL++EPJw294hhWTWdABgfKhB79FPZjqrlEDG1U05XLXO1YYNNhVHULYC+JnbwqRkkw==",
|
||||
"from": "react-native-document-picker@https://github.com/BlueWallet/react-native-document-picker#27fddb9d9a88fff09a41ce654f7008cfd33cb4c4",
|
||||
"requires": {
|
||||
"invariant": "^2.2.4"
|
||||
}
|
||||
|
@ -37557,9 +37546,9 @@
|
|||
"from": "react-native-passcode-auth@https://github.com/BlueWallet/react-native-passcode-auth#a2ff977ba92b36f8d0a5567f59c05cc608e8bd12"
|
||||
},
|
||||
"react-native-permissions": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-4.1.2.tgz",
|
||||
"integrity": "sha512-dduzHcjYp1o52M8OLYhygQbGb/UnQLD3Gqhu+5cZz6MyWAb/4DQp5yYwKu6a2lY9Cz0QgbRd1Ejkmqn01cQqEg=="
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/react-native-permissions/-/react-native-permissions-4.1.4.tgz",
|
||||
"integrity": "sha512-el6u90VZNFDEtmHpSheirwRhGvs6M5rSZcoqIqB128IPO00f2c1q+PP4kEaM1u19Q0ldQ+R+S7C/l8Zrduy6JQ=="
|
||||
},
|
||||
"react-native-privacy-snapshot": {
|
||||
"version": "git+ssh://git@github.com/BlueWallet/react-native-privacy-snapshot.git#529e4627d93f67752a27e82a040ff7b64dca0783",
|
||||
|
|
|
@ -124,11 +124,10 @@
|
|||
"bitcoinjs-message": "2.2.0",
|
||||
"bolt11": "1.4.1",
|
||||
"buffer": "6.0.3",
|
||||
"buffer-reverse": "1.0.1",
|
||||
"coinselect": "3.1.13",
|
||||
"crypto-js": "4.2.0",
|
||||
"dayjs": "1.11.10",
|
||||
"detox": "20.17.1",
|
||||
"detox": "20.18.3",
|
||||
"ecpair": "2.0.1",
|
||||
"ecurve": "1.0.6",
|
||||
"electrum-client": "github:BlueWallet/rn-electrum-client#1bfe3cc",
|
||||
|
@ -150,7 +149,7 @@
|
|||
"react-native-camera-kit": "13.0.0",
|
||||
"react-native-crypto": "2.2.0",
|
||||
"react-native-default-preference": "1.4.4",
|
||||
"react-native-device-info": "10.13.0",
|
||||
"react-native-device-info": "10.13.1",
|
||||
"react-native-document-picker": "https://github.com/BlueWallet/react-native-document-picker#27fddb9d9a88fff09a41ce654f7008cfd33cb4c4",
|
||||
"react-native-draggable-flatlist": "github:BlueWallet/react-native-draggable-flatlist#ebfddc4",
|
||||
"react-native-elements": "3.4.3",
|
||||
|
@ -167,7 +166,7 @@
|
|||
"react-native-modal": "13.0.1",
|
||||
"react-native-obscure": "https://github.com/BlueWallet/react-native-obscure.git#f4b83b4a261e39b1f5ed4a45ac5bcabc8a59eadb",
|
||||
"react-native-passcode-auth": "https://github.com/BlueWallet/react-native-passcode-auth#a2ff977ba92b36f8d0a5567f59c05cc608e8bd12",
|
||||
"react-native-permissions": "4.1.2",
|
||||
"react-native-permissions": "4.1.4",
|
||||
"react-native-privacy-snapshot": "https://github.com/BlueWallet/react-native-privacy-snapshot#529e4627d93f67752a27e82a040ff7b64dca0783",
|
||||
"react-native-prompt-android": "https://github.com/BlueWallet/react-native-prompt-android#ed168d66fed556bc2ed07cf498770f058b78a376",
|
||||
"react-native-push-notification": "8.1.1",
|
||||
|
|
|
@ -271,6 +271,10 @@ export default class Selftest extends Component {
|
|||
}
|
||||
|
||||
//
|
||||
|
||||
assertStrictEqual(Buffer.from('00ff0f', 'hex').reverse().toString('hex'), '0fff00');
|
||||
|
||||
//
|
||||
} catch (Err) {
|
||||
errorMessage += Err;
|
||||
isOk = false;
|
||||
|
|
|
@ -18,8 +18,6 @@ import loc from '../../loc';
|
|||
import { FiatUnit, FiatUnitSource, FiatUnitType, getFiatRate } from '../../models/fiatUnit';
|
||||
dayjs.extend(require('dayjs/plugin/calendar'));
|
||||
|
||||
const ITEM_HEIGHT = 50;
|
||||
|
||||
const Currency = () => {
|
||||
const { setPreferredFiatCurrency } = useContext(BlueStorageContext);
|
||||
const [isSavingNewPreferredCurrency, setIsSavingNewPreferredCurrency] = useState(false);
|
||||
|
@ -62,17 +60,11 @@ const Currency = () => {
|
|||
fetchCurrency();
|
||||
}, [setOptions]);
|
||||
|
||||
const getItemLayout = (_data: any, index: number) => ({
|
||||
length: ITEM_HEIGHT,
|
||||
offset: ITEM_HEIGHT * index,
|
||||
index,
|
||||
});
|
||||
|
||||
const renderItem = ({ item }: { item: FiatUnitType }) => (
|
||||
<ListItem
|
||||
disabled={isSavingNewPreferredCurrency || selectedCurrency.endPointKey === item.endPointKey}
|
||||
title={`${item.endPointKey} (${item.symbol})`}
|
||||
containerStyle={StyleSheet.flatten([styles.flex, { height: ITEM_HEIGHT }])}
|
||||
containerStyle={StyleSheet.flatten([styles.flex, { minHeight: 60 }])}
|
||||
checkmark={selectedCurrency.endPointKey === item.endPointKey}
|
||||
onPress={async () => {
|
||||
setIsSavingNewPreferredCurrency(true);
|
||||
|
@ -104,7 +96,6 @@ const Currency = () => {
|
|||
data={data}
|
||||
initialNumToRender={30}
|
||||
extraData={data}
|
||||
getItemLayout={getItemLayout}
|
||||
renderItem={renderItem}
|
||||
/>
|
||||
<BlueCard>
|
||||
|
|
|
@ -285,6 +285,10 @@ const ViewEditMultisigCosigners = ({ route }: Props) => {
|
|||
const xpub = wallet.getCosigner(keyIndex);
|
||||
const fp = wallet.getFingerprint(keyIndex);
|
||||
const path = wallet.getCustomDerivationPathForCosigner(keyIndex);
|
||||
if (!path) {
|
||||
presentAlert({ message: 'Cannot find derivation path for this cosigner' });
|
||||
return;
|
||||
}
|
||||
setVaultKeyData({
|
||||
keyIndex,
|
||||
seed: '',
|
||||
|
@ -340,6 +344,10 @@ const ViewEditMultisigCosigners = ({ route }: Props) => {
|
|||
setIsMnemonicsModalVisible(true);
|
||||
const fp = wallet.getFingerprint(keyIndex);
|
||||
const path = wallet.getCustomDerivationPathForCosigner(keyIndex);
|
||||
if (!path) {
|
||||
presentAlert({ message: 'Cannot find derivation path for this cosigner' });
|
||||
return;
|
||||
}
|
||||
const xpub = wallet.convertXpubToMultisignatureXpub(MultisigHDWallet.seedToXpub(seed, path));
|
||||
setExportString(MultisigCosigner.exportToJson(fp, xpub, path));
|
||||
setExportStringURv2(encodeUR(MultisigCosigner.exportToJson(fp, xpub, path))[0]);
|
||||
|
|
|
@ -3,9 +3,10 @@ import * as bitcoin from 'bitcoinjs-lib';
|
|||
|
||||
import { HDLegacyBreadwalletWallet } from '../../class';
|
||||
import * as BlueElectrum from '../../blue_modules/BlueElectrum';
|
||||
import { AbstractHDElectrumWallet } from '../../class/wallets/abstract-hd-electrum-wallet';
|
||||
|
||||
jest.setTimeout(300 * 1000);
|
||||
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
afterAll(async () => {
|
||||
// after all tests we close socket so the test suite can actually terminate
|
||||
|
@ -19,7 +20,7 @@ beforeAll(async () => {
|
|||
await BlueElectrum.connectMain();
|
||||
});
|
||||
|
||||
it('Legacy HD Breadwallet can fetch balance and create transaction', async () => {
|
||||
it('Legacy HD Breadwallet can fetch utxo, balance, and create transaction', async () => {
|
||||
if (!process.env.HD_MNEMONIC_BREAD) {
|
||||
console.error('process.env.HD_MNEMONIC_BREAD not set, skipped');
|
||||
return;
|
||||
|
@ -38,13 +39,26 @@ it('Legacy HD Breadwallet can fetch balance and create transaction', async () =>
|
|||
|
||||
// try to create a tx
|
||||
await wallet.fetchUtxo();
|
||||
|
||||
for (const utxo of wallet.getUtxo()) {
|
||||
assert.ok(utxo.txhex);
|
||||
assert.ok(typeof utxo.vout !== 'undefined');
|
||||
assert.ok(utxo.txid);
|
||||
assert.ok(utxo.confirmations);
|
||||
assert.ok(utxo.value);
|
||||
}
|
||||
|
||||
const { tx } = wallet.createTransaction(
|
||||
wallet.getUtxo(),
|
||||
[{ address: 'bc1q47efz9aav8g4mnnz9r6ql4pf48phy3g509p7gx' }],
|
||||
1,
|
||||
'bc1qk9hvkxqsqmps6ex3qawr79rvtg8es4ecjfu5v0',
|
||||
AbstractHDElectrumWallet.defaultRBFSequence,
|
||||
false,
|
||||
0,
|
||||
);
|
||||
|
||||
assert.ok(tx);
|
||||
const transaction = bitcoin.Transaction.fromHex(tx.toHex());
|
||||
assert.ok(transaction.ins.length === 4);
|
||||
assert.strictEqual(transaction.outs.length, 1);
|
|
@ -59,12 +59,12 @@ it('HD (BIP49) can create TX', async () => {
|
|||
|
||||
await hd.fetchBalance();
|
||||
await hd.fetchUtxo();
|
||||
assert.ok(typeof hd.utxo[0].confirmations === 'number');
|
||||
assert.ok(hd.utxo[0].txid);
|
||||
assert.ok(hd.utxo[0].vout !== undefined);
|
||||
assert.ok(hd.utxo[0].amount);
|
||||
assert.ok(hd.utxo[0].address);
|
||||
assert.ok(hd.utxo[0].wif);
|
||||
assert.ok(typeof hd._utxo[0].confirmations === 'number');
|
||||
assert.ok(hd._utxo[0].txid);
|
||||
assert.ok(hd._utxo[0].vout !== undefined);
|
||||
assert.ok(hd._utxo[0].amount);
|
||||
assert.ok(hd._utxo[0].address);
|
||||
assert.ok(hd._utxo[0].wif);
|
||||
|
||||
let txNew = hd.createTransaction(
|
||||
hd.getUtxo(),
|
||||
|
@ -178,10 +178,10 @@ it('Segwit HD (BIP49) can fetch balance with many used addresses in hierarchy',
|
|||
assert.strictEqual(hd.getBalance(), 51432);
|
||||
|
||||
await hd.fetchUtxo();
|
||||
assert.ok(hd.utxo.length > 0);
|
||||
assert.ok(hd.utxo[0].txid);
|
||||
assert.ok(hd.utxo[0].vout === 0);
|
||||
assert.ok(hd.utxo[0].amount);
|
||||
assert.ok(hd._utxo.length > 0);
|
||||
assert.ok(hd._utxo[0].txid);
|
||||
assert.ok(hd._utxo[0].vout === 0);
|
||||
assert.ok(hd._utxo[0].amount);
|
||||
|
||||
await hd.fetchTransactions();
|
||||
assert.strictEqual(hd.getTransactions().length, 107);
|
||||
|
|
|
@ -32,6 +32,7 @@ describe('LegacyWallet', function () {
|
|||
w._address = '115fUy41sZkAG14CmdP1VbEKcNRZJWkUWG'; // hack internals
|
||||
assert.ok(w.weOwnAddress('115fUy41sZkAG14CmdP1VbEKcNRZJWkUWG'));
|
||||
assert.ok(!w.weOwnAddress('aaa'));
|
||||
// @ts-ignore wrong type on purpose
|
||||
assert.ok(!w.weOwnAddress(false));
|
||||
assert.ok(w.getBalance() === 0);
|
||||
assert.ok(w.getUnconfirmedBalance() === 0);
|
||||
|
@ -52,7 +53,7 @@ describe('LegacyWallet', function () {
|
|||
assert.ok(tx.hash);
|
||||
assert.ok(tx.value);
|
||||
assert.ok(tx.received);
|
||||
assert.ok(tx.confirmations > 1);
|
||||
assert.ok(tx.confirmations! > 1);
|
||||
}
|
||||
|
||||
assert.ok(w.weOwnTransaction('b2ac59bc282083498d1e87805d89bef9d3f3bc216c1d2c4dfaa2e2911b547100'));
|
||||
|
@ -66,7 +67,7 @@ describe('LegacyWallet', function () {
|
|||
assert.strictEqual(tx.address, '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK');
|
||||
assert.strictEqual(tx.value, 51432);
|
||||
assert.strictEqual(tx.value, tx.amount);
|
||||
assert.ok(tx.confirmations > 0);
|
||||
assert.ok(tx.confirmations! > 0);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -88,7 +89,7 @@ describe('LegacyWallet', function () {
|
|||
assert.ok(tx.hash);
|
||||
assert.ok(tx.value);
|
||||
assert.ok(tx.received);
|
||||
assert.ok(tx.confirmations > 1);
|
||||
assert.ok(tx.confirmations! > 1);
|
||||
}
|
||||
},
|
||||
240000,
|
||||
|
@ -98,13 +99,14 @@ describe('LegacyWallet', function () {
|
|||
const w = new LegacyWallet();
|
||||
w._address = '12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX';
|
||||
await w.fetchUtxo();
|
||||
assert.ok(w.utxo.length > 0, 'unexpected empty UTXO');
|
||||
assert.ok(w._utxo.length > 0, 'unexpected empty UTXO');
|
||||
assert.ok(w.getUtxo().length > 0, 'unexpected empty UTXO');
|
||||
|
||||
assert.ok(w.getUtxo()[0].value);
|
||||
assert.ok(w.getUtxo()[0].vout === 1, JSON.stringify(w.getUtxo()[0]));
|
||||
assert.ok(w.getUtxo()[0].txid);
|
||||
assert.ok(w.getUtxo()[0].confirmations);
|
||||
assert.ok(w.getUtxo()[0].txhex);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -116,6 +118,7 @@ describe('SegwitP2SHWallet', function () {
|
|||
assert.ok(l.getAddress() === (await l.getAddressAsync()));
|
||||
assert.ok(l.weOwnAddress('34AgLJhwXrvmkZS1o5TrcdeevMt22Nar53'));
|
||||
assert.ok(!l.weOwnAddress('garbage'));
|
||||
// @ts-ignore wrong type on purpose
|
||||
assert.ok(!l.weOwnAddress(false));
|
||||
});
|
||||
});
|
||||
|
@ -127,6 +130,7 @@ describe('SegwitBech32Wallet', function () {
|
|||
assert.ok(w.weOwnAddress('bc1q063ctu6jhe5k4v8ka99qac8rcm2tzjjnuktyrl'));
|
||||
assert.ok(w.weOwnAddress('BC1Q063CTU6JHE5K4V8KA99QAC8RCM2TZJJNUKTYRL'));
|
||||
assert.ok(!w.weOwnAddress('garbage'));
|
||||
// @ts-ignore wrong type on purpose
|
||||
assert.ok(!w.weOwnAddress(false));
|
||||
await w.fetchBalance();
|
||||
assert.strictEqual(w.getBalance(), 69909);
|
||||
|
@ -159,7 +163,7 @@ describe('SegwitBech32Wallet', function () {
|
|||
assert.ok(tx.hash);
|
||||
assert.ok(tx.value);
|
||||
assert.ok(tx.received);
|
||||
assert.ok(tx.confirmations > 1);
|
||||
assert.ok(tx.confirmations! > 1);
|
||||
}
|
||||
|
||||
assert.strictEqual(w.getTransactions()[0].value, -892111);
|
||||
|
@ -172,6 +176,7 @@ describe('SegwitBech32Wallet', function () {
|
|||
assert.ok(w.weOwnAddress('bc1qn887fmetaytw4vj68vsh529ft408q8j9x3dndc'));
|
||||
assert.ok(w.weOwnAddress('BC1QN887FMETAYTW4VJ68VSH529FT408Q8J9X3DNDC'));
|
||||
assert.ok(!w.weOwnAddress('garbage'));
|
||||
// @ts-ignore wrong type on purpose
|
||||
assert.ok(!w.weOwnAddress(false));
|
||||
await w.fetchTransactions();
|
||||
assert.strictEqual(w.getTransactions().length, 2);
|
||||
|
@ -179,7 +184,7 @@ describe('SegwitBech32Wallet', function () {
|
|||
assert.ok(tx.hash);
|
||||
assert.strictEqual(tx.value, 100000);
|
||||
assert.ok(tx.received);
|
||||
assert.ok(tx.confirmations > 1);
|
||||
assert.ok(tx.confirmations! > 1);
|
||||
|
||||
const tx0 = w.getTransactions()[0];
|
||||
assert.ok(tx0.inputs);
|
|
@ -1794,7 +1794,7 @@ describe('multisig-wallet (native segwit)', () => {
|
|||
);
|
||||
assert.strictEqual(w.getFingerprint(2), '2C0908B6');
|
||||
assert.strictEqual(w.getCustomDerivationPathForCosigner(2), path);
|
||||
assert.ok(!w.getPassphrase(2));
|
||||
assert.ok(!w.getCosignerPassphrase(2));
|
||||
|
||||
w.replaceCosignerXpubWithSeed(
|
||||
2,
|
||||
|
@ -1804,14 +1804,14 @@ describe('multisig-wallet (native segwit)', () => {
|
|||
assert.strictEqual(w.getCosigner(2), 'salon smoke bubble dolphin powder govern rival sport better arrest certain manual');
|
||||
assert.strictEqual(w.getFingerprint(2), '2C0908B6');
|
||||
assert.strictEqual(w.getCustomDerivationPathForCosigner(2), path);
|
||||
assert.strictEqual(w.getPassphrase(2), '9WDdFSZX4d6mPxkr');
|
||||
assert.strictEqual(w.getCosignerPassphrase(2), '9WDdFSZX4d6mPxkr');
|
||||
|
||||
// test that after deleting cosinger with passphrase, it has been cleaned out properly
|
||||
w.deleteCosigner('2C0908B6');
|
||||
assert.ok(!w.getCosigner(2));
|
||||
assert.ok(!w.getFingerprint(2));
|
||||
assert.ok(!w.getCustomDerivationPathForCosigner(2));
|
||||
assert.ok(!w.getPassphrase(2));
|
||||
assert.ok(!w.getCosignerPassphrase(2));
|
||||
assert.strictEqual(w.getN(), 1);
|
||||
assert.strictEqual(w.getM(), 2);
|
||||
|
||||
|
@ -1820,7 +1820,7 @@ describe('multisig-wallet (native segwit)', () => {
|
|||
assert.strictEqual(w.getCosigner(1), process.env.MNEMONICS_COBO);
|
||||
assert.strictEqual(w.getFingerprint(1), fp1cobo);
|
||||
assert.strictEqual(w.getCustomDerivationPathForCosigner(1), path);
|
||||
assert.strictEqual(w.getPassphrase(1), undefined);
|
||||
assert.strictEqual(w.getCosignerPassphrase(1), undefined);
|
||||
});
|
||||
|
||||
it('can sign valid tx if we have more keys than quorum ("Too many signatures" error)', async () => {
|
||||
|
@ -1967,9 +1967,9 @@ describe('multisig-wallet (native segwit)', () => {
|
|||
w.setDerivationPath(path);
|
||||
w.setM(2);
|
||||
|
||||
assert.strictEqual(w.getPassphrase(1), '9WDdFSZX4d6mPxkr');
|
||||
assert.strictEqual(w.getPassphrase(2), 'E5jMAzsf464Hgwns');
|
||||
assert.strictEqual(w.getPassphrase(3), 'RyBFfLr7weK3nDUG');
|
||||
assert.strictEqual(w.getCosignerPassphrase(1), '9WDdFSZX4d6mPxkr');
|
||||
assert.strictEqual(w.getCosignerPassphrase(2), 'E5jMAzsf464Hgwns');
|
||||
assert.strictEqual(w.getCosignerPassphrase(3), 'RyBFfLr7weK3nDUG');
|
||||
assert.strictEqual(w._getExternalAddressByIndex(0), 'bc1q8rks34ypj5edxx82f7z7yzy4qy6dynfhcftjs9axzr2ml37p4pfs7j4uvm');
|
||||
assert.strictEqual(w._getInternalAddressByIndex(0), 'bc1qjpjgumzs2afrr3mk85anwdnzd9qg5hc5p6f62un4umpyf4ccde5q4cywgy');
|
||||
|
||||
|
@ -1978,9 +1978,9 @@ describe('multisig-wallet (native segwit)', () => {
|
|||
|
||||
assert.strictEqual(w._getExternalAddressByIndex(0), w2._getExternalAddressByIndex(0));
|
||||
assert.strictEqual(w._getExternalAddressByIndex(1), w2._getExternalAddressByIndex(1));
|
||||
assert.strictEqual(w.getPassphrase(1), w2.getPassphrase(1));
|
||||
assert.strictEqual(w.getPassphrase(2), w2.getPassphrase(2));
|
||||
assert.strictEqual(w.getPassphrase(3), w2.getPassphrase(3));
|
||||
assert.strictEqual(w.getCosignerPassphrase(1), w2.getCosignerPassphrase(1));
|
||||
assert.strictEqual(w.getCosignerPassphrase(2), w2.getCosignerPassphrase(2));
|
||||
assert.strictEqual(w.getCosignerPassphrase(3), w2.getCosignerPassphrase(3));
|
||||
});
|
||||
|
||||
it('can import descriptor from Sparrow', () => {
|
||||
|
@ -1988,7 +1988,6 @@ describe('multisig-wallet (native segwit)', () => {
|
|||
'UR:CRYPTO-OUTPUT/TAADMETAADMSOEADAOAOLSTAADDLOLAOWKAXHDCLAOCEBDFLNNTKJTIOJSFSURBNFXRPEEHKDLGYRTEMRPYTGYZOCASWENCYMKPAVWJKHYAAHDCXJEFTGSZOIMFEYNDYHYZEJTBAMSJEHLDSRDDIYLSRFYTSZTKNRNYLRNDPAMTLDPZCAHTAADEHOEADAEAOAEAMTAADDYOTADLOCSDYYKAEYKAEYKAOYKAOCYUOHFJPKOAXAAAYCYCSYASAVDTAADDLOLAOWKAXHDCLAXMSZTWZDIGERYDKFSFWTYDPFNDKLNAYSWTTMUHYZTOXHSETPEWSFXPEAYWLJSDEMTAAHDCXSPLTSTDPNTLESANSUTTLPRPFHNVSPFCNMHESOYGASTLRPYVAATNNDKFYHLQZPKLEAHTAADEHOEADAEAOAEAMTAADDYOTADLOCSDYYKAEYKAEYKAOYKAOCYWZFEPLETAXAAAYCYCPCKRENBTAADDLOLAOWKAXHDCLAOLSFWYKYLKTFHJLPYEMGLCEDPFNSNRDDSRFASEOZTGWIALFLUIYDNFXHGVESFEMMEAAHDCXHTZETLJNKPHHAYLSCXWPNDSWPSTPGTEOJKKGHDAELSKPNNBKBSYAWZJTFWNNBDKTAHTAADEHOEADAEAOAEAMTAADDYOTADLOCSDYYKAEYKAEYKAOYKAOCYSKTPJPMSAXAAAYCYCEBKWLAMTDWZGRZE\n';
|
||||
const decoder = new BlueURDecoder();
|
||||
decoder.receivePart(payload);
|
||||
console.log(decoder.isComplete());
|
||||
|
||||
const data = decoder.toString();
|
||||
|
||||
|
|
2
typings/coinselect.d.ts
vendored
2
typings/coinselect.d.ts
vendored
|
@ -32,7 +32,7 @@ declare module 'coinselect' {
|
|||
};
|
||||
|
||||
export type CoinSelectOutput = {
|
||||
address?: string;
|
||||
address?: string; // if output has no address - this is a change output
|
||||
value: number;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue