From a55654c18bab4fb78c19c95f3adbac5bb6a31d45 Mon Sep 17 00:00:00 2001 From: Martin Homola Date: Thu, 14 Nov 2024 18:37:16 +0100 Subject: [PATCH] fix(trade): pending transaction and re-rendering --- .../form/useCoinmarketExchangeForm.ts | 156 ++++++++++-------- .../useCoinmarketExchangeWatchSendApproval.ts | 26 +-- .../CoinmarketOfferExchangeSendApproval.tsx | 27 ++- 3 files changed, 123 insertions(+), 86 deletions(-) diff --git a/packages/suite/src/hooks/wallet/coinmarket/form/useCoinmarketExchangeForm.ts b/packages/suite/src/hooks/wallet/coinmarket/form/useCoinmarketExchangeForm.ts index 41202612810..82958ac7b2a 100644 --- a/packages/suite/src/hooks/wallet/coinmarket/form/useCoinmarketExchangeForm.ts +++ b/packages/suite/src/hooks/wallet/coinmarket/form/useCoinmarketExchangeForm.ts @@ -342,86 +342,100 @@ export const useCoinmarketExchangeForm = ({ } }; - const confirmTrade = async (address: string, extraField?: string, trade?: ExchangeTrade) => { - analytics.report({ - type: EventType.CoinmarketConfirmTrade, - payload: { - type, - }, - }); + const confirmTrade = useCallback( + async (address: string, extraField?: string, trade?: ExchangeTrade) => { + analytics.report({ + type: EventType.CoinmarketConfirmTrade, + payload: { + type, + }, + }); - let ok = false; - const { address: refundAddress } = getUnusedAddressFromAccount(account); - if (!trade) { - trade = selectedQuote; - } - if (!trade || !refundAddress) return false; + let ok = false; + const { address: refundAddress } = getUnusedAddressFromAccount(account); + if (!trade) { + trade = selectedQuote; + } + if (!trade || !refundAddress) return false; - if (trade.isDex && !trade.fromAddress) { - trade = { ...trade, fromAddress: refundAddress }; - } + if (trade.isDex && !trade.fromAddress) { + trade = { ...trade, fromAddress: refundAddress }; + } - setCallInProgress(true); - const response = await invityAPI.doExchangeTrade({ - trade, - receiveAddress: address, - refundAddress, - extraField, - }); + setCallInProgress(true); + const response = await invityAPI.doExchangeTrade({ + trade, + receiveAddress: address, + refundAddress, + extraField, + }); - if (!response) { - dispatch( - notificationsActions.addToast({ - type: 'error', - error: 'No response from the server', - }), - ); - } else if ( - response.error || - !response.status || - !response.orderId || - response.status === 'ERROR' - ) { - dispatch( - notificationsActions.addToast({ - type: 'error', - error: response.error || 'Error response from the server', - }), - ); - dispatch(coinmarketExchangeActions.saveSelectedQuote(response)); - } else if (response.status === 'APPROVAL_REQ' || response.status === 'APPROVAL_PENDING') { - dispatch(coinmarketExchangeActions.saveSelectedQuote(response)); - setExchangeStep('SEND_APPROVAL_TRANSACTION'); - ok = true; - } else if (response.status === 'CONFIRM') { - dispatch(coinmarketExchangeActions.saveSelectedQuote(response)); - if (response.isDex) { - if (exchangeStep === 'RECEIVING_ADDRESS' || trade.approvalType === 'ZERO') { - setExchangeStep('SEND_APPROVAL_TRANSACTION'); + if (!response) { + dispatch( + notificationsActions.addToast({ + type: 'error', + error: 'No response from the server', + }), + ); + } else if ( + response.error || + !response.status || + !response.orderId || + response.status === 'ERROR' + ) { + dispatch( + notificationsActions.addToast({ + type: 'error', + error: response.error || 'Error response from the server', + }), + ); + dispatch(coinmarketExchangeActions.saveSelectedQuote(response)); + } else if ( + response.status === 'APPROVAL_REQ' || + response.status === 'APPROVAL_PENDING' + ) { + dispatch(coinmarketExchangeActions.saveSelectedQuote(response)); + setExchangeStep('SEND_APPROVAL_TRANSACTION'); + ok = true; + } else if (response.status === 'CONFIRM') { + dispatch(coinmarketExchangeActions.saveSelectedQuote(response)); + if (response.isDex) { + if (exchangeStep === 'RECEIVING_ADDRESS' || trade.approvalType === 'ZERO') { + setExchangeStep('SEND_APPROVAL_TRANSACTION'); + } else { + setExchangeStep('SEND_TRANSACTION'); + } } else { setExchangeStep('SEND_TRANSACTION'); } + ok = true; } else { - setExchangeStep('SEND_TRANSACTION'); + // CONFIRMING, SUCCESS + dispatch( + coinmarketExchangeActions.saveTrade( + response, + selectedAccount.account, + new Date().toISOString(), + ), + ); + dispatch(coinmarketExchangeActions.saveTransactionId(response.orderId)); + ok = true; + navigateToExchangeDetail(); } - ok = true; - } else { - // CONFIRMING, SUCCESS - dispatch( - coinmarketExchangeActions.saveTrade( - response, - selectedAccount.account, - new Date().toISOString(), - ), - ); - dispatch(coinmarketExchangeActions.saveTransactionId(response.orderId)); - ok = true; - navigateToExchangeDetail(); - } - setCallInProgress(false); + setCallInProgress(false); - return ok; - }; + return ok; + }, + [ + account, + selectedQuote, + exchangeStep, + selectedAccount.account, + dispatch, + setCallInProgress, + navigateToExchangeDetail, + ], + ); const sendDexTransaction = async () => { if ( @@ -592,7 +606,7 @@ export const useCoinmarketExchangeForm = ({ } checkQuotesTimer(handleChange); - }); + }, [quotesRequest, isNotFormPage, navigateToExchangeForm, checkQuotesTimer, handleChange]); useEffect(() => { return () => { diff --git a/packages/suite/src/hooks/wallet/coinmarket/form/useCoinmarketExchangeWatchSendApproval.ts b/packages/suite/src/hooks/wallet/coinmarket/form/useCoinmarketExchangeWatchSendApproval.ts index e10bf181a8d..775db07a64c 100644 --- a/packages/suite/src/hooks/wallet/coinmarket/form/useCoinmarketExchangeWatchSendApproval.ts +++ b/packages/suite/src/hooks/wallet/coinmarket/form/useCoinmarketExchangeWatchSendApproval.ts @@ -3,9 +3,10 @@ import { useTimeoutFn, useUnmount } from 'react-use'; import { ExchangeTrade } from 'invity-api'; -import { useDispatch } from 'src/hooks/suite'; import invityAPI from 'src/services/suite/invityAPI'; import { CoinmarketTradeExchangeType } from 'src/types/coinmarket/coinmarket'; +import { useDispatch } from 'src/hooks/suite'; +import { saveSelectedQuote } from 'src/actions/wallet/coinmarketExchangeActions'; interface CoinmarketUseExchangeWatchSendApprovalProps { selectedQuote?: ExchangeTrade; @@ -49,19 +50,24 @@ export const useCoinmarketExchangeWatchSendApproval = ({ refreshCount, ); - if (!selectedQuote.dexTx) return; - if (!response.status || response.status === selectedQuote.status) return; + if (response.status && response.status !== selectedQuote.status) { + const updatedSelectedQuote = { + ...selectedQuote, + status: response.status, + error: response.error, + approvalType: undefined, + }; + + dispatch(saveSelectedQuote(updatedSelectedQuote)); - await confirmTrade(selectedQuote.dexTx.from, undefined, { - ...selectedQuote, - status: response.status, - error: response.error, - approvalType: undefined, - }); + if (selectedQuote.dexTx) { + await confirmTrade(selectedQuote.dexTx.from, undefined, updatedSelectedQuote); + } + } resetRefresh(); }; watchTradeAsync(); - }, [refreshCount, selectedQuote, cancelRefresh, confirmTrade, resetRefresh, dispatch]); + }, [refreshCount, selectedQuote, cancelRefresh, resetRefresh, dispatch, confirmTrade]); }; diff --git a/packages/suite/src/views/wallet/coinmarket/common/CoinmarketSelectedOffer/CoinmarketOfferExchange/CoinmarketOfferExchangeSendApproval.tsx b/packages/suite/src/views/wallet/coinmarket/common/CoinmarketSelectedOffer/CoinmarketOfferExchange/CoinmarketOfferExchangeSendApproval.tsx index d78d4038814..2d1f8bb89e0 100644 --- a/packages/suite/src/views/wallet/coinmarket/common/CoinmarketSelectedOffer/CoinmarketOfferExchange/CoinmarketOfferExchangeSendApproval.tsx +++ b/packages/suite/src/views/wallet/coinmarket/common/CoinmarketSelectedOffer/CoinmarketOfferExchange/CoinmarketOfferExchangeSendApproval.tsx @@ -23,6 +23,9 @@ import { CoinmarketTradeExchangeType } from 'src/types/coinmarket/coinmarket'; import { useCoinmarketFormContext } from 'src/hooks/wallet/coinmarket/form/useCoinmarketCommonForm'; import { useCoinmarketInfo } from 'src/hooks/wallet/coinmarket/useCoinmarketInfo'; import { useCoinmarketExchangeWatchSendApproval } from 'src/hooks/wallet/coinmarket/form/useCoinmarketExchangeWatchSendApproval'; +import { useDispatch } from 'src/hooks/suite'; +import { saveSelectedQuote } from 'src/actions/wallet/coinmarketExchangeActions'; +import { parseCryptoId } from 'src/utils/wallet/coinmarket/coinmarketUtils'; // add APPROVED means no approval request is necessary type ExtendedDexApprovalType = DexApprovalType | 'APPROVED'; @@ -32,6 +35,7 @@ const BreakableValue = styled.span` `; export const CoinmarketOfferExchangeSendApproval = () => { + const dispatch = useDispatch(); const { account, callInProgress, @@ -62,10 +66,11 @@ export const CoinmarketOfferExchangeSendApproval = () => { exchangeInfo?.providerInfos[exchange]?.companyName || selectedQuote.exchange; const isFullApproval = !(Number(selectedQuote.preapprovedStringAmount) > 0); - const isToken = selectedQuote.send !== account.symbol.toUpperCase(); if (!selectedQuote.send) return null; + const isToken = parseCryptoId(selectedQuote.send)?.contractAddress !== undefined; + if (isFullApproval && approvalType === 'ZERO') { setApprovalType('MINIMAL'); } @@ -79,20 +84,32 @@ export const CoinmarketOfferExchangeSendApproval = () => { const selectApprovalValue = async (type: ExtendedDexApprovalType) => { setApprovalType(type); if (type !== 'APPROVED') { - await confirmTrade(dexTx.from, undefined, { + const updatedSelectedQuote = { ...selectedQuote, approvalType: type, - }); + }; + + dispatch(saveSelectedQuote(updatedSelectedQuote)); + + await confirmTrade(dexTx.from, undefined, updatedSelectedQuote); } }; // if the last step was change in approval, we have to recompute the swap request const proceedToSwap = async () => { if (selectedQuote.approvalType) { - const confirmedTrade = await confirmTrade(dexTx.from, undefined, { + const updatedSelectedQuote = { ...selectedQuote, approvalType: undefined, - }); + }; + dispatch( + saveSelectedQuote({ + ...selectedQuote, + approvalType: undefined, + }), + ); + + const confirmedTrade = await confirmTrade(dexTx.from, undefined, updatedSelectedQuote); if (!confirmedTrade) { return;