From 95ff8c2824907939e3bcb6bc5593c7b1ec9ca994 Mon Sep 17 00:00:00 2001 From: Ivan Vershigora Date: Wed, 20 Nov 2024 11:52:56 +0000 Subject: [PATCH] fix: async beignet wallet --- package.json | 2 +- src/AppOnboarded.tsx | 12 ++-- src/hooks/wallet.ts | 23 ++++++++ src/screens/Activity/ActivityDetail.tsx | 15 ++++- src/screens/Settings/GapLimit/index.tsx | 4 +- src/store/actions/wallet.ts | 46 +++++++-------- src/store/utils/activity.ts | 2 +- src/store/utils/fees.ts | 4 +- src/utils/boost.ts | 42 ++++++++------ src/utils/ledger.ts | 4 +- src/utils/lightning/index.ts | 12 ++-- src/utils/wallet/electrum.ts | 41 +++++-------- src/utils/wallet/index.ts | 76 ++++++++++++++++++------- src/utils/wallet/transactions.ts | 12 ++-- src/utils/wallet/transfer.ts | 13 ++++- yarn.lock | 10 ++-- 16 files changed, 191 insertions(+), 127 deletions(-) diff --git a/package.json b/package.json index 045ac27ab..1f7817005 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@synonymdev/slashtags-widget-price-feed": "1.1.0", "@synonymdev/web-relay": "1.0.7", "bech32": "2.0.0", - "beignet": "0.0.46", + "beignet": "0.0.47", "bip21": "2.0.3", "bip32": "4.0.0", "bip39": "3.1.0", diff --git a/src/AppOnboarded.tsx b/src/AppOnboarded.tsx index fb21bab9f..68549549b 100644 --- a/src/AppOnboarded.tsx +++ b/src/AppOnboarded.tsx @@ -7,7 +7,7 @@ import RootNavigator from './navigation/root/RootNavigator'; import InactivityTracker from './components/InactivityTracker'; import { showToast } from './utils/notifications'; import { startWalletServices } from './utils/startup'; -import { getOnChainWalletElectrum } from './utils/wallet'; +import { getOnChainWalletElectrumAsync } from './utils/wallet'; import { unsubscribeFromLightningSubscriptions } from './utils/lightning'; import { useAppSelector } from './hooks/redux'; import { dispatch } from './store/helpers'; @@ -25,8 +25,6 @@ import { import { updateSettings } from './store/slices/settings'; // import { updateExchangeRates } from './store/actions/wallet'; -const electrum = getOnChainWalletElectrum(); - const AppOnboarded = (): ReactElement => { const { t } = useTranslation('other'); const appState = useRef(AppState.currentState); @@ -59,16 +57,16 @@ const AppOnboarded = (): ReactElement => { // on AppState change const appStateSubscription = AppState.addEventListener( 'change', - (nextAppState) => { + async (nextAppState) => { dispatch(updateUi({ appState: nextAppState })); - + const electrum = await getOnChainWalletElectrumAsync(); // on App to foreground if ( appState.current.match(/inactive|background/) && nextAppState === 'active' ) { // resubscribe to electrum connection changes - electrum?.startConnectionPolling(); + electrum.startConnectionPolling(); } // on App to background @@ -76,7 +74,7 @@ const AppOnboarded = (): ReactElement => { appState.current.match(/active|inactive/) && nextAppState === 'background' ) { - electrum?.stopConnectionPolling(); + electrum.stopConnectionPolling(); } appState.current = nextAppState; diff --git a/src/hooks/wallet.ts b/src/hooks/wallet.ts index 8471c3769..9741ead31 100644 --- a/src/hooks/wallet.ts +++ b/src/hooks/wallet.ts @@ -1,3 +1,5 @@ +import { Wallet as TWallet } from 'beignet'; +import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useAppDispatch, useAppSelector } from '../hooks/redux'; @@ -9,6 +11,7 @@ import { ignoreSwitchUnitToast } from '../store/slices/user'; import { EUnit } from '../store/types/wallet'; import i18n from '../utils/i18n'; import { showToast } from '../utils/notifications'; +import { getOnChainWalletAsync } from '../utils/wallet'; import { useCurrency } from './displayValues'; /** @@ -63,3 +66,23 @@ export const useSwitchUnitAnnounced = (): (() => void) => { return switchUnitAnnounced; }; + +/** + * Wait for the onchain wallet to be loaded. + */ +export const useOnchainWallet = (): { wallet: TWallet | null } => { + const [wallet, setWallet] = useState(null); + + useEffect(() => { + const getWallet = async (): Promise => { + const w = await getOnChainWalletAsync(); + setWallet(w); + }; + + getWallet(); + }, []); + + return { + wallet, + }; +}; diff --git a/src/screens/Activity/ActivityDetail.tsx b/src/screens/Activity/ActivityDetail.tsx index 2b5e79d21..6f14def7a 100644 --- a/src/screens/Activity/ActivityDetail.tsx +++ b/src/screens/Activity/ActivityDetail.tsx @@ -95,7 +95,7 @@ import type { RootStackScreenProps, } from '../../navigation/types'; import { i18nTime } from '../../utils/i18n'; -import { useSwitchUnit } from '../../hooks/wallet'; +import { useOnchainWallet, useSwitchUnit } from '../../hooks/wallet'; import { contactsSelector } from '../../store/reselect/slashtags'; import { ETransferStatus } from '../../store/types/wallet'; @@ -157,6 +157,7 @@ const OnchainActivityDetail = ({ const isSend = txType === EPaymentType.sent; const total = isSend ? fee + value : value; + const { wallet } = useOnchainWallet(); const { t } = useTranslation('wallet'); const { t: tTime } = useTranslation('intl', { i18n: i18nTime }); const switchUnit = useSwitchUnit(); @@ -210,17 +211,21 @@ const OnchainActivityDetail = ({ }, [confirmed, isBoosted, txId]); const boostedParents = useMemo(() => { + if (!wallet) { + return []; + } return getBoostedTransactionParents({ + wallet, txId, boostedTransactions, }); - }, [boostedTransactions, txId]); + }, [boostedTransactions, txId, wallet]); const hasBoostedParents = useMemo(() => { return boostedParents.length > 0; }, [boostedParents.length]); - const handleBoostParentPress = (parentTxId): void => { + const handleBoostParentPress = (parentTxId: string): void => { const activityItem = activityItems.find((i) => { return i.activityType === EActivityType.onchain && i.txId === parentTxId; }); @@ -301,6 +306,10 @@ const OnchainActivityDetail = ({ return ; }, [txDetails]); + if (!wallet) { + return ; + } + let fees = fee; let paymentAmount = value; let status = ( diff --git a/src/screens/Settings/GapLimit/index.tsx b/src/screens/Settings/GapLimit/index.tsx index 81e68b769..63bc249c3 100644 --- a/src/screens/Settings/GapLimit/index.tsx +++ b/src/screens/Settings/GapLimit/index.tsx @@ -12,7 +12,7 @@ import { gapLimitOptionsSelector } from '../../../store/reselect/wallet'; import { ScrollView, TextInput, View } from '../../../styles/components'; import { Caption13Up } from '../../../styles/text'; import { showToast } from '../../../utils/notifications'; -import { getOnChainWallet, refreshWallet } from '../../../utils/wallet'; +import { getOnChainWalletAsync, refreshWallet } from '../../../utils/wallet'; const GapLimit = ({}: SettingsScreenProps<'GapLimit'>): ReactElement => { const { t } = useTranslation('settings'); @@ -68,7 +68,7 @@ const GapLimit = ({}: SettingsScreenProps<'GapLimit'>): ReactElement => { const saveGapLimit = async (): Promise => { setLoading(true); - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); const res = wallet.updateGapLimit({ lookAhead: Number(lookAhead), lookBehind: Number(lookBehind), diff --git a/src/store/actions/wallet.ts b/src/store/actions/wallet.ts index 3c5d95e0d..c5de1eb6f 100644 --- a/src/store/actions/wallet.ts +++ b/src/store/actions/wallet.ts @@ -25,11 +25,12 @@ import { createDefaultWallet, getCurrentWallet, getOnChainWallet, + getOnChainWalletAsync, getOnChainWalletTransaction, + getOnChainWalletTransactionAsync, getSelectedNetwork, getSelectedWallet, refreshWallet, - waitForWallet, } from '../../utils/wallet'; import { dispatch, @@ -125,7 +126,7 @@ export const generateNewReceiveAddress = async ({ keyDerivationPath?: IKeyDerivationPath; }): Promise> => { try { - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); return wallet.generateNewReceiveAddress({ addressType, keyDerivationPath }); } catch (e) { console.log(e); @@ -138,22 +139,22 @@ export const generateNewReceiveAddress = async ({ * @returns {Promise} */ export const clearUtxos = async (): Promise => { - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); return await wallet.clearUtxos(); }; -export const updateWalletBalance = ({ - balance, -}: { - balance: number; -}): Result => { - try { - const wallet = getOnChainWallet(); - return wallet.updateWalletBalance({ balance }); - } catch (e) { - return err(e); - } -}; +// export const updateWalletBalance = ({ +// balance, +// }: { +// balance: number; +// }): Result => { +// try { +// const wallet = getOnChainWalletAsync(); +// return wallet.updateWalletBalance({ balance }); +// } catch (e) { +// return err(e); +// } +// }; /** * Parses and adds unconfirmed transactions to the store. @@ -216,7 +217,7 @@ export const injectFakeTransaction = ( // scanAllAddresses?: boolean; // replaceStoredTransactions?: boolean; // }): Promise> => { -// const wallet = getOnChainWallet(); +// const wallet = async getOnChainWalletAsync(); // return await wallet.updateTransactions({ // scanAllAddresses, // replaceStoredTransactions, @@ -233,7 +234,7 @@ export const deleteOnChainTransactionById = async ({ }: { txid: string; }): Promise => { - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); return await wallet.deleteOnChainTransactionById({ txid }); }; @@ -255,7 +256,7 @@ export const addBoostedTransaction = async ({ type?: EBoostType; fee: number; }): Promise> => { - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); return await wallet.addBoostedTransaction({ newTxId, oldTxId, @@ -290,7 +291,7 @@ export const setupOnChainTransaction = async ({ outputs?: IOutput[]; // Used to pre-specify outputs to use. } = {}): Promise => { rbf = rbf ?? getSettingsStore().rbf; - const transaction = getOnChainWalletTransaction(); + const transaction = await getOnChainWalletTransactionAsync(); return await transaction.setupTransaction({ inputTxHashes, utxos, @@ -310,7 +311,7 @@ export const getChangeAddress = async ({ }: { addressType?: EAddressType; }): Promise> => { - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); return await wallet.getChangeAddress(addressType); }; @@ -331,8 +332,7 @@ export const updateSendTransaction = ( * @returns {Result} */ export const resetSendTransaction = async (): Promise> => { - await waitForWallet(); - const transaction = getOnChainWalletTransaction(); + const transaction = await getOnChainWalletTransactionAsync(); return transaction.resetSendTransaction(); }; @@ -341,7 +341,7 @@ export const updateSelectedAddressType = async ({ }: { addressType: EAddressType; }): Promise => { - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); const addressTypesToMonitor = wallet.addressTypesToMonitor; if (!addressTypesToMonitor.includes(addressType)) { // Append the new address type so we monitor it in subsequent sessions. diff --git a/src/store/utils/activity.ts b/src/store/utils/activity.ts index cfe42a438..8b609253d 100644 --- a/src/store/utils/activity.ts +++ b/src/store/utils/activity.ts @@ -101,7 +101,7 @@ export const updateOnChainActivityList = async (): Promise> => { }); const activityItems = await Promise.all(promises); - const boostFormattedItems = formatBoostedActivityItems({ + const boostFormattedItems = await formatBoostedActivityItems({ items: activityItems, boostedTransactions, selectedWallet, diff --git a/src/store/utils/fees.ts b/src/store/utils/fees.ts index 75d89abca..8edd89ec4 100644 --- a/src/store/utils/fees.ts +++ b/src/store/utils/fees.ts @@ -4,7 +4,7 @@ import { dispatch, getFeesStore } from '../helpers'; import { updateOnchainFees } from '../slices/fees'; import { getFeeEstimates } from '../../utils/wallet/transactions'; import { EAvailableNetwork } from '../../utils/networks'; -import { getOnChainWallet, getSelectedNetwork } from '../../utils/wallet'; +import { getOnChainWalletAsync, getSelectedNetwork } from '../../utils/wallet'; import { IOnchainFees } from 'beignet'; export const REFRESH_INTERVAL = 60 * 30; // in seconds, 30 minutes @@ -46,6 +46,6 @@ export const refreshOnchainFeeEstimates = async ({ }: { forceUpdate?: boolean; }): Promise> => { - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); return await wallet.updateFeeEstimates(forceUpdate); }; diff --git a/src/utils/boost.ts b/src/utils/boost.ts index dad9b5923..f0f42f9ec 100644 --- a/src/utils/boost.ts +++ b/src/utils/boost.ts @@ -1,11 +1,11 @@ -import { EBoostType, IBoostedTransactions } from 'beignet'; +import { EBoostType, IBoostedTransactions, Wallet as TWallet } from 'beignet'; import { getActivityStore, getWalletStore } from '../store/helpers'; import { IActivityItem, TOnchainActivityItem } from '../store/types/activity'; import { TWalletName } from '../store/types/wallet'; import { EAvailableNetwork } from './networks'; import { - getOnChainWallet, + getOnChainWalletAsync, getSelectedNetwork, getSelectedWallet, } from './wallet'; @@ -30,18 +30,21 @@ export const getBoostedTransactions = ({ /** * Returns an array of parents for a boosted transaction id. + * @param {TWallet} wallet * @param {string} txId * @param {IBoostedTransactions} [boostedTransactions] * @returns {string[]} */ export const getBoostedTransactionParents = ({ + wallet, txId, boostedTransactions, }: { + wallet: TWallet; txId: string; boostedTransactions?: IBoostedTransactions; }): string[] => { - return getOnChainWallet().getBoostedTransactionParents({ + return wallet.getBoostedTransactionParents({ txid: txId, boostedTransactions, }); @@ -57,11 +60,13 @@ export const getBoostedTransactionParents = ({ * @returns {boolean} */ export const hasBoostedParents = ({ + wallet, txId, boostedTransactions, selectedWallet = getSelectedWallet(), selectedNetwork = getSelectedNetwork(), }: { + wallet: TWallet; txId: string; boostedTransactions?: IBoostedTransactions; selectedWallet?: TWalletName; @@ -74,6 +79,7 @@ export const hasBoostedParents = ({ }); } const boostedParents = getBoostedTransactionParents({ + wallet, txId, boostedTransactions, }); @@ -89,7 +95,7 @@ export const hasBoostedParents = ({ * @param {EAvailableNetwork} [selectedNetwork] * @returns {TOnchainActivityItem|undefined} */ -export const getRootParentActivity = ({ +const getRootParentActivity = async ({ txId, items, boostedTransactions, @@ -101,7 +107,8 @@ export const getRootParentActivity = ({ boostedTransactions?: IBoostedTransactions; selectedWallet?: TWalletName; selectedNetwork?: EAvailableNetwork; -}): TOnchainActivityItem | undefined => { +}): Promise => { + const wallet = await getOnChainWalletAsync(); if (!boostedTransactions) { boostedTransactions = getBoostedTransactions({ selectedWallet, @@ -109,6 +116,7 @@ export const getRootParentActivity = ({ }); } const boostedParents = getBoostedTransactionParents({ + wallet, txId, boostedTransactions, }); @@ -147,7 +155,7 @@ export const getParentsActivity = ({ * @param {EAvailableNetwork} [selectedNetwork] * @returns {TOnchainActivityItem[]} */ -export const formatBoostedActivityItems = ({ +export const formatBoostedActivityItems = async ({ items, boostedTransactions, selectedWallet, @@ -157,18 +165,18 @@ export const formatBoostedActivityItems = ({ boostedTransactions: IBoostedTransactions; selectedWallet: TWalletName; selectedNetwork: EAvailableNetwork; -}): TOnchainActivityItem[] => { +}): Promise => { const formattedItems: TOnchainActivityItem[] = []; - items.forEach((item) => { + for (const item of items) { const { txId } = item; // if boosted tx don't add for now if (txId in boostedTransactions) { - return; + continue; } - const rootParent = getRootParentActivity({ + const rootParent = await getRootParentActivity({ txId, items, boostedTransactions, @@ -179,7 +187,7 @@ export const formatBoostedActivityItems = ({ // if we can't find a parent tx leave as is if (!rootParent) { formattedItems.push(item); - return; + continue; } const parentBoostType = boostedTransactions[rootParent.txId].type; @@ -187,10 +195,10 @@ export const formatBoostedActivityItems = ({ // if it's an RBF tx leave as is, only mark as boosted if (parentBoostType === EBoostType.rbf) { formattedItems.push({ ...item, isBoosted: true }); - return; + continue; } - const value = calculateBoostTransactionValue({ + const value = await calculateBoostTransactionValue({ currentActivityItem: item, items, boostedTransactions, @@ -207,7 +215,7 @@ export const formatBoostedActivityItems = ({ address: rootParent.address, isBoosted: true, }); - }); + } return formattedItems; }; @@ -220,7 +228,7 @@ export const formatBoostedActivityItems = ({ * @param {IBoostedTransactions} boostedTransactions * @returns {number} */ -export const calculateBoostTransactionValue = ({ +export const calculateBoostTransactionValue = async ({ currentActivityItem, items, boostedTransactions, @@ -230,14 +238,14 @@ export const calculateBoostTransactionValue = ({ items: TOnchainActivityItem[]; boostedTransactions: IBoostedTransactions; includeFee?: boolean; -}): number => { +}): Promise => { const boostedTransaction = Object.values(boostedTransactions).find( (tx) => tx.childTransaction === currentActivityItem.txId, ); if (!boostedTransaction) { return currentActivityItem.value; } - const rootParent = getRootParentActivity({ + const rootParent = await getRootParentActivity({ txId: currentActivityItem.txId, items, boostedTransactions, diff --git a/src/utils/ledger.ts b/src/utils/ledger.ts index 9f7a2384e..c5a003a75 100644 --- a/src/utils/ledger.ts +++ b/src/utils/ledger.ts @@ -16,7 +16,7 @@ import { } from './lightning'; import { EAvailableNetwork } from './networks'; import { - getOnChainWalletElectrum, + getOnChainWalletElectrumAsync, getScriptHash, getSelectedNetwork, getSelectedWallet, @@ -59,7 +59,7 @@ export const getChannelCloseTime = async ( // we have to construct IAddress to get the history const scriptHash = await getScriptHash(address, selectedNetwork); - const el = getOnChainWalletElectrum(); + const el = await getOnChainWalletElectrumAsync(); const scriptHashes: IAddress[] = [ { index: 0, diff --git a/src/utils/lightning/index.ts b/src/utils/lightning/index.ts index 70969ef6a..ca555516f 100644 --- a/src/utils/lightning/index.ts +++ b/src/utils/lightning/index.ts @@ -42,8 +42,8 @@ import { getBip39Passphrase, getCurrentAddressIndex, getMnemonicPhrase, - getOnChainWalletData, - getOnChainWalletElectrum, + getOnChainWalletDataAsync, + getOnChainWalletElectrumAsync, getSelectedNetwork, getSelectedWallet, ldkSeed, @@ -214,7 +214,7 @@ export const setLdkStoragePath = (): Promise> => const broadcastTransaction: TBroadcastTransaction = async ( rawTx: string, ): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); const res = await electrum.broadcastTransaction({ rawTx, subscribeToOutputAddress: false, @@ -229,7 +229,7 @@ const broadcastTransaction: TBroadcastTransaction = async ( const getScriptPubKeyHistory = async ( scriptPubKey: string, ): Promise => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); return await electrum.getScriptPubKeyHistory(scriptPubKey); }; @@ -913,7 +913,7 @@ export const getBestBlock = async ( selectedNetwork: EAvailableNetwork = getSelectedNetwork(), ): Promise => { try { - const beignetHeader = getOnChainWalletData().header; + const beignetHeader = (await getOnChainWalletDataAsync()).header; const storageHeader = getWalletStore().header[selectedNetwork]; const header = beignetHeader.height > storageHeader.height @@ -937,7 +937,7 @@ export const getTransactionData = async ( let transactionData = DefaultTransactionDataShape; try { const data = [{ tx_hash: txId }]; - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); const response = await electrum.getTransactions({ txHashes: data, }); diff --git a/src/utils/wallet/electrum.ts b/src/utils/wallet/electrum.ts index f8a9111d7..cb1c11abe 100644 --- a/src/utils/wallet/electrum.ts +++ b/src/utils/wallet/electrum.ts @@ -2,11 +2,12 @@ import { err, ok, Result } from '@synonymdev/result'; import { EAvailableNetwork } from '../networks'; import { + ITransaction, getCustomElectrumPeers, - getOnChainWallet, + getOnChainWalletAsync, getOnChainWalletElectrum, + getOnChainWalletElectrumAsync, getSelectedNetwork, - ITransaction, refreshWallet, } from './index'; import { @@ -35,7 +36,7 @@ export type TUnspentAddressScriptHashData = { * @returns {Promise} */ export const isConnectedElectrum = async (): Promise => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); return electrum.isConnected(); }; @@ -65,7 +66,7 @@ export const listUnspentAddressScriptHashes = async ({ }: { addresses: TUnspentAddressScriptHashData; }): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); const unspentAddressResult = await electrum.listUnspentAddressScriptHashes({ addresses, }); @@ -89,7 +90,7 @@ export const subscribeToAddresses = async ({ scriptHashes?: string[]; onReceive?: () => void; } = {}): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); return electrum.subscribeToAddresses({ scriptHashes, onReceive }); }; @@ -132,7 +133,7 @@ export const getTransactions = async ({ }: { txHashes: ITxHash[]; }): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); return await electrum.getTransactions({ txHashes }); }; @@ -147,7 +148,7 @@ export interface IPeerData { * @return {Promise>} */ export const getConnectedPeer = async (): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); const peerData = await electrum.getConnectedPeer(); return peerData as Result; }; @@ -173,7 +174,7 @@ export const getTransactionsFromInputs = async ({ }: { txHashes: ITxHash[]; }): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); return await electrum.getTransactionsFromInputs({ txHashes }); }; @@ -197,7 +198,7 @@ export const getAddressHistory = async ({ scriptHashes?: IAddress[]; scanAllAddresses?: boolean; }): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); return await electrum.getAddressHistory({ scriptHashes, scanAllAddresses }); }; @@ -217,7 +218,7 @@ export const connectToElectrum = async ({ showNotification?: boolean; selectedNetwork?: EAvailableNetwork; } = {}): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); // Attempt to disconnect from any old/lingering connections await electrum?.disconnect(); @@ -254,7 +255,7 @@ export const getAddressBalance = async ({ }: { addresses: string[]; }): Promise> => { - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); return await wallet.getAddressesBalance(addresses); }; @@ -269,26 +270,10 @@ export const getBlockHex = async ({ }: { height?: number; }): Promise> => { - const electrum = getOnChainWalletElectrum(); + const electrum = await getOnChainWalletElectrumAsync(); return await electrum.getBlockHex({ height }); }; -/** - * Returns the block hash given a block hex. - * Leaving blockHex empty will return the last known block hash from storage. - * @param {string} [blockHex] - * @param {EAvailableNetwork} [selectedNetwork] - * @returns {string} - */ -export const getBlockHashFromHex = ({ - blockHex, -}: { - blockHex?: string; -}): string => { - const electrum = getOnChainWalletElectrum(); - return electrum.getBlockHashFromHex({ blockHex }); -}; - /** * Returns last known block height, and it's corresponding hex from local storage. * @returns {IHeader} diff --git a/src/utils/wallet/index.ts b/src/utils/wallet/index.ts index fd23517fb..a64ae8e00 100644 --- a/src/utils/wallet/index.ts +++ b/src/utils/wallet/index.ts @@ -96,8 +96,8 @@ import { createWallet } from '../../store/slices/wallet'; bitcoin.initEccLib(ecc); const bip32 = BIP32Factory(ecc); -let wallet: TWallet; let addressGenerator: BitcoinActions | undefined; +let globalWallet: TWallet | undefined; export const setupAddressGenerator = async ({ selectedWallet = getSelectedWallet(), @@ -135,15 +135,13 @@ export const setupAddressGenerator = async ({ /* * Wait for wallet to be ready */ -export const waitForWallet = async (): Promise => { - if (wallet) { - return; +export const waitForWallet = async (): Promise => { + // Return the wallet when it's defined + while (typeof globalWallet === 'undefined') { + // eslint-disable-next-line no-promise-executor-return + await new Promise((resolve) => setTimeout(resolve, 10)); } - return new Promise((resolve) => { - setTimeout(() => { - resolve(waitForWallet()); - }, 100); - }); + return globalWallet; }; export const refreshWallet = async ({ @@ -167,8 +165,6 @@ export const refreshWallet = async ({ InteractionManager.runAfterInteractions(() => resolve(null)); }); - await waitForWallet(); - let notificationTxid: string | undefined; if (onchain) { @@ -213,6 +209,8 @@ const refreshBeignet = async ( console.error('Error reading additional addresses from LDK:', e); } + const wallet = await getOnChainWalletAsync(); + const refreshWalletRes = await wallet.refreshWallet({ scanAllAddresses, additionalAddresses, @@ -250,6 +248,7 @@ const handleRefreshError = (msg: string): void => { * In the event we temporarily changed the gap limit in Beignet when restoring Bitkit, we need to reset it back to Bitkit's default/saved values. */ const checkGapLimit = (): void => { + const wallet = getOnChainWallet(); const savedGapLimitOptions = getGapLimitOptions(); const beignetGapLimit = wallet.gapLimitOptions; if ( @@ -280,6 +279,7 @@ export const generateAddresses = async ({ addressType, }: IGenerateAddresses): Promise> => { try { + const wallet = await getOnChainWalletAsync(); return await wallet.generateAddresses({ addressAmount, changeAddressAmount, @@ -810,6 +810,7 @@ export const getRbfData = async ({ }: { txHash: ITxHash; }): Promise> => { + const wallet = await getOnChainWalletAsync(); return await wallet.getRbfData({ txHash }); }; @@ -1025,6 +1026,7 @@ const onMessage: TOnMessage = async (key, data): Promise => { switch (key) { case 'transactionReceived': { const txMsg = data as TTransactionMessage; + const wallet = await getOnChainWalletAsync(); if ( wallet?.isSwitchingNetworks !== undefined && !wallet?.isSwitchingNetworks @@ -1161,8 +1163,9 @@ export const setupOnChainWallet = async ({ if (createWalletResponse.isErr()) { return err(createWalletResponse.error.message); } - wallet = createWalletResponse.value; - return ok(wallet); + globalWallet = createWalletResponse.value; + await globalWallet.refreshWallet({}); // wait for wallet to load it's balance + return ok(globalWallet); }; /** @@ -1413,6 +1416,7 @@ export const getReceiveAddress = async ({ if (!addressType) { addressType = getSelectedAddressType({ selectedNetwork }); } + const wallet = await getOnChainWalletAsync(); const address = await wallet.getAddress({ addressType }); return address ? ok(address) : err('Unable to get receive address.'); } catch (e) { @@ -1430,6 +1434,7 @@ export const getCurrentAddressIndex = async ({ addressType?: EAddressType; }): Promise> => { try { + const wallet = await getOnChainWalletAsync(); addressType = addressType ?? wallet.addressType; const currentWallet = wallet.data; const addressIndex = currentWallet.addressIndex[addressType]; @@ -1490,10 +1495,10 @@ export const getBalance = ({ selectedNetwork, }); + const wallet = getOnChainWallet(); const { localBalance } = getLightningBalance(); const reserveBalance = getLightningReserveBalance(); const spendingBalance = localBalance - reserveBalance; - const onchainBalance = wallet.getBalance() ?? currentWallet.balance[selectedNetwork]; // const lightningBalance = localBalance + claimableBalance; @@ -1527,6 +1532,7 @@ export const rescanAddresses = async ({ shouldClearAddresses?: boolean; shouldClearTransactions?: boolean; }): Promise> => { + const wallet = await getOnChainWalletAsync(); const res = await wallet.rescanAddresses({ shouldClearAddresses, shouldClearTransactions, @@ -1564,24 +1570,48 @@ export const blockHeightToConfirmations = ({ return currentHeight - blockHeight + 1; }; +/** + * @deprecated Use getOnChainWalletAsync instead. + */ export const getOnChainWallet = (): Wallet => { - return wallet; + if (!globalWallet) { + throw new Error('Beignet wallet not initialized.'); + } + return globalWallet; }; +export const getOnChainWalletAsync = async (): Promise => { + return waitForWallet(); +}; + +/** + * @deprecated Use getOnChainWalletTransactionAsync instead. + */ export const getOnChainWalletTransaction = (): Transaction => { - return wallet.transaction; + return getOnChainWallet().transaction; }; +export const getOnChainWalletTransactionAsync = + async (): Promise => { + const wallet = await getOnChainWalletAsync(); + return wallet.transaction; + }; + +/** + * @deprecated Use getOnChainWalletAsync instead. + */ export const getOnChainWalletElectrum = (): Electrum => { - return wallet?.electrum; + return getOnChainWallet().electrum; }; -export const getOnChainWalletTransactionData = (): ISendTransaction => { - return wallet.transaction.data; +export const getOnChainWalletElectrumAsync = async (): Promise => { + const wallet = await getOnChainWalletAsync(); + return wallet.electrum; }; -export const getOnChainWalletData = (): IWalletData => { - return wallet?.data; +export const getOnChainWalletDataAsync = async (): Promise => { + const wallet = await getOnChainWalletAsync(); + return wallet.data; }; export const switchNetwork = async ( @@ -1597,6 +1627,7 @@ export const switchNetwork = async ( dispatch(resetActivityState()); // Switch to new network. dispatch(updateWallet({ selectedNetwork })); + const wallet = await getOnChainWalletAsync(); const response = await wallet.switchNetwork( EAvailableNetworks[selectedNetwork], servers, @@ -1606,7 +1637,7 @@ export const switchNetwork = async ( console.error(response.error.message); return err(response.error.message); } - wallet = response.value; + globalWallet = response.value; setTimeout(updateActivityList, 500); return ok(true); }; @@ -1630,6 +1661,7 @@ export const getFeeInfo = ({ transaction?: Partial; fundingLightning?: boolean; }): Result => { + const wallet = getOnChainWallet(); return wallet.getFeeInfo({ satsPerByte, transaction, diff --git a/src/utils/wallet/transactions.ts b/src/utils/wallet/transactions.ts index 0ce6566da..dfa0d1322 100644 --- a/src/utils/wallet/transactions.ts +++ b/src/utils/wallet/transactions.ts @@ -40,8 +40,10 @@ import { TRANSACTION_DEFAULTS } from './constants'; import { getBalance, getOnChainWallet, + getOnChainWalletAsync, getOnChainWalletElectrum, getOnChainWalletTransaction, + getOnChainWalletTransactionAsync, getSelectedNetwork, getSelectedWallet, refreshWallet, @@ -103,7 +105,7 @@ export const createTransaction = async ( transactionData?: ISendTransaction, ): Promise> => { try { - const transaction = getOnChainWalletTransaction(); + const transaction = await getOnChainWalletTransactionAsync(); const createTxRes = await transaction.createTransaction({ transactionData, }); @@ -495,7 +497,7 @@ export const sendMax = async ({ const { paymentMethod } = getUiStore(); try { - const tx = getOnChainWalletTransaction(); + const tx = await getOnChainWalletTransactionAsync(); const transaction = tx.data; // TODO: Re-work lightning transaction invoices once beignet migration is complete. @@ -813,7 +815,7 @@ export const setupCpfp = async ({ }: { txid: string; }): Promise> => { - const transaction = getOnChainWalletTransaction(); + const transaction = await getOnChainWalletTransactionAsync(); return await transaction.setupCpfp({ txid }); }; @@ -826,7 +828,7 @@ export const setupRbf = async ({ }: { txid: string; }): Promise> => { - const transaction = getOnChainWalletTransaction(); + const transaction = await getOnChainWalletTransactionAsync(); return await transaction.setupRbf({ txid }); }; @@ -901,7 +903,7 @@ export const getFeeEstimates = async ( }); } - const wallet = getOnChainWallet(); + const wallet = await getOnChainWalletAsync(); const feeRes = await wallet.getFeeEstimates(); if (!feeRes) { return err('Unable to get fee estimates.'); diff --git a/src/utils/wallet/transfer.ts b/src/utils/wallet/transfer.ts index 63404af8a..5955553b6 100644 --- a/src/utils/wallet/transfer.ts +++ b/src/utils/wallet/transfer.ts @@ -7,9 +7,13 @@ import { import { err, ok } from '@synonymdev/result'; import lm, { ldk } from '@synonymdev/react-native-ldk'; -import { getCurrentWallet, getSelectedNetwork, refreshWallet } from '.'; +import { + getCurrentWallet, + getOnChainWalletAsync, + getSelectedNetwork, + refreshWallet, +} from '.'; import { btcToSats } from '../conversion'; -import { getBoostedTransactionParents } from '../boost'; import { getTransactions } from './electrum'; import { ETransferStatus, @@ -33,6 +37,7 @@ export const getTransferForTx = async ( ): Promise => { const { currentWallet, selectedNetwork } = getCurrentWallet(); const transfers = currentWallet.transfers[selectedNetwork]; + const wallet = await getOnChainWalletAsync(); if (tx.type === EPaymentType.sent) { const transfersToSpending = transfers.filter((t) => { @@ -46,7 +51,9 @@ export const getTransferForTx = async ( // check if the tx is a transfer that was boosted if (!transferToSpending) { - const boostedParents = getBoostedTransactionParents({ txId: tx.txid }); + const boostedParents = wallet.getBoostedTransactionParents({ + txid: tx.txid, + }); const isBoosted = boostedParents.length > 0; if (isBoosted) { transferToSpending = transfersToSpending.find((t) => { diff --git a/yarn.lock b/yarn.lock index 1e19befa4..89f7a7f29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6368,9 +6368,9 @@ __metadata: languageName: node linkType: hard -"beignet@npm:0.0.46": - version: 0.0.46 - resolution: "beignet@npm:0.0.46" +"beignet@npm:0.0.47": + version: 0.0.47 + resolution: "beignet@npm:0.0.47" dependencies: "@bitcoinerlab/secp256k1": 1.0.5 bech32: 2.0.0 @@ -6384,7 +6384,7 @@ __metadata: lodash.clonedeep: 4.5.0 net: 1.0.2 rn-electrum-client: 0.0.18 - checksum: aee2f6d7b852287b33950bd0a1f784db9fa2db0549218eb314ce34409f611b0cd6ddc9bb7b4c4ed2ee46694080b92a1d4a755198c658bf477d435ec8b3e3457f + checksum: c3f3ec63c2324610579a1b63482b1baf2f3606f27d24ee0c9fbde405cad6fe8f4e94a7ef43c4532d61cd57f4715cf525c00776abfcf1f709e64835c67ef514f4 languageName: node linkType: hard @@ -6631,7 +6631,7 @@ __metadata: babel-jest: ^29.7.0 babel-plugin-transform-remove-console: ^6.9.4 bech32: 2.0.0 - beignet: 0.0.46 + beignet: 0.0.47 bip21: 2.0.3 bip32: 4.0.0 bip39: 3.1.0