diff --git a/src/core/raps/execute.ts b/src/core/raps/execute.ts index ed4be22b2e..d0187805d4 100644 --- a/src/core/raps/execute.ts +++ b/src/core/raps/execute.ts @@ -137,13 +137,18 @@ export const walletExecuteRap = async ( parameters: RapSwapActionParameters< 'swap' | 'crosschainSwap' | 'claimBridge' >, -): Promise<{ nonce: number | undefined; errorMessage: string | null }> => { +): Promise<{ + nonce: number | undefined; + errorMessage: string | null; + hash?: string | null; +}> => { const rap: Rap = await createSwapRapByType(type, parameters); const { actions } = rap; const rapName = getRapFullName(rap.actions); let nonce = parameters?.nonce; let errorMessage = null; + let txHash = null; if (actions.length) { const firstAction = actions[0]; const actionParams = { @@ -177,11 +182,14 @@ export const walletExecuteRap = async ( }; const { hash } = await executeAction(actionParams); hash && (await waitForNodeAck(hash, wallet.provider)); + if (index === actions.length - 1) { + txHash = hash; + } } nonce = baseNonce + actions.length - 1; } else { errorMessage = error; } } - return { nonce, errorMessage }; + return { nonce, errorMessage, hash: txHash }; }; diff --git a/src/core/resources/assets/userAssets.ts b/src/core/resources/assets/userAssets.ts index 9fcbf260c3..61c2b1dae9 100644 --- a/src/core/resources/assets/userAssets.ts +++ b/src/core/resources/assets/userAssets.ts @@ -12,6 +12,7 @@ import { import { SupportedCurrencyKey } from '~/core/references'; import { supportedAssetsChainIds } from '~/core/references/chains'; import { useTestnetModeStore } from '~/core/state/currentSettings/testnetMode'; +import { staleBalancesStore } from '~/core/state/staleBalances'; import { ParsedAssetsDictByChain, ParsedUserAsset } from '~/core/types/assets'; import { ChainId } from '~/core/types/chains'; import { AddressAssetsReceivedMessage } from '~/core/types/refraction'; @@ -78,7 +79,11 @@ export const userAssetsFetchQuery = ({ testnetMode, }: FetchUserAssetsArgs) => { queryClient.fetchQuery({ - queryKey: userAssetsQueryKey({ address, currency, testnetMode }), + queryKey: userAssetsQueryKey({ + address, + currency, + testnetMode, + }), queryFn: userAssetsQueryFunction, }); }; @@ -114,7 +119,11 @@ async function userAssetsQueryFunction({ }: QueryFunctionArgs) { const cache = queryClient.getQueryCache(); const cachedUserAssets = (cache.find({ - queryKey: userAssetsQueryKey({ address, currency, testnetMode }), + queryKey: userAssetsQueryKey({ + address, + currency, + testnetMode, + }), })?.state?.data || {}) as ParsedAssetsDictByChain; try { const supportedChainIds = getSupportedChains({ @@ -122,12 +131,14 @@ async function userAssetsQueryFunction({ }) .map(({ id }) => id) .filter((id) => supportedAssetsChainIds.includes(id)); - - const url = `/${supportedChainIds.join(',')}/${address}/assets`; + staleBalancesStore.getState().clearExpiredData(address as Address); + const staleBalancesParam = staleBalancesStore + .getState() + .getStaleBalancesQueryParam(address as Address); + const url = `/${supportedChainIds.join( + ',', + )}/${address}/assets/?currency=${currency.toLowerCase()}${staleBalancesParam}`; const res = await addysHttp.get(url, { - params: { - currency: currency.toLowerCase(), - }, timeout: USER_ASSETS_TIMEOUT_DURATION, }); const chainIdsInResponse = res?.data?.meta?.chain_ids || []; @@ -183,7 +194,11 @@ async function userAssetsQueryFunctionRetryByChain({ const cache = queryClient.getQueryCache(); const cachedUserAssets = (cache.find({ - queryKey: userAssetsQueryKey({ address, currency, testnetMode }), + queryKey: userAssetsQueryKey({ + address, + currency, + testnetMode, + }), })?.state?.data as ParsedAssetsDictByChain) || {}; const retries = []; for (const chainIdWithError of chainIds) { @@ -206,7 +221,11 @@ async function userAssetsQueryFunctionRetryByChain({ } } queryClient.setQueryData( - userAssetsQueryKey({ address, currency, testnetMode }), + userAssetsQueryKey({ + address, + currency, + testnetMode, + }), cachedUserAssets, ); } catch (e) { @@ -230,7 +249,11 @@ export function useUserAssets( ) { const { testnetMode } = useTestnetModeStore(); return useQuery({ - queryKey: userAssetsQueryKey({ address, currency, testnetMode }), + queryKey: userAssetsQueryKey({ + address, + currency, + testnetMode, + }), queryFn: userAssetsQueryFunction, ...config, refetchInterval: USER_ASSETS_REFETCH_INTERVAL, diff --git a/src/core/state/staleBalances/index.test.ts b/src/core/state/staleBalances/index.test.ts new file mode 100644 index 0000000000..d9c9989927 --- /dev/null +++ b/src/core/state/staleBalances/index.test.ts @@ -0,0 +1,170 @@ +import { Address } from 'viem'; +import { expect, test } from 'vitest'; + +import { DAI_ADDRESS, ETH_ADDRESS, OP_ADDRESS } from '~/core/references'; +import { ChainId } from '~/core/types/chains'; +import { TEST_ADDRESS_1, TEST_ADDRESS_2 } from '~/test/utils'; + +import { staleBalancesStore } from '.'; + +const THEN = Date.now() - 700000; +const WHEN = Date.now() + 60000; + +test('should be able to add asset information to the staleBalances object', async () => { + const { addStaleBalance, staleBalances } = staleBalancesStore.getState(); + expect(staleBalances).toStrictEqual({}); + addStaleBalance({ + address: TEST_ADDRESS_1, + chainId: ChainId.mainnet, + info: { + address: DAI_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: THEN, + }, + }); + addStaleBalance({ + address: TEST_ADDRESS_1, + chainId: ChainId.mainnet, + info: { + address: ETH_ADDRESS as Address, + transactionHash: '0xFOOBAR', + expirationTime: WHEN, + }, + }); + const newStaleBalances = staleBalancesStore.getState().staleBalances; + expect(newStaleBalances).toStrictEqual({ + [TEST_ADDRESS_1]: { + [ChainId.mainnet]: { + [DAI_ADDRESS]: { + address: DAI_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: THEN, + }, + [ETH_ADDRESS]: { + address: ETH_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: WHEN, + }, + }, + }, + }); +}); + +test('should generate accurate stale balance query params and clear expired data - case #1', async () => { + const { getStaleBalancesQueryParam, clearExpiredData } = + staleBalancesStore.getState(); + clearExpiredData(TEST_ADDRESS_1); + const queryParam = getStaleBalancesQueryParam(TEST_ADDRESS_1); + expect(queryParam).toStrictEqual(`&token=${ChainId.mainnet}.${ETH_ADDRESS}`); +}); + +test('should be able to remove expired stale balance and preserve unexpired data', async () => { + const { addStaleBalance, clearExpiredData } = staleBalancesStore.getState(); + addStaleBalance({ + address: TEST_ADDRESS_1, + chainId: ChainId.mainnet, + info: { + address: DAI_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: THEN, + }, + }); + addStaleBalance({ + address: TEST_ADDRESS_1, + chainId: ChainId.mainnet, + info: { + address: ETH_ADDRESS as Address, + transactionHash: '0xFOOBAR', + expirationTime: WHEN, + }, + }); + clearExpiredData(TEST_ADDRESS_1); + const newStaleBalances = staleBalancesStore.getState().staleBalances; + expect(newStaleBalances).toStrictEqual({ + [TEST_ADDRESS_1]: { + [ChainId.mainnet]: { + [ETH_ADDRESS]: { + address: ETH_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: WHEN, + }, + }, + }, + }); +}); + +test('should preserve data from other addresses when clearing expired data', async () => { + const { addStaleBalance, clearExpiredData } = staleBalancesStore.getState(); + addStaleBalance({ + address: TEST_ADDRESS_1, + chainId: ChainId.mainnet, + info: { + address: DAI_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: THEN, + }, + }); + addStaleBalance({ + address: TEST_ADDRESS_2, + chainId: ChainId.mainnet, + info: { + address: ETH_ADDRESS as Address, + transactionHash: '0xFOOBAR', + expirationTime: WHEN, + }, + }); + clearExpiredData(TEST_ADDRESS_1); + const newStaleBalances = staleBalancesStore.getState().staleBalances; + expect(newStaleBalances).toStrictEqual({ + [TEST_ADDRESS_1]: { + [ChainId.mainnet]: { + [ETH_ADDRESS]: { + address: ETH_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: WHEN, + }, + }, + }, + [TEST_ADDRESS_2]: { + [ChainId.mainnet]: { + [ETH_ADDRESS]: { + address: ETH_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: WHEN, + }, + }, + }, + }); +}); + +test('should generate accurate stale balance query params and clear expired data - case #2', async () => { + const { getStaleBalancesQueryParam, clearExpiredData } = + staleBalancesStore.getState(); + clearExpiredData(TEST_ADDRESS_2); + const queryParam = getStaleBalancesQueryParam(TEST_ADDRESS_2); + expect(queryParam).toStrictEqual(`&token=${ChainId.mainnet}.${ETH_ADDRESS}`); +}); + +test('should generate accurate stale balance query params and clear expired data - case #3', async () => { + const { addStaleBalance, getStaleBalancesQueryParam, clearExpiredData } = + staleBalancesStore.getState(); + addStaleBalance({ + address: TEST_ADDRESS_1, + chainId: ChainId.optimism, + info: { + address: OP_ADDRESS, + transactionHash: '0xFOOBAR', + expirationTime: WHEN, + }, + }); + + clearExpiredData(TEST_ADDRESS_1); + const queryParam = getStaleBalancesQueryParam(TEST_ADDRESS_1); + expect(queryParam).toStrictEqual( + `&token=${ChainId.mainnet}.${ETH_ADDRESS}&token=${ChainId.optimism}.${OP_ADDRESS}`, + ); + + clearExpiredData(TEST_ADDRESS_2); + const queryParam2 = getStaleBalancesQueryParam(TEST_ADDRESS_2); + expect(queryParam2).toStrictEqual(`&token=${ChainId.mainnet}.${ETH_ADDRESS}`); +}); diff --git a/src/core/state/staleBalances/index.ts b/src/core/state/staleBalances/index.ts new file mode 100644 index 0000000000..9d019c01e2 --- /dev/null +++ b/src/core/state/staleBalances/index.ts @@ -0,0 +1,127 @@ +import { Address } from 'viem'; +import create from 'zustand'; + +import { ChainId } from '~/core/types/chains'; + +import { createStore } from '../internal/createStore'; + +const TIME_TO_WATCH = 600000; + +interface StaleBalanceInfo { + address: Address; + expirationTime?: number; + transactionHash: string; +} + +interface StaleBalances { + [key: Address]: StaleBalanceInfo; +} +interface StaleBalancesByChainId { + [key: number]: StaleBalances; +} + +export interface StaleBalancesState { + addStaleBalance: ({ + address, + chainId, + info, + }: { + address: Address; + chainId: ChainId; + info: StaleBalanceInfo; + }) => void; + clearExpiredData: (address: Address) => void; + getStaleBalancesQueryParam: (address: Address) => string; + staleBalances: Record; +} + +export const staleBalancesStore = createStore( + (set, get) => ({ + addStaleBalance: ({ + address, + chainId, + info, + }: { + address: Address; + chainId: ChainId; + info: StaleBalanceInfo; + }) => { + set((state) => { + const { staleBalances } = state; + const staleBalancesForUser = staleBalances[address] || {}; + const staleBalancesForChain = staleBalancesForUser[chainId] || {}; + const newStaleBalancesForChain = { + ...staleBalancesForChain, + [info.address]: { + ...info, + expirationTime: info.expirationTime || Date.now() + TIME_TO_WATCH, + }, + }; + const newStaleBalancesForUser = { + ...staleBalancesForUser, + [chainId]: newStaleBalancesForChain, + }; + return { + staleBalances: { + ...staleBalances, + [address]: newStaleBalancesForUser, + }, + }; + }); + }, + clearExpiredData: (address: Address) => { + set((state) => { + const { staleBalances } = state; + const staleBalancesForUser = staleBalances[address] || {}; + const newStaleBalancesForUser: StaleBalancesByChainId = { + ...staleBalancesForUser, + }; + for (const c of Object.keys(staleBalancesForUser)) { + const chainId = parseInt(c, 10); + const newStaleBalancesForChain = { + ...(staleBalancesForUser[chainId] || {}), + }; + for (const staleBalance of Object.values(newStaleBalancesForChain)) { + if ( + typeof staleBalance.expirationTime === 'number' && + staleBalance.expirationTime <= Date.now() + ) { + delete newStaleBalancesForChain[staleBalance.address]; + } + } + newStaleBalancesForUser[chainId] = newStaleBalancesForChain; + } + return { + staleBalances: { + ...staleBalances, + [address]: newStaleBalancesForUser, + }, + }; + }); + }, + getStaleBalancesQueryParam: (address: Address) => { + let queryStringFragment = ''; + const { staleBalances } = get(); + const staleBalancesForUser = staleBalances[address]; + for (const c of Object.keys(staleBalancesForUser)) { + const chainId = parseInt(c, 10); + const staleBalancesForChain = staleBalancesForUser[chainId]; + for (const staleBalance of Object.values(staleBalancesForChain)) { + if (typeof staleBalance.expirationTime === 'number') { + queryStringFragment += `&token=${chainId}.${staleBalance.address}`; + } + } + } + return queryStringFragment; + }, + staleBalances: {}, + }), + { + persist: { + name: 'staleBalances', + version: 0, + }, + }, +); + +export const useStaleBalancesStore = create(staleBalancesStore); diff --git a/src/entries/popup/hooks/swap/useSwapAssetsRefresh.tsx b/src/entries/popup/hooks/swap/useSwapAssetsRefresh.tsx deleted file mode 100644 index e64570ce51..0000000000 --- a/src/entries/popup/hooks/swap/useSwapAssetsRefresh.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { useCallback, useEffect, useRef } from 'react'; - -import { ETH_ADDRESS } from '~/core/references'; -import { - selectUserAssetsDictByChain, - selectorFilterByUserChains, -} from '~/core/resources/_selectors/assets'; -import { useUserAssets } from '~/core/resources/assets'; -import { - USER_ASSETS_STALE_INTERVAL, - userAssetsSetQueryData, - userAssetsSetQueryDefaults, -} from '~/core/resources/assets/userAssets'; -import { useCurrentAddressStore, useCurrentCurrencyStore } from '~/core/state'; -import { useSwapAssetsToRefreshStore } from '~/core/state/swapAssetsToRefresh'; -import { fetchAssetBalanceViaProvider } from '~/core/utils/assets'; -import { getProvider } from '~/core/wagmi/clientToProvider'; - -export const useSwapRefreshAssets = () => { - const { currentAddress } = useCurrentAddressStore(); - const { currentCurrency } = useCurrentCurrencyStore(); - const { swapAssetsToRefresh, removeSwapAssetsToRefresh } = - useSwapAssetsToRefreshStore(); - const { data: userAssets } = useUserAssets( - { - address: currentAddress, - currency: currentCurrency, - }, - { - select: (data) => - selectorFilterByUserChains({ - data, - selector: selectUserAssetsDictByChain, - }), - }, - ); - - const timeout = useRef(); - - const swapRefreshAssets = useCallback( - async (nonce?: number) => { - const assetsToRefresh = swapAssetsToRefresh[nonce || -1]; - if ( - !assetsToRefresh || - !assetsToRefresh?.length || - !userAssets || - nonce === undefined - ) - return; - const [assetToBuy, assetToSell] = assetsToRefresh; - - const updatedAssets = userAssets; - - const fetchAssetPromises = [assetToBuy, assetToSell] - .map( - (asset) => - asset.address !== ETH_ADDRESS && - fetchAssetBalanceViaProvider({ - parsedAsset: asset, - currentAddress, - currency: currentCurrency, - provider: getProvider({ chainId: asset.chainId }), - }), - ) - .filter(Boolean); - - const assets = await Promise.all(fetchAssetPromises); - - assets.forEach((asset) => { - if (!asset) return; - updatedAssets[asset.chainId][asset.uniqueId] = asset; - }); - - userAssetsSetQueryData({ - address: currentAddress, - currency: currentCurrency, - userAssets: updatedAssets, - }); - userAssetsSetQueryDefaults({ - address: currentAddress, - currency: currentCurrency, - staleTime: USER_ASSETS_STALE_INTERVAL, - }); - - timeout.current = setTimeout(() => { - userAssetsSetQueryDefaults({ - address: currentAddress, - currency: currentCurrency, - staleTime: 0, - }); - }, USER_ASSETS_STALE_INTERVAL); - - removeSwapAssetsToRefresh({ nonce }); - }, - [ - currentAddress, - currentCurrency, - removeSwapAssetsToRefresh, - swapAssetsToRefresh, - userAssets, - ], - ); - - useEffect(() => { - return () => clearTimeout(timeout.current); - }); - - return { swapRefreshAssets }; -}; diff --git a/src/entries/popup/hooks/useTransactionListForPendingTxs.ts b/src/entries/popup/hooks/useTransactionListForPendingTxs.ts index 8df435e14d..da88e035ef 100644 --- a/src/entries/popup/hooks/useTransactionListForPendingTxs.ts +++ b/src/entries/popup/hooks/useTransactionListForPendingTxs.ts @@ -1,14 +1,17 @@ import { useEffect } from 'react'; import { Address } from 'viem'; +import { userAssetsFetchQuery } from '~/core/resources/assets/userAssets'; import { useConsolidatedTransactions } from '~/core/resources/transactions/consolidatedTransactions'; import { + currentCurrencyStore, nonceStore, pendingTransactionsStore, useCurrentAddressStore, useCurrentCurrencyStore, } from '~/core/state'; import { useTestnetModeStore } from '~/core/state/currentSettings/testnetMode'; +import { staleBalancesStore } from '~/core/state/staleBalances'; import { ChainId } from '~/core/types/chains'; import { RainbowTransaction } from '~/core/types/transactions'; import { getSupportedChains, useSupportedChains } from '~/core/utils/chains'; @@ -76,11 +79,14 @@ function watchForPendingTransactionsReportedByRainbowBackend({ const supportedChainIds = getSupportedChains({ testnets: false, }).map(({ id }) => id); + const { addStaleBalance } = staleBalancesStore.getState(); + const { currentCurrency } = currentCurrencyStore.getState(); for (const supportedChainId of supportedChainIds) { const latestTxConfirmedByBackend = latestTransactions.get(supportedChainId); if (latestTxConfirmedByBackend) { const latestNonceConfirmedByBackend = latestTxConfirmedByBackend.nonce || 0; + const [latestPendingTx] = pendingTransactions.filter( (tx) => tx?.chainId === supportedChainId, ); @@ -106,15 +112,60 @@ function watchForPendingTransactionsReportedByRainbowBackend({ } } - const updatedPendingTransactions = pendingTransactions?.filter((tx) => { + const newlyConfirmedTransactions: RainbowTransaction[] = []; + const updatedPendingTransactions: RainbowTransaction[] = []; + + pendingTransactions.forEach((tx) => { const txNonce = tx.nonce || 0; const latestTx = latestTransactions.get(tx.chainId); const latestTxNonce = latestTx?.nonce || 0; // still pending or backend is not returning confirmation yet // if !latestTx means that is the first tx of the wallet - return !latestTx || txNonce > latestTxNonce; + const newlyConfirmed = latestTxNonce && txNonce <= latestTxNonce; + if (newlyConfirmed) { + newlyConfirmedTransactions.push(tx); + } else { + updatedPendingTransactions.push(tx); + } }); + newlyConfirmedTransactions.forEach((tx) => { + if (tx.changes?.length) { + tx.changes?.forEach((change) => { + const changedAsset = change?.asset; + const changedAssetAddress = changedAsset?.address as Address; + if (changedAsset) { + addStaleBalance({ + address: currentAddress, + chainId: changedAsset?.chainId, + info: { + address: changedAssetAddress, + transactionHash: tx.hash, + }, + }); + } + }); + } else if (tx.asset) { + const changedAsset = tx.asset; + const changedAssetAddress = changedAsset?.address as Address; + addStaleBalance({ + address: currentAddress, + chainId: changedAsset?.chainId, + info: { + address: changedAssetAddress, + transactionHash: tx.hash, + }, + }); + } + }); + + if (newlyConfirmedTransactions.length) { + userAssetsFetchQuery({ + address: currentAddress, + currency: currentCurrency, + }); + } + setPendingTransactions({ address: currentAddress, pendingTransactions: updatedPendingTransactions, diff --git a/src/entries/popup/hooks/useWatchPendingTransactions.ts b/src/entries/popup/hooks/useWatchPendingTransactions.ts index 512fb2a434..84ee419379 100644 --- a/src/entries/popup/hooks/useWatchPendingTransactions.ts +++ b/src/entries/popup/hooks/useWatchPendingTransactions.ts @@ -3,6 +3,7 @@ import { Address } from 'viem'; import { mainnet } from 'viem/chains'; import { queryClient } from '~/core/react-query'; +import { userAssetsQueryKey } from '~/core/resources/assets/common'; import { userAssetsFetchQuery } from '~/core/resources/assets/userAssets'; import { consolidatedTransactionsQueryKey } from '~/core/resources/transactions/consolidatedTransactions'; import { fetchTransaction } from '~/core/resources/transactions/transaction'; @@ -11,6 +12,8 @@ import { useNonceStore, usePendingTransactionsStore, } from '~/core/state'; +import { useTestnetModeStore } from '~/core/state/currentSettings/testnetMode'; +import { useStaleBalancesStore } from '~/core/state/staleBalances'; import { useCustomNetworkTransactionsStore } from '~/core/state/transactions/customNetworkTransactions'; import { useUserChainsStore } from '~/core/state/userChains'; import { @@ -26,14 +29,11 @@ import { import { getProvider } from '~/core/wagmi/clientToProvider'; import { RainbowError, logger } from '~/logger'; -import { useSwapRefreshAssets } from './swap/useSwapAssetsRefresh'; - export const useWatchPendingTransactions = ({ address, }: { address: Address; }) => { - const { swapRefreshAssets } = useSwapRefreshAssets(); const { pendingTransactions: storePendingTransactions, setPendingTransactions, @@ -43,25 +43,20 @@ export const useWatchPendingTransactions = ({ const addCustomNetworkTransactions = useCustomNetworkTransactionsStore.use.addCustomNetworkTransactions(); const { userChains } = useUserChainsStore(); + const { testnetMode } = useTestnetModeStore.getState(); + const { addStaleBalance } = useStaleBalancesStore(); const pendingTransactions = useMemo( () => storePendingTransactions[address] || [], [address, storePendingTransactions], ); - const refreshAssets = useCallback( - (tx: RainbowTransaction) => { - if (tx.type === 'swap') { - swapRefreshAssets(tx.nonce); - } else { - userAssetsFetchQuery({ - address, - currency: currentCurrency, - }); - } - }, - [address, currentCurrency, swapRefreshAssets], - ); + const refreshAssets = useCallback(() => { + userAssetsFetchQuery({ + address, + currency: currentCurrency, + }); + }, [address, currentCurrency]); const processFlashbotsTransaction = useCallback( async (tx: RainbowTransaction): Promise => { @@ -148,7 +143,7 @@ export const useWatchPendingTransactions = ({ } if (updatedTransaction?.status !== 'pending') { - refreshAssets(tx); + refreshAssets(); } return updatedTransaction; }, @@ -244,17 +239,34 @@ export const useWatchPendingTransactions = ({ }, ); - if (minedTransactions.length) { - await queryClient.refetchQueries({ - queryKey: consolidatedTransactionsQueryKey({ - address, - currency: currentCurrency, - userChainIds: Object.keys(userChains).map(Number), - }), - }); - } - minedTransactions.forEach((minedTransaction) => { + if (minedTransaction.changes?.length) { + minedTransaction.changes?.forEach((change) => { + const changedAsset = change?.asset; + const changedAssetAddress = changedAsset?.address as Address; + if (changedAsset) { + addStaleBalance({ + address, + chainId: changedAsset?.chainId, + info: { + address: changedAssetAddress, + transactionHash: minedTransaction.hash, + }, + }); + } + }); + } else if (minedTransaction.asset) { + const changedAsset = minedTransaction.asset; + const changedAssetAddress = changedAsset?.address as Address; + addStaleBalance({ + address, + chainId: changedAsset?.chainId, + info: { + address: changedAssetAddress, + transactionHash: minedTransaction.hash, + }, + }); + } if (isCustomChain(minedTransaction.chainId)) { addCustomNetworkTransactions({ address, @@ -264,11 +276,29 @@ export const useWatchPendingTransactions = ({ } }); + if (minedTransactions.length) { + await queryClient.refetchQueries({ + queryKey: consolidatedTransactionsQueryKey({ + address, + currency: currentCurrency, + userChainIds: Object.keys(userChains).map(Number), + }), + }); + await queryClient.refetchQueries({ + queryKey: userAssetsQueryKey({ + address, + currency: currentCurrency, + testnetMode, + }), + }); + } + setPendingTransactions({ address, pendingTransactions: newPendingTransactions, }); }, [ + addStaleBalance, addCustomNetworkTransactions, address, currentCurrency, @@ -276,6 +306,7 @@ export const useWatchPendingTransactions = ({ processNonces, processPendingTransaction, setPendingTransactions, + testnetMode, userChains, ]); diff --git a/src/entries/popup/pages/messages/SendTransaction/index.tsx b/src/entries/popup/pages/messages/SendTransaction/index.tsx index f699234170..762b950416 100644 --- a/src/entries/popup/pages/messages/SendTransaction/index.tsx +++ b/src/entries/popup/pages/messages/SendTransaction/index.tsx @@ -112,8 +112,8 @@ export function SendTransaction({ } satisfies NewTransaction; addNewTransaction({ - address: txData.from as Address, - chainId: txData.chainId as ChainId, + address: txData.from, + chainId: txData.chainId, transaction, }); approveRequest(result.hash);