Changeset View
Changeset View
Standalone View
Standalone View
web/cashtab/src/hooks/useWallet.js
/* eslint-disable react-hooks/exhaustive-deps */ | /* eslint-disable react-hooks/exhaustive-deps */ | ||||
import React, { useState, useEffect } from 'react'; | import React, { useState, useEffect } from 'react'; | ||||
import Paragraph from 'antd/lib/typography/Paragraph'; | import Paragraph from 'antd/lib/typography/Paragraph'; | ||||
import { notification } from 'antd'; | import { notification } from 'antd'; | ||||
import useAsyncTimeout from '@hooks/useAsyncTimeout'; | import useAsyncTimeout from '@hooks/useAsyncTimeout'; | ||||
import usePrevious from '@hooks/usePrevious'; | import usePrevious from '@hooks/usePrevious'; | ||||
import useBCH from '@hooks/useBCH'; | import useBCH from '@hooks/useBCH'; | ||||
import BigNumber from 'bignumber.js'; | import BigNumber from 'bignumber.js'; | ||||
import { fromSmallestDenomination } from '@utils/cashMethods'; | import { | ||||
fromSmallestDenomination, | |||||
loadStoredWallet, | |||||
isValidStoredWallet, | |||||
} from '@utils/cashMethods'; | |||||
import localforage from 'localforage'; | import localforage from 'localforage'; | ||||
import { currency } from '@components/Common/Ticker'; | import { currency } from '@components/Common/Ticker'; | ||||
import _ from 'lodash'; | import _ from 'lodash'; | ||||
const useWallet = () => { | const useWallet = () => { | ||||
const [wallet, setWallet] = useState(false); | const [wallet, setWallet] = useState(false); | ||||
const [fiatPrice, setFiatPrice] = useState(null); | const [fiatPrice, setFiatPrice] = useState(null); | ||||
const [ws, setWs] = useState(null); | const [ws, setWs] = useState(null); | ||||
const [apiError, setApiError] = useState(false); | const [apiError, setApiError] = useState(false); | ||||
const [walletState, setWalletState] = useState({ | const [walletState, setWalletState] = useState({ | ||||
balances: {}, | balances: {}, | ||||
hydratedUtxoDetails: {}, | |||||
tokens: [], | tokens: [], | ||||
slpBalancesAndUtxos: [], | slpBalancesAndUtxos: {}, | ||||
parsedTxHistory: [], | parsedTxHistory: [], | ||||
utxos: [], | |||||
}); | }); | ||||
const { | const { | ||||
getBCH, | getBCH, | ||||
getUtxos, | getUtxos, | ||||
getHydratedUtxoDetails, | getHydratedUtxoDetails, | ||||
getSlpBalancesAndUtxos, | getSlpBalancesAndUtxos, | ||||
getTxHistory, | getTxHistory, | ||||
getTxData, | getTxData, | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | const deriveAccount = async (BCH, { masterHDNode, path }) => { | ||||
cashAddress, | cashAddress, | ||||
slpAddress, | slpAddress, | ||||
fundingWif: BCH.HDNode.toWIF(node), | fundingWif: BCH.HDNode.toWIF(node), | ||||
fundingAddress: BCH.SLP.Address.toSLPAddress(cashAddress), | fundingAddress: BCH.SLP.Address.toSLPAddress(cashAddress), | ||||
legacyAddress: BCH.SLP.Address.toLegacyAddress(cashAddress), | legacyAddress: BCH.SLP.Address.toLegacyAddress(cashAddress), | ||||
}; | }; | ||||
}; | }; | ||||
const haveUtxosChanged = (utxos, previousUtxos) => { | const haveUtxosChanged = (wallet, utxos, previousUtxos) => { | ||||
// Relevant points for this array comparing exercise | // Relevant points for this array comparing exercise | ||||
// https://stackoverflow.com/questions/13757109/triple-equal-signs-return-false-for-arrays-in-javascript-why | // https://stackoverflow.com/questions/13757109/triple-equal-signs-return-false-for-arrays-in-javascript-why | ||||
// https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript | // https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript | ||||
// If this is initial state | // If this is initial state | ||||
if (utxos === null) { | if (utxos === null) { | ||||
// Then make sure to get slpBalancesAndUtxos | // Then make sure to get slpBalancesAndUtxos | ||||
return true; | return true; | ||||
} | } | ||||
// If this is the first time the wallet received utxos | // If this is the first time the wallet received utxos | ||||
if ( | if (typeof utxos === 'undefined') { | ||||
typeof previousUtxos === 'undefined' || | |||||
typeof utxos === 'undefined' | |||||
) { | |||||
// Then they have certainly changed | // Then they have certainly changed | ||||
return true; | return true; | ||||
} | } | ||||
if (typeof previousUtxos === 'undefined') { | |||||
// Compare to what you have in localStorage on startup | |||||
// If previousUtxos are undefined, see if you have previousUtxos in wallet state | |||||
// If you do, and it has everything you need, set wallet state with that instead of calling hydrateUtxos on all utxos | |||||
if (isValidStoredWallet(wallet)) { | |||||
// Convert all the token balance figures to big numbers | |||||
const liveWalletState = loadStoredWallet(wallet.state); | |||||
return setWalletState(liveWalletState); | |||||
} | |||||
const cachedUtxos = wallet.state.utxos; | |||||
// Compare | |||||
return !_.isEqual(utxos, cachedUtxos); | |||||
} | |||||
// return true for empty array, since this means you definitely do not want to skip the next API call | // return true for empty array, since this means you definitely do not want to skip the next API call | ||||
if (utxos && utxos.length === 0) { | if (utxos && utxos.length === 0) { | ||||
return true; | return true; | ||||
} | } | ||||
// Compare utxo sets | // Compare utxo sets | ||||
const utxoArraysUnchanged = _.isEqual(utxos, previousUtxos); | const utxoArraysUnchanged = _.isEqual(utxos, previousUtxos); | ||||
Show All 26 Lines | const update = async ({ wallet, setWalletState }) => { | ||||
// If an error is returned or utxos from only 1 address are returned | // If an error is returned or utxos from only 1 address are returned | ||||
if (!utxos || _.isEmpty(utxos) || utxos.error || utxos.length < 2) { | if (!utxos || _.isEmpty(utxos) || utxos.error || utxos.length < 2) { | ||||
// Throw error here to prevent more attempted api calls | // Throw error here to prevent more attempted api calls | ||||
// as you are likely already at rate limits | // as you are likely already at rate limits | ||||
throw new Error('Error fetching utxos'); | throw new Error('Error fetching utxos'); | ||||
} | } | ||||
setUtxos(utxos); | setUtxos(utxos); | ||||
const utxosHaveChanged = haveUtxosChanged(utxos, previousUtxos); | // Need to call with wallet as a parameter rather than trusting it is in state, otherwise can sometimes get wallet=false from haveUtxosChanged | ||||
const utxosHaveChanged = haveUtxosChanged( | |||||
wallet, | |||||
utxos, | |||||
previousUtxos, | |||||
); | |||||
// If the utxo set has not changed, | // If the utxo set has not changed, | ||||
if (!utxosHaveChanged) { | if (!utxosHaveChanged) { | ||||
// remove api error here; otherwise it will remain if recovering from a rate | // remove api error here; otherwise it will remain if recovering from a rate | ||||
// limit error with an unchanged utxo set | // limit error with an unchanged utxo set | ||||
setApiError(false); | setApiError(false); | ||||
// then walletState has not changed and does not need to be updated | // then walletState has not changed and does not need to be updated | ||||
//console.timeEnd("update"); | //console.timeEnd("update"); | ||||
Show All 32 Lines | const update = async ({ wallet, setWalletState }) => { | ||||
); | ); | ||||
newState.balances = normalizeBalance(slpBalancesAndUtxos); | newState.balances = normalizeBalance(slpBalancesAndUtxos); | ||||
newState.tokens = tokens; | newState.tokens = tokens; | ||||
newState.parsedTxHistory = parsedWithTokens; | newState.parsedTxHistory = parsedWithTokens; | ||||
newState.utxos = utxos; | |||||
newState.hydratedUtxoDetails = hydratedUtxoDetails; | |||||
setWalletState(newState); | setWalletState(newState); | ||||
// Write this state to indexedDb using localForage | // Write this state to indexedDb using localForage | ||||
writeWalletState(wallet, newState); | writeWalletState(wallet, newState); | ||||
// If everything executed correctly, remove apiError | // If everything executed correctly, remove apiError | ||||
setApiError(false); | setApiError(false); | ||||
} catch (error) { | } catch (error) { | ||||
console.log(`Error in update({wallet, setWalletState})`); | console.log(`Error in update({wallet, setWalletState})`); | ||||
▲ Show 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | const migrateLegacyWallet = async (BCH, wallet) => { | ||||
} | } | ||||
return wallet; | return wallet; | ||||
}; | }; | ||||
const writeWalletState = async (wallet, newState) => { | const writeWalletState = async (wallet, newState) => { | ||||
// Add new state as an object on the active wallet | // Add new state as an object on the active wallet | ||||
wallet.state = newState; | wallet.state = newState; | ||||
console.log(`wallet with state`, wallet); | |||||
try { | try { | ||||
await localforage.setItem('wallet', wallet); | await localforage.setItem('wallet', wallet); | ||||
console.log(`Wallet new state successfully written`); | |||||
} catch (err) { | } catch (err) { | ||||
console.log(`Error in writeWalletState()`); | console.log(`Error in writeWalletState()`); | ||||
console.log(err); | console.log(err); | ||||
} | } | ||||
}; | }; | ||||
const getWalletDetails = async wallet => { | const getWalletDetails = async wallet => { | ||||
if (!wallet) { | if (!wallet) { | ||||
▲ Show 20 Lines • Show All 897 Lines • Show Last 20 Lines |