diff --git a/Navigation.js b/Navigation.js
index 92b9ee3f6..5bceb67dc 100644
--- a/Navigation.js
+++ b/Navigation.js
@@ -470,7 +470,7 @@ const PaymentCodeStack = createNativeStackNavigator();
const PaymentCodeStackRoot = () => {
return (
-
+
);
};
diff --git a/blue_modules/BlueElectrum.d.ts b/blue_modules/BlueElectrum.d.ts
index 7d4e066d5..89689fa83 100644
--- a/blue_modules/BlueElectrum.d.ts
+++ b/blue_modules/BlueElectrum.d.ts
@@ -96,3 +96,5 @@ export function getTransactionsFullByAddress(address: string): Promise;
+
+export function multiGetTransactionHexByTxid(txids: string[], batchsize?: number): Promise>;
diff --git a/blue_modules/BlueElectrum.js b/blue_modules/BlueElectrum.js
index 7717c0a3f..3ee0947f0 100644
--- a/blue_modules/BlueElectrum.js
+++ b/blue_modules/BlueElectrum.js
@@ -1053,3 +1053,24 @@ function txhexToElectrumTransaction(txhex) {
// exported only to be used in unit tests
module.exports.txhexToElectrumTransaction = txhexToElectrumTransaction;
+
+module.exports.multiGetTransactionHexByTxid = async function (txids, batchsize = 100) {
+ if (!mainClient) throw new Error('Electrum client is not connected');
+
+ const chunks = splitIntoChunks(txids, batchsize);
+ const result = {};
+ for (const chunk of chunks) {
+ if (!disableBatching) {
+ await Promise.all(
+ chunk.map(async txid => {
+ const hex = await mainClient.blockchainTransaction_get(txid);
+ result[txid] = hex;
+ }),
+ );
+ } else {
+ const res = await mainClient.blockchainTransaction_getBatch(chunk);
+ res.forEach(({ result: r, param }) => (result[param] = r));
+ }
+ }
+ return result;
+};
diff --git a/blue_modules/storage-context.js b/blue_modules/storage-context.js
index f27860622..f7f413c46 100644
--- a/blue_modules/storage-context.js
+++ b/blue_modules/storage-context.js
@@ -120,6 +120,10 @@ export const BlueStorageProvider = ({ children }) => {
setWalletTransactionUpdateStatus(WalletTransactionsStatus.ALL);
}
await BlueElectrum.waitTillConnected();
+ const paymentCodesStart = Date.now();
+ await fetchSenderPaymentCodes(lastSnappedTo);
+ const paymentCodesEnd = Date.now();
+ console.log('fetch payment codes took', (paymentCodesEnd - paymentCodesStart) / 1000, 'sec');
const balanceStart = +new Date();
await fetchWalletBalances(lastSnappedTo);
const balanceEnd = +new Date();
@@ -201,6 +205,7 @@ export const BlueStorageProvider = ({ children }) => {
const getTransactions = BlueApp.getTransactions;
const isAdancedModeEnabled = BlueApp.isAdancedModeEnabled;
+ const fetchSenderPaymentCodes = BlueApp.fetchSenderPaymentCodes;
const fetchWalletBalances = BlueApp.fetchWalletBalances;
const fetchWalletTransactions = BlueApp.fetchWalletTransactions;
const getBalance = BlueApp.getBalance;
diff --git a/class/app-storage.js b/class/app-storage.js
index 43e720075..039e0a778 100644
--- a/class/app-storage.js
+++ b/class/app-storage.js
@@ -725,6 +725,17 @@ export class AppStorage {
}
};
+ fetchSenderPaymentCodes = async index => {
+ console.log('fetchSenderPaymentCodes for wallet#', typeof index === 'undefined' ? '(all)' : index);
+ if (index || index === 0) {
+ this.wallets[index].fetchBIP47SenderPaymentCodes();
+ } else {
+ for (const wallet of this.wallets) {
+ wallet.fetchBIP47SenderPaymentCodes();
+ }
+ }
+ };
+
/**
*
* @returns {Array.}
diff --git a/class/wallets/abstract-hd-electrum-wallet.ts b/class/wallets/abstract-hd-electrum-wallet.ts
index 817efbb92..493d6352f 100644
--- a/class/wallets/abstract-hd-electrum-wallet.ts
+++ b/class/wallets/abstract-hd-electrum-wallet.ts
@@ -4,6 +4,7 @@ import BigNumber from 'bignumber.js';
import b58 from 'bs58check';
import BIP32Factory, { BIP32Interface } from 'bip32';
import * as ecc from 'tiny-secp256k1';
+import BIP47Factory, { BIP47Interface } from 'bip47';
import { randomBytes } from '../rng';
import { AbstractHDWallet } from './abstract-hd-wallet';
@@ -20,6 +21,7 @@ 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);
type BalanceByIndex = {
c: number;
@@ -45,6 +47,16 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
_utxo: any[];
+ // BIP47
+ _enable_BIP47: boolean;
+ _payment_code: string;
+ _sender_payment_codes: string[];
+ addresses_by_payment_code: Record;
+ next_free_payment_code_address_index: Record;
+ _txs_by_payment_code_index: Record;
+ _balances_by_payment_code_index: Record;
+ bip47_instance?: BIP47Interface;
+
constructor() {
super();
this._balances_by_external_index = {}; // 0 => { c: 0, u: 0 } // confirmed/unconfirmed
@@ -54,6 +66,15 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
this._txs_by_internal_index = {};
this._utxo = [];
+
+ // BIP47
+ this._enable_BIP47 = false;
+ this._payment_code = '';
+ this._sender_payment_codes = [];
+ this.next_free_payment_code_address_index = {};
+ this._txs_by_payment_code_index = {};
+ this._balances_by_payment_code_index = {};
+ this.addresses_by_payment_code = {};
}
/**
@@ -67,6 +88,9 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
for (const bal of Object.values(this._balances_by_internal_index)) {
ret += bal.c;
}
+ for (const pc of this._sender_payment_codes) {
+ ret += this._getBalancesByPaymentCodeIndex(pc).c;
+ }
return ret + (this.getUnconfirmedBalance() < 0 ? this.getUnconfirmedBalance() : 0);
}
@@ -82,20 +106,23 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
for (const bal of Object.values(this._balances_by_internal_index)) {
ret += bal.u;
}
+ for (const pc of this._sender_payment_codes) {
+ ret += this._getBalancesByPaymentCodeIndex(pc).u;
+ }
return ret;
}
async generate() {
const buf = await randomBytes(16);
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
- this.setPaymentCode();
+ this.setBIP47PaymentCode();
}
async generateFromEntropy(user: Buffer) {
const random = await randomBytes(user.length < 32 ? 32 - user.length : 0);
const buf = Buffer.concat([user, random], 32);
this.secret = bip39.entropyToMnemonic(buf.toString('hex'));
- this.setPaymentCode();
+ this.setBIP47PaymentCode();
}
_getExternalWIFByIndex(index: number): string | false {
@@ -268,6 +295,21 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
}
}
+ // next, bip47 addresses
+ for (const pc of this._sender_payment_codes) {
+ for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 2; c++) {
+ let hasUnconfirmed = false;
+ this._txs_by_payment_code_index[pc] = this._txs_by_payment_code_index[pc] || {};
+ this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c] || [];
+ for (const tx of this._txs_by_payment_code_index[pc][c])
+ hasUnconfirmed = hasUnconfirmed || !tx.confirmations || tx.confirmations < 7;
+
+ if (hasUnconfirmed || this._txs_by_payment_code_index[pc][c].length === 0 || this._balances_by_payment_code_index[pc].u !== 0) {
+ addresses2fetch.push(this._getBIP47Address(pc, c));
+ }
+ }
+ }
+
// first: batch fetch for all addresses histories
const histories = await BlueElectrum.multiGetHistoryByAddress(addresses2fetch);
const txs: Record = {};
@@ -314,6 +356,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
this._txs_by_internal_index[c] = this._txs_by_internal_index[c].filter(tx => !!tx.confirmations);
}
+ for (const pc of this._sender_payment_codes) {
+ for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 2; c++) {
+ this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c].filter(tx => !!tx.confirmations);
+ }
+ }
// now, we need to put transactions in all relevant `cells` of internal hashmaps: this._txs_by_internal_index && this._txs_by_external_index
@@ -399,6 +446,51 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
}
}
+ for (const pc of this._sender_payment_codes) {
+ for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 2; c++) {
+ for (const tx of Object.values(txdatas)) {
+ for (const vin of tx.vin) {
+ if (vin.addresses && vin.addresses.indexOf(this._getBIP47Address(pc, c)) !== -1) {
+ // this TX is related to our address
+ this._txs_by_payment_code_index[pc] = this._txs_by_payment_code_index[pc] || {};
+ this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c] || [];
+ const { vin: txVin, vout: txVout, ...txRest } = tx;
+ const clonedTx = { ...txRest, inputs: txVin.slice(0), outputs: txVout.slice(0) };
+
+ // trying to replace tx if it exists already (because it has lower confirmations, for example)
+ let replaced = false;
+ for (let cc = 0; cc < this._txs_by_payment_code_index[pc][c].length; cc++) {
+ if (this._txs_by_payment_code_index[pc][c][cc].txid === clonedTx.txid) {
+ replaced = true;
+ this._txs_by_payment_code_index[pc][c][cc] = clonedTx;
+ }
+ }
+ if (!replaced) this._txs_by_payment_code_index[pc][c].push(clonedTx);
+ }
+ }
+ for (const vout of tx.vout) {
+ if (vout.scriptPubKey.addresses && vout.scriptPubKey.addresses.indexOf(this._getBIP47Address(pc, c)) !== -1) {
+ // this TX is related to our address
+ this._txs_by_payment_code_index[pc] = this._txs_by_payment_code_index[pc] || {};
+ this._txs_by_payment_code_index[pc][c] = this._txs_by_payment_code_index[pc][c] || [];
+ const { vin: txVin, vout: txVout, ...txRest } = tx;
+ const clonedTx = { ...txRest, inputs: txVin.slice(0), outputs: txVout.slice(0) };
+
+ // trying to replace tx if it exists already (because it has lower confirmations, for example)
+ let replaced = false;
+ for (let cc = 0; cc < this._txs_by_internal_index[c].length; cc++) {
+ if (this._txs_by_internal_index[c][cc].txid === clonedTx.txid) {
+ replaced = true;
+ this._txs_by_internal_index[c][cc] = clonedTx;
+ }
+ }
+ if (!replaced) this._txs_by_internal_index[c].push(clonedTx);
+ }
+ }
+ }
+ }
+ }
+
this._lastTxFetch = +new Date();
}
@@ -411,6 +503,14 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
for (const addressTxs of Object.values(this._txs_by_internal_index)) {
txs = txs.concat(addressTxs);
}
+ if (this._sender_payment_codes) {
+ for (const pc of this._sender_payment_codes) {
+ if (this._txs_by_payment_code_index[pc])
+ for (const addressTxs of Object.values(this._txs_by_payment_code_index[pc])) {
+ txs = txs.concat(addressTxs);
+ }
+ }
+ }
if (txs.length === 0) return []; // guard clause; so we wont spend time calculating addresses
@@ -552,12 +652,60 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
return lastUsedIndex;
}
+ async _binarySearchIterationForBIP47Address(paymentCode: string, index: number) {
+ const generateChunkAddresses = (chunkNum: number) => {
+ const ret = [];
+ for (let c = this.gap_limit * chunkNum; c < this.gap_limit * (chunkNum + 1); c++) {
+ ret.push(this._getBIP47Address(paymentCode, c));
+ }
+ return ret;
+ };
+
+ let lastChunkWithUsedAddressesNum = null;
+ let lastHistoriesWithUsedAddresses = null;
+ for (let c = 0; c < Math.round(index / this.gap_limit); c++) {
+ const histories = await BlueElectrum.multiGetHistoryByAddress(generateChunkAddresses(c));
+ // @ts-ignore
+ if (this.constructor._getTransactionsFromHistories(histories).length > 0) {
+ // in this particular chunk we have used addresses
+ lastChunkWithUsedAddressesNum = c;
+ lastHistoriesWithUsedAddresses = histories;
+ } else {
+ // empty chunk. no sense searching more chunks
+ break;
+ }
+ }
+
+ let lastUsedIndex = 0;
+
+ if (lastHistoriesWithUsedAddresses) {
+ // now searching for last used address in batch lastChunkWithUsedAddressesNum
+ for (
+ let c = Number(lastChunkWithUsedAddressesNum) * this.gap_limit;
+ c < Number(lastChunkWithUsedAddressesNum) * this.gap_limit + this.gap_limit;
+ c++
+ ) {
+ const address = this._getBIP47Address(paymentCode, c);
+ if (lastHistoriesWithUsedAddresses[address] && lastHistoriesWithUsedAddresses[address].length > 0) {
+ lastUsedIndex = Math.max(c, lastUsedIndex) + 1; // point to next, which is supposed to be unsued
+ }
+ }
+ }
+
+ return lastUsedIndex;
+ }
+
async fetchBalance() {
try {
if (this.next_free_change_address_index === 0 && this.next_free_address_index === 0) {
// doing binary search for last used address:
this.next_free_change_address_index = await this._binarySearchIterationForInternalAddress(1000);
this.next_free_address_index = await this._binarySearchIterationForExternalAddress(1000);
+ if (this._sender_payment_codes) {
+ for (const pc of this._sender_payment_codes) {
+ this.next_free_payment_code_address_index[pc] = await this._binarySearchIterationForBIP47Address(pc, 10);
+ }
+ }
} // end rescanning fresh wallet
// finally fetching balance
@@ -579,6 +727,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
for (let c = this.next_free_change_address_index; c < this.next_free_change_address_index + this.gap_limit; c++) {
lagAddressesToFetch.push(this._getInternalAddressByIndex(c));
}
+ for (const pc in this._sender_payment_codes) {
+ for (let c = this.next_free_payment_code_address_index[pc]; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
+ lagAddressesToFetch.push(this._getBIP47Address(pc, c));
+ }
+ }
const txs = await BlueElectrum.multiGetHistoryByAddress(lagAddressesToFetch); // <------ electrum call
@@ -598,6 +751,16 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
}
}
+ for (const pc in this._sender_payment_codes) {
+ for (let c = this.next_free_payment_code_address_index[pc]; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
+ const address = this._getBIP47Address(pc, c);
+ if (txs[address] && Array.isArray(txs[address]) && txs[address].length > 0) {
+ // whoa, someone uses our wallet outside! better catch up
+ this.next_free_payment_code_address_index[pc] = c + 1;
+ }
+ }
+ }
+
// next, business as usuall. fetch balances
const addresses2fetch = [];
@@ -616,6 +779,12 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
addresses2fetch.push(this._getInternalAddressByIndex(c));
}
+ for (const pc in this._sender_payment_codes) {
+ for (let c = this.next_free_payment_code_address_index[pc]; c < this.next_free_payment_code_address_index[pc] + this.gap_limit; c++) {
+ addresses2fetch.push(this._getBIP47Address(pc, c));
+ }
+ }
+
const balances = await BlueElectrum.multiGetBalanceByAddress(addresses2fetch);
// converting to a more compact internal format
@@ -660,6 +829,29 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
}
}
+ for (const pc of this._sender_payment_codes) {
+ for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 2; c++) {
+ const addr = this._getBIP47Address(pc, c);
+ if (balances.addresses[addr]) {
+ // first, if balances differ from what we store - we delete transactions for that
+ // address so next fetchTransactions() will refetch everything
+ if (this._getBalancesByPaymentCodeIndex(pc).c) {
+ if (
+ this._getBalancesByPaymentCodeIndex(pc).c !== balances.addresses[addr].confirmed ||
+ this._getBalancesByPaymentCodeIndex(pc).u !== balances.addresses[addr].unconfirmed
+ ) {
+ delete this._txs_by_payment_code_index[pc];
+ }
+ }
+ // update local representation of balances on that address:
+ this._balances_by_payment_code_index[pc] = {
+ c: balances.addresses[addr].confirmed,
+ u: balances.addresses[addr].unconfirmed,
+ };
+ }
+ }
+ }
+
this._lastBalanceFetch = +new Date();
}
@@ -758,6 +950,11 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
for (let c = 0; c < this.next_free_change_address_index + 1; c++) {
ownedAddressesHashmap[this._getInternalAddressByIndex(c)] = true;
}
+ for (const pc of this._sender_payment_codes) {
+ for (let c = 0; c < this._getNextFreePaymentCodeAddress(pc) + 1; c++) {
+ ownedAddressesHashmap[this._getBIP47Address(pc, c)] = true;
+ }
+ }
for (const tx of this.getTransactions()) {
for (const output of tx.outputs) {
@@ -1194,4 +1391,101 @@ export class AbstractHDElectrumWallet extends AbstractHDWallet {
const seed = this._getSeed();
return AbstractHDElectrumWallet.seedToFingerprint(seed);
}
+
+ // Same as that in AbstractHDWallet, but also sets the BIP47 payment code
+ setSecret(newSecret: string): this {
+ this.secret = newSecret.trim().toLowerCase();
+ this.secret = this.secret.replace(/[^a-zA-Z0-9]/g, ' ').replace(/\s+/g, ' ');
+
+ // Try to match words to the default bip39 wordlist and complete partial words
+ const wordlist = bip39.wordlists[bip39.getDefaultWordlist()];
+ const lookupMap = wordlist.reduce((map, word) => {
+ const prefix3 = word.substr(0, 3);
+ const prefix4 = word.substr(0, 4);
+
+ map.set(prefix3, !map.has(prefix3) ? word : false);
+ map.set(prefix4, !map.has(prefix4) ? word : false);
+
+ return map;
+ }, new Map());
+
+ this.secret = this.secret
+ .split(' ')
+ .map(word => lookupMap.get(word) || word)
+ .join(' ');
+
+ this.setBIP47PaymentCode();
+
+ return this;
+ }
+
+ /**
+ * Whether BIP47 is enabled
+ * @returns boolean
+ */
+ isBIP47Enabled(): boolean {
+ return this._enable_BIP47;
+ }
+
+ switchBIP47(value: boolean): void {
+ this._enable_BIP47 = value;
+ }
+
+ getBIP47FromSeed(): BIP47Interface {
+ if (!this.bip47_instance) this.bip47_instance = bip47.fromBip39Seed(this.secret, undefined, this.passphrase);
+ return this.bip47_instance;
+ }
+
+ setBIP47PaymentCode(): void {
+ this._payment_code = this.getBIP47FromSeed().getSerializedPaymentCode();
+ }
+
+ getBIP47PaymentCode(): string {
+ return this._payment_code;
+ }
+
+ getBIP47NotificationAddress(): string {
+ return this.getBIP47FromSeed().getNotificationAddress();
+ }
+
+ async fetchBIP47SenderPaymentCodes() {
+ const bip47 = this.getBIP47FromSeed();
+ const address = bip47.getNotificationAddress();
+
+ const histories = await BlueElectrum.multiGetHistoryByAddress([address]);
+ const txHashes = histories[address].map(({ tx_hash }) => tx_hash);
+
+ const txHexs = await BlueElectrum.multiGetTransactionHexByTxid(txHashes);
+ this._sender_payment_codes = Object.values(txHexs).map(str => {
+ return bip47.getPaymentCodeFromRawNotificationTransaction(str);
+ });
+ }
+
+ getBIP47SenderPaymentCodes(): string[] {
+ return this._sender_payment_codes;
+ }
+
+ _getBIP47Address(paymentCode: string, index: number) {
+ if (!this.addresses_by_payment_code[paymentCode]) this.addresses_by_payment_code[paymentCode] = [];
+
+ if (this.addresses_by_payment_code[paymentCode][index]) {
+ return this.addresses_by_payment_code[paymentCode][index];
+ }
+
+ const bip47_instance = this.getBIP47FromSeed();
+ const senderBIP47_instance = bip47.fromPaymentCode(paymentCode);
+ const remotePaymentNode = senderBIP47_instance.getPaymentCodeNode();
+ const hdNode = bip47_instance.getPaymentWallet(remotePaymentNode, index);
+ const address = bip47_instance.getAddressFromNode(hdNode, bip47_instance.network);
+ this.addresses_by_payment_code[paymentCode][index] = address;
+ return address;
+ }
+
+ _getNextFreePaymentCodeAddress(paymentCode: string) {
+ return this.next_free_payment_code_address_index[paymentCode] || 0;
+ }
+
+ _getBalancesByPaymentCodeIndex(paymentCode: string): BalanceByIndex {
+ return this._balances_by_payment_code_index[paymentCode] || { c: 0, u: 0 };
+ }
}
diff --git a/class/wallets/abstract-wallet.ts b/class/wallets/abstract-wallet.ts
index 02d6e8f93..d86381bb6 100644
--- a/class/wallets/abstract-wallet.ts
+++ b/class/wallets/abstract-wallet.ts
@@ -2,12 +2,6 @@ import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
import b58 from 'bs58check';
import createHash from 'create-hash';
import { CreateTransactionResult, CreateTransactionUtxo, Transaction, Utxo } from './types';
-import BIP47Factory from 'bip47';
-import * as ecc from 'tiny-secp256k1';
-import ECPairFactory from 'ecpair';
-
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
-const ECPair = ECPairFactory(ecc);
type WalletStatics = {
type: string;
@@ -57,8 +51,6 @@ export class AbstractWallet {
_utxoMetadata: Record;
use_with_hardware_wallet: boolean; // eslint-disable-line camelcase
masterFingerprint: number | false;
- _enableBIP47: boolean;
- paymentCode: string;
constructor() {
const Constructor = this.constructor as unknown as WalletStatics;
@@ -82,8 +74,6 @@ export class AbstractWallet {
this._utxoMetadata = {};
this.use_with_hardware_wallet = false;
this.masterFingerprint = false;
- this._enableBIP47 = false;
- this.paymentCode = '';
}
/**
@@ -121,22 +111,6 @@ export class AbstractWallet {
this._hideTransactionsInWalletsList = value;
}
- /**
- * Whether BIP47 is enabled
- * @returns true/false
- */
- getBIP47(): boolean {
- return this._enableBIP47;
- }
-
- setBIP47(value: boolean): void {
- this._enableBIP47 = value;
- }
-
- setPaymentCode(): void {
- this.paymentCode = BIP47Factory(ecc).fromBip39Seed(this.secret).getSerializedPaymentCode();
- }
-
/**
*
* @returns {string}
diff --git a/components/TransactionsNavigationHeader.js b/components/TransactionsNavigationHeader.js
index 26dd3c511..89d38214c 100644
--- a/components/TransactionsNavigationHeader.js
+++ b/components/TransactionsNavigationHeader.js
@@ -244,13 +244,13 @@ export default class TransactionsNavigationHeader extends Component {
{loc.lnd.title}
)}
- {this.state.wallet.getBIP47() && (
+ {this.state.wallet.isBIP47Enabled() && (
{
this.props.navigation.navigate('PaymentCodeRoot', {
screen: 'PaymentCode',
- params: { paymentCode: this.state.wallet.paymentCode },
+ params: { paymentCode: this.state.wallet.getBIP47PaymentCode() },
});
}}
>
diff --git a/package-lock.json b/package-lock.json
index 077159688..72b8eab15 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31,7 +31,7 @@
"bip32": "3.0.1",
"bip38": "github:BlueWallet/bip38",
"bip39": "3.0.4",
- "bip47": "github:abhiShandy/bip47#bluewallet",
+ "bip47": "github:abhiShandy/bip47#9a52740670a683850612406398c66ad2c2482a5f",
"bitcoinjs-lib": "6.0.2",
"bitcoinjs-message": "2.2.0",
"bolt11": "1.3.4",
@@ -7839,8 +7839,9 @@
},
"node_modules/bip47": {
"name": "@spsina/bip47",
- "version": "1.0.0",
- "resolved": "git+ssh://git@github.com/abhiShandy/bip47.git#d75b4089310f2884a4610f0586ada8a6c223581f",
+ "version": "1.0.1",
+ "resolved": "git+ssh://git@github.com/abhiShandy/bip47.git#9a52740670a683850612406398c66ad2c2482a5f",
+ "integrity": "sha512-lsgEpiEMDgpiYOA2kizOwiSS3vjTeLe2VnkOTIGnJ7Eu7Mkgl9dLES7oSLAjY64aQXr0VolqCRciRDc2nAC++w==",
"license": "MIT",
"dependencies": {
"bip32": "^3.0.1",
@@ -7849,23 +7850,12 @@
"bs58check": "^2.1.1",
"create-hmac": "^1.1.7",
"ecpair": "^2.0.1",
- "tiny-secp256k1": "^2.2.1"
+ "tiny-secp256k1": "^1.1.6"
},
"engines": {
"node": ">=6.0.0"
}
},
- "node_modules/bip47/node_modules/tiny-secp256k1": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.1.tgz",
- "integrity": "sha512-/U4xfVqnVxJXN4YVsru0E6t5wVncu2uunB8+RVR40fYUxkKYUPS10f+ePQZgFBoE/Jbf9H1NBveupF2VmB58Ng==",
- "dependencies": {
- "uint8array-tools": "0.0.7"
- },
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/bip66": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
@@ -26313,14 +26303,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/uint8array-tools": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz",
- "integrity": "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ==",
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/ultron": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
@@ -33019,8 +33001,9 @@
}
},
"bip47": {
- "version": "git+ssh://git@github.com/abhiShandy/bip47.git#d75b4089310f2884a4610f0586ada8a6c223581f",
- "from": "bip47@github:abhiShandy/bip47#bluewallet",
+ "version": "git+ssh://git@github.com/abhiShandy/bip47.git#9a52740670a683850612406398c66ad2c2482a5f",
+ "integrity": "sha512-lsgEpiEMDgpiYOA2kizOwiSS3vjTeLe2VnkOTIGnJ7Eu7Mkgl9dLES7oSLAjY64aQXr0VolqCRciRDc2nAC++w==",
+ "from": "bip47@github:abhiShandy/bip47#9a52740670a683850612406398c66ad2c2482a5f",
"requires": {
"bip32": "^3.0.1",
"bip39": "^3.0.4",
@@ -33028,17 +33011,7 @@
"bs58check": "^2.1.1",
"create-hmac": "^1.1.7",
"ecpair": "^2.0.1",
- "tiny-secp256k1": "^2.2.1"
- },
- "dependencies": {
- "tiny-secp256k1": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-2.2.1.tgz",
- "integrity": "sha512-/U4xfVqnVxJXN4YVsru0E6t5wVncu2uunB8+RVR40fYUxkKYUPS10f+ePQZgFBoE/Jbf9H1NBveupF2VmB58Ng==",
- "requires": {
- "uint8array-tools": "0.0.7"
- }
- }
+ "tiny-secp256k1": "^1.1.6"
}
},
"bip66": {
@@ -47383,11 +47356,6 @@
}
}
},
- "uint8array-tools": {
- "version": "0.0.7",
- "resolved": "https://registry.npmjs.org/uint8array-tools/-/uint8array-tools-0.0.7.tgz",
- "integrity": "sha512-vrrNZJiusLWoFWBqz5Y5KMCgP9W9hnjZHzZiZRT8oNAkq3d5Z5Oe76jAvVVSRh4U8GGR90N2X1dWtrhvx6L8UQ=="
- },
"ultron": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
diff --git a/package.json b/package.json
index af299e15f..8406d3513 100644
--- a/package.json
+++ b/package.json
@@ -117,7 +117,7 @@
"bip32": "3.0.1",
"bip38": "github:BlueWallet/bip38",
"bip39": "3.0.4",
- "bip47": "github:abhiShandy/bip47#bluewallet",
+ "bip47": "github:abhiShandy/bip47#9a52740670a683850612406398c66ad2c2482a5f",
"bitcoinjs-lib": "6.0.2",
"bitcoinjs-message": "2.2.0",
"bolt11": "1.3.4",
diff --git a/screen/wallets/details.js b/screen/wallets/details.js
index ae9f505fa..f31a93114 100644
--- a/screen/wallets/details.js
+++ b/screen/wallets/details.js
@@ -128,7 +128,7 @@ const WalletDetails = () => {
const [useWithHardwareWallet, setUseWithHardwareWallet] = useState(wallet.useWithHardwareWalletEnabled());
const { isAdancedModeEnabled } = useContext(BlueStorageContext);
const [isAdvancedModeEnabledRender, setIsAdvancedModeEnabledRender] = useState(false);
- const [isBIP47Enabled, setIsBIP47Enabled] = useState(wallet.getBIP47());
+ const [isBIP47Enabled, setIsBIP47Enabled] = useState(wallet.isBIP47Enabled());
const [hideTransactionsInWalletsList, setHideTransactionsInWalletsList] = useState(!wallet.getHideTransactionsInWalletsList());
const { goBack, navigate, setOptions, popToTop } = useNavigation();
const { colors } = useTheme();
@@ -188,7 +188,7 @@ const WalletDetails = () => {
wallet.setUseWithHardwareWalletEnabled(useWithHardwareWallet);
}
wallet.setHideTransactionsInWalletsList(!hideTransactionsInWalletsList);
- wallet.setBIP47(isBIP47Enabled);
+ wallet.switchBIP47(isBIP47Enabled);
}
saveToDisk()
.then(() => {
diff --git a/screen/wallets/paymentCode.tsx b/screen/wallets/paymentCode.tsx
index 019246db7..99c918557 100644
--- a/screen/wallets/paymentCode.tsx
+++ b/screen/wallets/paymentCode.tsx
@@ -12,8 +12,13 @@ export default function PaymentCode({ route }: NativeStackScreenProps
-
- {paymentCode}
+ {!paymentCode && Payment code not found}
+ {paymentCode && (
+ <>
+
+ {paymentCode}
+ >
+ )}
);
}
diff --git a/screen/wallets/transactions.js b/screen/wallets/transactions.js
index 71b8a9d09..e0c5234d9 100644
--- a/screen/wallets/transactions.js
+++ b/screen/wallets/transactions.js
@@ -179,6 +179,7 @@ const WalletTransactions = ({ navigation }) => {
// await BlueElectrum.ping();
await BlueElectrum.waitTillConnected();
/** @type {LegacyWallet} */
+ await wallet.fetchBIP47SenderPaymentCodes();
const balanceStart = +new Date();
const oldBalance = wallet.getBalance();
await wallet.fetchBalance();
diff --git a/tests/setup.js b/tests/setup.js
index 6a6014985..bd18f3a92 100644
--- a/tests/setup.js
+++ b/tests/setup.js
@@ -4,7 +4,7 @@ import mockClipboard from '@react-native-clipboard/clipboard/jest/clipboard-mock
const consoleWarnOrig = console.warn;
console.warn = (...args) => {
- if (!args[0].startsWith('WARNING: Sending to a future segwit version address can lead to loss of funds')) {
+ if (typeof args[0] === 'string' && !args[0].startsWith('WARNING: Sending to a future segwit version address can lead to loss of funds')) {
consoleWarnOrig.apply(consoleWarnOrig, args);
}
};