From b11ff5d34c3652ed7e0cdf4fc79dc0e9cbff4418 Mon Sep 17 00:00:00 2001 From: piersss Date: Tue, 2 Apr 2024 21:42:27 +0200 Subject: [PATCH 1/7] 869: Added ApproveEvent and WETHEvent entities. Added useLatestApproveFromEvents, useLatestDepositOrWithdrawFromEvents, useLastSucceededTransaction, useLatestSwapFromEvents hooks. --- src/entities/ApproveEvent/ApproveEvent.ts | 7 ++ .../ApproveEvent/ApproveEventHelpers.ts | 25 +++++ .../ApproveEvent/ApproveEventTransformers.ts | 17 ++++ .../ExtendedFullSwapERC20.ts | 9 ++ .../ExtendedFullSwapERC20Helpers.ts | 10 ++ .../ExtendedFullSwapERC20Transformers.ts | 19 ++++ src/entities/WETHEvent/WETHEvent.ts | 8 ++ src/entities/WETHEvent/WETHEventHelpers.ts | 28 ++++++ .../WETHEvent/WETHEventTransformers.ts | 16 +++ src/features/balances/balancesSlice.ts | 1 - .../hooks/useHistoricalTransactions.ts | 7 +- .../hooks/useLatestApproveFromEvents.ts | 96 ++++++++++++++++++ .../useLatestDepositOrWithdrawFromEvents.ts | 99 +++++++++++++++++++ .../hooks/useLatestSucceededTransaction.ts | 64 ++++++++++++ .../hooks/useLatestSwapFromEvents.ts | 89 +++++++++++++++++ .../transactions/hooks/useSwapLogs.ts | 2 +- .../transactions/transactionsActions.ts | 4 +- .../transactions/transactionsHelpers.ts | 82 ++++++++++++++- .../transactions/transactionsHooks.ts | 41 +++++++- .../transactions/transactionsSlice.ts | 21 ++-- .../transactions/transactionsUtils.ts | 14 +-- src/helpers/array.ts | 6 ++ src/helpers/string.ts | 6 ++ src/hooks/useApprovalPending.ts | 1 - src/hooks/useNativeWrappedToken.ts | 1 - src/types/wethEventType.ts | 4 + 26 files changed, 649 insertions(+), 28 deletions(-) create mode 100644 src/entities/ApproveEvent/ApproveEvent.ts create mode 100644 src/entities/ApproveEvent/ApproveEventHelpers.ts create mode 100644 src/entities/ApproveEvent/ApproveEventTransformers.ts create mode 100644 src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20.ts create mode 100644 src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Helpers.ts create mode 100644 src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers.ts create mode 100644 src/entities/WETHEvent/WETHEvent.ts create mode 100644 src/entities/WETHEvent/WETHEventHelpers.ts create mode 100644 src/entities/WETHEvent/WETHEventTransformers.ts create mode 100644 src/features/transactions/hooks/useLatestApproveFromEvents.ts create mode 100644 src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts create mode 100644 src/features/transactions/hooks/useLatestSucceededTransaction.ts create mode 100644 src/features/transactions/hooks/useLatestSwapFromEvents.ts create mode 100644 src/helpers/string.ts create mode 100644 src/types/wethEventType.ts diff --git a/src/entities/ApproveEvent/ApproveEvent.ts b/src/entities/ApproveEvent/ApproveEvent.ts new file mode 100644 index 000000000..d2c115548 --- /dev/null +++ b/src/entities/ApproveEvent/ApproveEvent.ts @@ -0,0 +1,7 @@ +export interface ApproveEvent { + amount: string; + hash: string; + spenderAddress: string; + status?: number; + tokenAddress: string; +} diff --git a/src/entities/ApproveEvent/ApproveEventHelpers.ts b/src/entities/ApproveEvent/ApproveEventHelpers.ts new file mode 100644 index 000000000..35bb8ef28 --- /dev/null +++ b/src/entities/ApproveEvent/ApproveEventHelpers.ts @@ -0,0 +1,25 @@ +import { SubmittedApprovalTransaction } from "../SubmittedTransaction/SubmittedTransaction"; +import { ApproveEvent } from "./ApproveEvent"; + +export const isApproveEvent = (event: any): event is ApproveEvent => + typeof event === "object" && + "amount" in event && + "hash" in event && + "spenderAddress" in event && + "tokenAddress" in event; + +export const findMatchingApprovalTransaction = ( + transaction: SubmittedApprovalTransaction, + event: ApproveEvent +): boolean => { + if (transaction.hash === event.hash) { + return true; + } + + // If hash doesn't match, check if the token address and amount match, it's safely to assume + // the transaction was replaced + return ( + transaction.tokenAddress === event.tokenAddress && + transaction.amount === event.amount + ); +}; diff --git a/src/entities/ApproveEvent/ApproveEventTransformers.ts b/src/entities/ApproveEvent/ApproveEventTransformers.ts new file mode 100644 index 000000000..88456175d --- /dev/null +++ b/src/entities/ApproveEvent/ApproveEventTransformers.ts @@ -0,0 +1,17 @@ +import { ApproveEvent } from "./ApproveEvent"; + +export const transformToApproveEvent = ( + amount: string, + hash: string, + spenderAddress: string, + tokenAddress: string, + status?: number +): ApproveEvent => { + return { + amount, + hash, + spenderAddress, + tokenAddress, + status, + }; +}; diff --git a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20.ts b/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20.ts new file mode 100644 index 000000000..a77c2fa4b --- /dev/null +++ b/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20.ts @@ -0,0 +1,9 @@ +import { FullSwapERC20 } from "@airswap/utils/build/src/swap-erc20"; + +export interface ExtendedFullSwapERC20 { + hash: string; + senderWallet: string; + status?: number; + swap: FullSwapERC20; + timestamp: number; +} diff --git a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Helpers.ts b/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Helpers.ts new file mode 100644 index 000000000..1c1debb8b --- /dev/null +++ b/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Helpers.ts @@ -0,0 +1,10 @@ +import { ExtendedFullSwapERC20 } from "./ExtendedFullSwapERC20"; + +export const isExtendedFullSwapERC20Event = ( + event: any +): event is ExtendedFullSwapERC20 => + typeof event === "object" && + "hash" in event && + "senderWallet" in event && + "swap" in event && + "timestamp" in event; diff --git a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers.ts b/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers.ts new file mode 100644 index 000000000..ae9804767 --- /dev/null +++ b/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers.ts @@ -0,0 +1,19 @@ +import { FullSwapERC20 } from "@airswap/utils/build/src/swap-erc20"; + +import { ExtendedFullSwapERC20 } from "./ExtendedFullSwapERC20"; + +export const transformToExtendedFullSwapERC20 = ( + swap: FullSwapERC20, + hash: string, + senderWallet: string, + timestamp: number, + status?: number +): ExtendedFullSwapERC20 => { + return { + hash, + senderWallet, + swap, + timestamp, + status, + }; +}; diff --git a/src/entities/WETHEvent/WETHEvent.ts b/src/entities/WETHEvent/WETHEvent.ts new file mode 100644 index 000000000..c73ddaf84 --- /dev/null +++ b/src/entities/WETHEvent/WETHEvent.ts @@ -0,0 +1,8 @@ +import { WethEventType } from "../../types/wethEventType"; + +export interface WETHEvent { + type: WethEventType; + amount: string; + hash: string; + status?: number; +} diff --git a/src/entities/WETHEvent/WETHEventHelpers.ts b/src/entities/WETHEvent/WETHEventHelpers.ts new file mode 100644 index 000000000..f4cb7871d --- /dev/null +++ b/src/entities/WETHEvent/WETHEventHelpers.ts @@ -0,0 +1,28 @@ +import { WethEventType } from "../../types/wethEventType"; +import { ApproveEvent } from "../ApproveEvent/ApproveEvent"; +import { + SubmittedApprovalTransaction, + SubmittedDepositTransaction, + SubmittedWithdrawTransaction, +} from "../SubmittedTransaction/SubmittedTransaction"; +import { WETHEvent } from "./WETHEvent"; + +export const isWETHEvent = (event: any): event is WETHEvent => + typeof event === "object" && + "type" in event && + (event.type === WethEventType.deposit || + event.type === WethEventType.withdrawal) && + "amount" in event && + "hash" in event; + +export const findMatchingDepositOrWithdrawTransaction = ( + transaction: SubmittedDepositTransaction | SubmittedWithdrawTransaction, + event: WETHEvent +): boolean => { + if (transaction.hash === event.hash) { + return true; + } + + // If hash doesn't match, check if the amount matches, it's safely to assume the transaction was replaced + return transaction.order.signerAmount === event.amount; +}; diff --git a/src/entities/WETHEvent/WETHEventTransformers.ts b/src/entities/WETHEvent/WETHEventTransformers.ts new file mode 100644 index 000000000..c9affa782 --- /dev/null +++ b/src/entities/WETHEvent/WETHEventTransformers.ts @@ -0,0 +1,16 @@ +import { WethEventType } from "../../types/wethEventType"; +import { WETHEvent } from "./WETHEvent"; + +export const transformToDepositOrWithdrawEvent = ( + type: WethEventType, + amount: string, + hash: string, + status?: number +): WETHEvent => { + return { + type, + amount, + hash, + status, + }; +}; diff --git a/src/features/balances/balancesSlice.ts b/src/features/balances/balancesSlice.ts index 847373dce..e19b723e6 100644 --- a/src/features/balances/balancesSlice.ts +++ b/src/features/balances/balancesSlice.ts @@ -1,4 +1,3 @@ -import { WETH } from "@airswap/libraries"; import { ADDRESS_ZERO } from "@airswap/utils"; import { AsyncThunk, diff --git a/src/features/transactions/hooks/useHistoricalTransactions.ts b/src/features/transactions/hooks/useHistoricalTransactions.ts index bbd3dd516..31789c708 100644 --- a/src/features/transactions/hooks/useHistoricalTransactions.ts +++ b/src/features/transactions/hooks/useHistoricalTransactions.ts @@ -6,6 +6,7 @@ import { sortSubmittedTransactionsByExpiry } from "../../../entities/SubmittedTr import { transformToSubmittedRFQOrder } from "../../../entities/SubmittedTransaction/SubmittedTransactionTransformers"; import { getUniqueArrayChildren } from "../../../helpers/array"; import { getOrdersFromLogs } from "../../../helpers/getOrdersFromLogs"; +import { compareAddresses } from "../../../helpers/string"; import { selectAllTokenInfo } from "../../metadata/metadataSlice"; import useSwapLogs from "./useSwapLogs"; @@ -17,7 +18,7 @@ interface HistoricalTransactionsCollection { const useHistoricalTransactions = ( chainId?: number, - account?: string + account?: string | null ): [HistoricalTransactionsCollection | undefined, boolean] => { const tokens = useAppSelector(selectAllTokenInfo); const { result: swapLogs, status: swapLogStatus } = useSwapLogs( @@ -52,8 +53,8 @@ const useHistoricalTransactions = ( const rfqSubmittedTransactions = rfqOrders .filter( (order) => - order.params.signerWallet.toLowerCase() === account.toLowerCase() || - order.senderWallet.toLowerCase() === account.toLowerCase() + compareAddresses(order.params.signerWallet, account) || + compareAddresses(order.senderWallet, account) ) .map((order) => { const signerToken = tokens.find( diff --git a/src/features/transactions/hooks/useLatestApproveFromEvents.ts b/src/features/transactions/hooks/useLatestApproveFromEvents.ts new file mode 100644 index 000000000..7850a995c --- /dev/null +++ b/src/features/transactions/hooks/useLatestApproveFromEvents.ts @@ -0,0 +1,96 @@ +import { useEffect, useState } from "react"; + +import { SwapERC20 } from "@airswap/libraries"; +import { useWeb3React } from "@web3-react/core"; + +import erc20Abi from "erc-20-abi"; +import { BigNumber, providers, Event, EventFilter, ethers } from "ethers"; +import { hexZeroPad, id } from "ethers/lib/utils"; + +import { useAppSelector } from "../../../app/hooks"; +import { ApproveEvent } from "../../../entities/ApproveEvent/ApproveEvent"; +import { transformToApproveEvent } from "../../../entities/ApproveEvent/ApproveEventTransformers"; +import { compareAddresses } from "../../../helpers/string"; +import useDebounce from "../../../hooks/useDebounce"; +import { selectActiveTokens } from "../../metadata/metadataSlice"; + +const erc20Interface = new ethers.utils.Interface(erc20Abi); + +const useLatestApproveFromEvents = ( + chainId?: number, + account?: string | null +): ApproveEvent | undefined => { + const { library: provider } = useWeb3React(); + const tokens = useAppSelector(selectActiveTokens); + + const [accountState, setAccountState] = useState(); + const [chainIdState, setChainIdState] = useState(); + const [latestApprove, setLatestApprove] = useState(); + const [debouncedLatestApprove, setDebouncedLatestApprove] = + useState(); + + useDebounce( + () => { + setDebouncedLatestApprove(latestApprove); + }, + 1000, + [latestApprove] + ); + + useEffect(() => { + if (!chainId || !account || !provider) return; + + if (account === accountState && chainId === chainIdState) return; + + const filter: EventFilter = { + topics: [ + [id("Approval(address,address,uint256)")], + hexZeroPad(account, 32), + ], + }; + + const handleEvent = async (event: Event) => { + const transaction = await event.getTransactionReceipt(); + const tokenAddress = event.address; + const parsedEvent = erc20Interface.parseLog(event); + const spenderAddress = parsedEvent.args[1]; + const isApproval = parsedEvent.name === "Approval"; + const amount: BigNumber = parsedEvent.args[2]; + + if (!isApproval) return; + + if ( + !tokens.some((token) => compareAddresses(token.address, tokenAddress)) + ) + return; + + if ( + !compareAddresses(spenderAddress, SwapERC20.getAddress(chainId) || "") + ) + return; + + setLatestApprove( + transformToApproveEvent( + amount.toString(), + transaction.transactionHash, + spenderAddress, + tokenAddress, + transaction.status + ) + ); + }; + + setAccountState(account); + setChainIdState(chainId); + + provider.on(filter, handleEvent); + + return () => { + provider.off(filter, handleEvent); + }; + }, [chainId, account, provider]); + + return debouncedLatestApprove; +}; + +export default useLatestApproveFromEvents; diff --git a/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts b/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts new file mode 100644 index 000000000..bee41535a --- /dev/null +++ b/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts @@ -0,0 +1,99 @@ +import { useEffect, useState } from "react"; + +import { WETH } from "@airswap/libraries"; +import { useWeb3React } from "@web3-react/core"; + +import { BigNumber, providers, Event } from "ethers"; + +import { WETHEvent } from "../../../entities/WETHEvent/WETHEvent"; +import { transformToDepositOrWithdrawEvent } from "../../../entities/WETHEvent/WETHEventTransformers"; +import { compareAddresses } from "../../../helpers/string"; +import useDebounce from "../../../hooks/useDebounce"; +import { WethEventType } from "../../../types/wethEventType"; + +const useLatestDepositOrWithdrawFromEvents = ( + chainId?: number, + account?: string | null +): WETHEvent | undefined => { + const { library: provider } = useWeb3React(); + + const [accountState, setAccountState] = useState(); + const [chainIdState, setChainIdState] = useState(); + const [latestDepositOrWithdraw, setLatestDepositOrWithdraw] = + useState(); + const [ + debouncedLatestDepositOrWithdraw, + setDebouncedLatestDepositOrWithdraw, + ] = useState(); + + useDebounce( + () => { + setDebouncedLatestDepositOrWithdraw(latestDepositOrWithdraw); + }, + 1000, + [latestDepositOrWithdraw] + ); + + useEffect(() => { + if (!chainId || !account || !provider) return; + + if (account === accountState && chainId === chainIdState) return; + + const wethContract = WETH.getContract(provider, chainId); + console.log(wethContract); + const depositEvent = "Deposit"; + const withdrawEvent = "Withdrawal"; + + const handleEvent = async ( + type: WethEventType, + from: string, + value: BigNumber, + depositEvent: Event + ) => { + if (!compareAddresses(from, account)) { + return; + } + + const receipt = await depositEvent.getTransactionReceipt(); + setLatestDepositOrWithdraw( + transformToDepositOrWithdrawEvent( + type, + value.toString(), + depositEvent.transactionHash, + receipt.status + ) + ); + }; + + const handleDepositEvent = async ( + from: string, + value: BigNumber, + depositEvent: Event + ) => { + handleEvent(WethEventType.deposit, from, value, depositEvent); + }; + + const handleWithdrawEvent = async ( + from: string, + value: BigNumber, + depositEvent: Event + ) => { + handleEvent(WethEventType.withdrawal, from, value, depositEvent); + }; + + wethContract.on(depositEvent, handleDepositEvent); + wethContract.on(withdrawEvent, handleWithdrawEvent); + + setAccountState(account); + setChainIdState(chainId); + + return () => { + wethContract.off(depositEvent, handleDepositEvent); + wethContract.off(withdrawEvent, handleWithdrawEvent); + }; + }, [chainId, account, provider]); + + return debouncedLatestDepositOrWithdraw; +}; + +export default useLatestDepositOrWithdrawFromEvents; diff --git a/src/features/transactions/hooks/useLatestSucceededTransaction.ts b/src/features/transactions/hooks/useLatestSucceededTransaction.ts new file mode 100644 index 000000000..deaf31236 --- /dev/null +++ b/src/features/transactions/hooks/useLatestSucceededTransaction.ts @@ -0,0 +1,64 @@ +import { useEffect, useState } from "react"; + +import { useWeb3React } from "@web3-react/core"; + +import { useAppSelector } from "../../../app/hooks"; +import { SubmittedTransaction } from "../../../entities/SubmittedTransaction/SubmittedTransaction"; +import { getUniqueSingleDimensionArray } from "../../../helpers/array"; +import { + selectPendingTransactions, + selectSuccessfulTransactions, +} from "../transactionsSlice"; + +const useLatestSucceededTransaction = () => { + const { chainId, account, library } = useWeb3React(); + + const pendingTransactions = useAppSelector(selectPendingTransactions); + const successfulTransactions = useAppSelector(selectSuccessfulTransactions); + + const [pendingTransactionHashes, setPendingTransactionHashes] = useState< + string[] + >([]); + const [latestSuccessfulTransaction, setLatestSuccessfulTransaction] = + useState(); + + useEffect(() => { + const transaction = successfulTransactions.find((transaction) => + pendingTransactionHashes.includes(transaction.hash || "") + ); + + if (transaction) { + setLatestSuccessfulTransaction(transaction); + } + }, [successfulTransactions]); + + useEffect(() => { + const newPendingTransactionNonces = [ + ...pendingTransactions.map((transaction) => transaction.hash || ""), + ...pendingTransactionHashes, + ] + .filter(Boolean) + .filter(getUniqueSingleDimensionArray); + + setPendingTransactionHashes(newPendingTransactionNonces); + }, [pendingTransactions]); + + useEffect(() => { + if (latestSuccessfulTransaction) { + setPendingTransactionHashes( + pendingTransactionHashes.filter( + (hash) => hash !== latestSuccessfulTransaction.hash + ) + ); + } + }, [latestSuccessfulTransaction]); + + useEffect(() => { + setPendingTransactionHashes([]); + setLatestSuccessfulTransaction(undefined); + }, [chainId, account]); + + return latestSuccessfulTransaction; +}; + +export default useLatestSucceededTransaction; diff --git a/src/features/transactions/hooks/useLatestSwapFromEvents.ts b/src/features/transactions/hooks/useLatestSwapFromEvents.ts new file mode 100644 index 000000000..034ffd158 --- /dev/null +++ b/src/features/transactions/hooks/useLatestSwapFromEvents.ts @@ -0,0 +1,89 @@ +import { useEffect, useState } from "react"; + +import { SwapERC20 } from "@airswap/libraries"; +import { getFullSwapERC20 } from "@airswap/utils"; +import { useWeb3React } from "@web3-react/core"; + +import { BigNumber, providers, Event } from "ethers"; + +import { ExtendedFullSwapERC20 } from "../../../entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20"; +import { transformToExtendedFullSwapERC20 } from "../../../entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers"; +import useDebounce from "../../../hooks/useDebounce"; + +const useLatestSwapFromEvents = ( + chainId?: number, + account?: string | null +): ExtendedFullSwapERC20 | undefined => { + const { library: provider } = useWeb3React(); + + const [accountState, setAccountState] = useState(); + const [chainIdState, setChainIdState] = useState(); + const [latestSwap, setLatestSwap] = useState(); + const [debouncedLatestSwap, setDebouncedLatestSwap] = + useState(); + + useDebounce( + () => { + setDebouncedLatestSwap(latestSwap); + }, + 1000, + [latestSwap] + ); + + useEffect(() => { + if (!chainId || !account || !provider) return; + + if (account === accountState && chainId === chainIdState) return; + + const swapContract = SwapERC20.getContract(provider, chainId); + const swapEvent = "SwapERC20"; + + swapContract.protocolFeeWallet().then((feeReceiver: string) => { + const handleSwapEvent = async ( + nonce: BigNumber, + signerWallet: string, + swapEvent: Event + ) => { + const receipt = await swapEvent.getTransactionReceipt(); + const swap = await getFullSwapERC20( + nonce.toString(), + signerWallet, + feeReceiver, + receipt.logs + ); + + if ( + swap.signerWallet.toLowerCase() === account.toLowerCase() || + swap.senderWallet.toLowerCase() === account.toLowerCase() + ) { + setLatestSwap( + transformToExtendedFullSwapERC20( + swap, + swapEvent.transactionHash, + signerWallet, + swapEvent.blockNumber, + receipt.status + ) + ); + } + }; + + swapContract.on(swapEvent, handleSwapEvent); + + return () => { + swapContract.off(swapEvent, handleSwapEvent); + }; + }); + + setAccountState(account); + setChainIdState(chainId); + + return () => { + swapContract.off(swapEvent, () => {}); + }; + }, [chainId, account, provider]); + + return debouncedLatestSwap; +}; + +export default useLatestSwapFromEvents; diff --git a/src/features/transactions/hooks/useSwapLogs.ts b/src/features/transactions/hooks/useSwapLogs.ts index ad9cf641c..09ae5aeeb 100644 --- a/src/features/transactions/hooks/useSwapLogs.ts +++ b/src/features/transactions/hooks/useSwapLogs.ts @@ -20,7 +20,7 @@ interface SwapLogs { const useSwapLogs = ( chainId?: number, - account?: string + account?: string | null ): IAsyncState => { const { library: provider } = useWeb3React(); diff --git a/src/features/transactions/transactionsActions.ts b/src/features/transactions/transactionsActions.ts index e213238f7..54d92bf8a 100644 --- a/src/features/transactions/transactionsActions.ts +++ b/src/features/transactions/transactionsActions.ts @@ -1,7 +1,5 @@ -import { createAction, createAsyncThunk } from "@reduxjs/toolkit"; +import { createAction } from "@reduxjs/toolkit"; -import { AppDispatch, RootState } from "../../app/store"; -import { ASSUMED_EXPIRY_NOTIFICATION_BUFFER_MS } from "../../constants/configParams"; import { ProtocolType, SubmittedApprovalTransaction, diff --git a/src/features/transactions/transactionsHelpers.ts b/src/features/transactions/transactionsHelpers.ts index a8dfa7612..c00b91af9 100644 --- a/src/features/transactions/transactionsHelpers.ts +++ b/src/features/transactions/transactionsHelpers.ts @@ -1,13 +1,31 @@ import { AppDispatch, RootState } from "../../app/store"; +import { ApproveEvent } from "../../entities/ApproveEvent/ApproveEvent"; +import { + findMatchingApprovalTransaction, + isApproveEvent, +} from "../../entities/ApproveEvent/ApproveEventHelpers"; +import { ExtendedFullSwapERC20 } from "../../entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20"; import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; +import { + isApprovalTransaction, + isDepositTransaction, + isWithdrawTransaction, +} from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; +import { WETHEvent } from "../../entities/WETHEvent/WETHEvent"; +import { + findMatchingDepositOrWithdrawTransaction, + isWETHEvent, +} from "../../entities/WETHEvent/WETHEventHelpers"; import { setTransactions } from "./transactionsSlice"; export const updateTransaction = - (updatedTransaction: SubmittedTransaction) => + (updatedTransaction: SubmittedTransaction, previousHash?: string) => async (dispatch: AppDispatch, getState: () => RootState): Promise => { const transactions = getState().transactions.transactions; const transactionIndex = transactions.findIndex( - (transaction) => transaction.hash === updatedTransaction.hash + (transaction) => + transaction.hash === updatedTransaction.hash || + transaction.hash === previousHash ); if (transactionIndex === -1) { @@ -19,3 +37,63 @@ export const updateTransaction = dispatch(setTransactions(updatedTransactions)); }; + +type TransactionEvent = ApproveEvent | ExtendedFullSwapERC20 | WETHEvent; + +const getMatchingTransaction = ( + event: TransactionEvent, + transactions: SubmittedTransaction[] +): SubmittedTransaction | undefined => { + if (isApproveEvent(event)) { + return transactions + .filter(isApprovalTransaction) + .find((transaction) => + findMatchingApprovalTransaction(transaction, event) + ); + } + + if (isWETHEvent(event)) { + return ( + transactions + .filter(isDepositTransaction) + .find((transaction) => + findMatchingDepositOrWithdrawTransaction(transaction, event) + ) || + transactions + .filter(isWithdrawTransaction) + .find((transaction) => + findMatchingDepositOrWithdrawTransaction(transaction, event) + ) + ); + } + + return undefined; +}; + +export const handleTransactionEvent = + (event: TransactionEvent) => + (dispatch: AppDispatch, getState: () => RootState): void => { + const transactions = getState().transactions.transactions; + const pendingTransactions = transactions.filter( + (transaction) => transaction.status === "processing" + ); + const matchingTransaction = getMatchingTransaction( + event, + pendingTransactions + ); + + if (!matchingTransaction) { + return; + } + + dispatch( + updateTransaction( + { + ...matchingTransaction, + hash: event.hash, + status: event.status === 1 ? "succeeded" : "declined", + }, + matchingTransaction.hash + ) + ); + }; diff --git a/src/features/transactions/transactionsHooks.ts b/src/features/transactions/transactionsHooks.ts index fcb75b15d..7d0cf25bc 100644 --- a/src/features/transactions/transactionsHooks.ts +++ b/src/features/transactions/transactionsHooks.ts @@ -7,6 +7,14 @@ import { SubmittedTransaction } from "../../entities/SubmittedTransaction/Submit import { sortSubmittedTransactionsByExpiry } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { getUniqueArrayChildren } from "../../helpers/array"; import useHistoricalTransactions from "./hooks/useHistoricalTransactions"; +import useLatestApproveFromEvents from "./hooks/useLatestApproveFromEvents"; +import useLatestDepositOrWithdrawFromEvents from "./hooks/useLatestDepositOrWithdrawFromEvents"; +import useLatestSucceededTransaction from "./hooks/useLatestSucceededTransaction"; +import useLatestSwapFromEvents from "./hooks/useLatestSwapFromEvents"; +import { + handleTransactionEvent, + updateTransaction, +} from "./transactionsHelpers"; import { selectTransactions, setTransactions } from "./transactionsSlice"; import { getLocalStorageTransactions, @@ -24,10 +32,14 @@ export const useTransactions = (): void => { const [activeListenerHashes, setActiveListenerHashes] = useState( [] ); - const [historicalTransactions] = useHistoricalTransactions( + const [historicalTransactions] = useHistoricalTransactions(chainId, account); + const latestSwap = useLatestSwapFromEvents(chainId, account); + const latestApprove = useLatestApproveFromEvents(chainId, account); + const latestDepositOrWithdraw = useLatestDepositOrWithdrawFromEvents( chainId, - account || undefined + account ); + const latestSuccessfulTransaction = useLatestSucceededTransaction(); useEffect(() => { if (!account || !chainId || !library) { @@ -86,4 +98,29 @@ export const useTransactions = (): void => { dispatch(setTransactions(sortedTransactions)); } }, [historicalTransactions]); + + useEffect(() => { + if (latestSwap) { + dispatch(handleTransactionEvent(latestSwap)); + } + }, [latestSwap]); + + useEffect(() => { + if (latestApprove) { + dispatch(handleTransactionEvent(latestApprove)); + } + }, [latestApprove]); + + useEffect(() => { + if (latestDepositOrWithdraw) { + dispatch(handleTransactionEvent(latestDepositOrWithdraw)); + } + }, [latestDepositOrWithdraw]); + + useEffect(() => { + if (latestSuccessfulTransaction) { + console.log("hop"); + console.log(latestSuccessfulTransaction); + } + }, [latestSuccessfulTransaction]); }; diff --git a/src/features/transactions/transactionsSlice.ts b/src/features/transactions/transactionsSlice.ts index 69166d9d5..ed6fcf9a1 100644 --- a/src/features/transactions/transactionsSlice.ts +++ b/src/features/transactions/transactionsSlice.ts @@ -234,13 +234,6 @@ export const selectFilteredTransactions = (state: RootState) => { ); }; -export const selectPendingTransactions = createSelector( - selectTransactions, - (transactions) => { - return transactions.filter((tx) => tx.status === "processing"); - } -); - export const selectOrderTransactions = createSelector( selectTransactions, (transactions) => { @@ -248,6 +241,20 @@ export const selectOrderTransactions = createSelector( } ); +export const selectPendingTransactions = ( + state: RootState +): SubmittedTransaction[] => + state.transactions.transactions.filter( + (tx) => tx.status === "processing" + ) as SubmittedTransaction[]; + +export const selectSuccessfulTransactions = ( + state: RootState +): SubmittedTransaction[] => + state.transactions.transactions.filter( + (tx) => tx.status === "succeeded" + ) as SubmittedTransaction[]; + export const selectPendingDeposits = ( state: RootState ): SubmittedDepositTransaction[] => diff --git a/src/features/transactions/transactionsUtils.ts b/src/features/transactions/transactionsUtils.ts index eb451fdec..fec7aadc8 100644 --- a/src/features/transactions/transactionsUtils.ts +++ b/src/features/transactions/transactionsUtils.ts @@ -147,11 +147,11 @@ export const listenForTransactionReceipt = async ( return; } - library.once(hash, async () => { - const receipt = await getTransactionReceipt(hash as string, library); - - if (receipt?.status !== undefined) { - handleTransactionReceipt(receipt, transaction, dispatch); - } - }); + // library.once(hash, async () => { + // const receipt = await getTransactionReceipt(hash as string, library); + // + // if (receipt?.status !== undefined) { + // handleTransactionReceipt(receipt, transaction, dispatch); + // } + // }); }; diff --git a/src/helpers/array.ts b/src/helpers/array.ts index 74d4d2aee..b9a873153 100644 --- a/src/helpers/array.ts +++ b/src/helpers/array.ts @@ -22,3 +22,9 @@ export const getUniqueArrayChildren = (array: T[], key: string): T[] => [ // @ts-ignore ...new Map(array.map((item: any) => [item[key], item])).values(), ]; + +export const getUniqueSingleDimensionArray = ( + value: T, + index: number, + array: T[] +) => array.indexOf(value) === index; diff --git a/src/helpers/string.ts b/src/helpers/string.ts new file mode 100644 index 000000000..2b8bf738d --- /dev/null +++ b/src/helpers/string.ts @@ -0,0 +1,6 @@ +export const compareAddresses = ( + address1: string, + address2: string +): boolean => { + return address1.toLowerCase() === address2.toLowerCase(); +}; diff --git a/src/hooks/useApprovalPending.ts b/src/hooks/useApprovalPending.ts index d086e93c5..0904b57a3 100644 --- a/src/hooks/useApprovalPending.ts +++ b/src/hooks/useApprovalPending.ts @@ -1,6 +1,5 @@ import { useMemo } from "react"; -import { WETH } from "@airswap/libraries"; import { ADDRESS_ZERO } from "@airswap/utils"; import { Web3Provider } from "@ethersproject/providers"; import { useWeb3React } from "@web3-react/core"; diff --git a/src/hooks/useNativeWrappedToken.ts b/src/hooks/useNativeWrappedToken.ts index 74e60e97d..7f4f3d2f3 100644 --- a/src/hooks/useNativeWrappedToken.ts +++ b/src/hooks/useNativeWrappedToken.ts @@ -1,6 +1,5 @@ import { useMemo } from "react"; -import { WETH } from "@airswap/libraries"; import { TokenInfo } from "@airswap/utils"; import { useAppSelector } from "../app/hooks"; diff --git a/src/types/wethEventType.ts b/src/types/wethEventType.ts new file mode 100644 index 000000000..ab94b3013 --- /dev/null +++ b/src/types/wethEventType.ts @@ -0,0 +1,4 @@ +export enum WethEventType { + deposit = "deposit", + withdrawal = "withdrawal", +} From a0b05be09fcb81b2dd23674bc35535eb3db9cfe0 Mon Sep 17 00:00:00 2001 From: piersss Date: Wed, 3 Apr 2024 21:55:21 +0200 Subject: [PATCH 2/7] 869: Added TransactionStatusType and TransactionType --- .../helpers/getWalletTransactionStatusText.ts | 14 ++-- .../ExtendedFullSwapERC20Helpers.ts | 10 --- ...FullSwapERC20.ts => FullSwapERC20Event.ts} | 2 +- .../FullSwapERC20EventHelpers.ts | 8 +++ ...s.ts => FullSwapERC20EventTransformers.ts} | 6 +- .../SubmittedTransaction.ts | 11 +-- .../SubmittedTransactionTransformers.ts | 14 ++-- src/features/orders/ordersActions.ts | 72 +++++++------------ src/features/takeOtc/takeOtcActions.ts | 9 +-- .../hooks/useHistoricalTransactions.ts | 3 +- .../useLatestDepositOrWithdrawFromEvents.ts | 1 - .../hooks/useLatestSwapFromEvents.ts | 12 ++-- .../transactions/transactionsHelpers.ts | 41 ++++++++++- .../transactions/transactionsHooks.ts | 4 +- .../transactions/transactionsSlice.ts | 18 +++-- .../transactions/transactionsUtils.ts | 58 ++++----------- src/types/transactionType.ts | 16 +++++ 17 files changed, 148 insertions(+), 151 deletions(-) delete mode 100644 src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Helpers.ts rename src/entities/ExtendedFullSwapERC20/{ExtendedFullSwapERC20.ts => FullSwapERC20Event.ts} (81%) create mode 100644 src/entities/ExtendedFullSwapERC20/FullSwapERC20EventHelpers.ts rename src/entities/ExtendedFullSwapERC20/{ExtendedFullSwapERC20Transformers.ts => FullSwapERC20EventTransformers.ts} (63%) create mode 100644 src/types/transactionType.ts diff --git a/src/components/TransactionsTab/helpers/getWalletTransactionStatusText.ts b/src/components/TransactionsTab/helpers/getWalletTransactionStatusText.ts index ff84cfccd..7539306ac 100644 --- a/src/components/TransactionsTab/helpers/getWalletTransactionStatusText.ts +++ b/src/components/TransactionsTab/helpers/getWalletTransactionStatusText.ts @@ -1,21 +1,21 @@ import { TFunction } from "react-i18next"; -import { StatusType } from "../../../entities/SubmittedTransaction/SubmittedTransaction"; +import { TransactionStatusType } from "../../../types/transactionType"; export default function getWalletTransactionStatusText( - status: StatusType, + status: TransactionStatusType, t: TFunction ): string { switch (status) { - case "succeeded": + case TransactionStatusType.succeeded: return t("common.success"); - case "processing": + case TransactionStatusType.processing: return t("common.processing"); - case "expired": + case TransactionStatusType.expired: return t("common.expired"); - case "reverted": + case TransactionStatusType.reverted: return t("common.failed"); - case "declined": + case TransactionStatusType.declined: return t("orders.swapRejected"); default: return t("common.unknown"); diff --git a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Helpers.ts b/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Helpers.ts deleted file mode 100644 index 1c1debb8b..000000000 --- a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Helpers.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ExtendedFullSwapERC20 } from "./ExtendedFullSwapERC20"; - -export const isExtendedFullSwapERC20Event = ( - event: any -): event is ExtendedFullSwapERC20 => - typeof event === "object" && - "hash" in event && - "senderWallet" in event && - "swap" in event && - "timestamp" in event; diff --git a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20.ts b/src/entities/ExtendedFullSwapERC20/FullSwapERC20Event.ts similarity index 81% rename from src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20.ts rename to src/entities/ExtendedFullSwapERC20/FullSwapERC20Event.ts index a77c2fa4b..ef69c9b6a 100644 --- a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20.ts +++ b/src/entities/ExtendedFullSwapERC20/FullSwapERC20Event.ts @@ -1,6 +1,6 @@ import { FullSwapERC20 } from "@airswap/utils/build/src/swap-erc20"; -export interface ExtendedFullSwapERC20 { +export interface FullSwapERC20Event { hash: string; senderWallet: string; status?: number; diff --git a/src/entities/ExtendedFullSwapERC20/FullSwapERC20EventHelpers.ts b/src/entities/ExtendedFullSwapERC20/FullSwapERC20EventHelpers.ts new file mode 100644 index 000000000..718919757 --- /dev/null +++ b/src/entities/ExtendedFullSwapERC20/FullSwapERC20EventHelpers.ts @@ -0,0 +1,8 @@ +import { FullSwapERC20Event } from "./FullSwapERC20Event"; + +export const isFullSwapERC20Event = (event: any): event is FullSwapERC20Event => + typeof event === "object" && + "hash" in event && + "senderWallet" in event && + "swap" in event && + "timestamp" in event; diff --git a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers.ts b/src/entities/ExtendedFullSwapERC20/FullSwapERC20EventTransformers.ts similarity index 63% rename from src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers.ts rename to src/entities/ExtendedFullSwapERC20/FullSwapERC20EventTransformers.ts index ae9804767..4b57481ab 100644 --- a/src/entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers.ts +++ b/src/entities/ExtendedFullSwapERC20/FullSwapERC20EventTransformers.ts @@ -1,14 +1,14 @@ import { FullSwapERC20 } from "@airswap/utils/build/src/swap-erc20"; -import { ExtendedFullSwapERC20 } from "./ExtendedFullSwapERC20"; +import { FullSwapERC20Event } from "./FullSwapERC20Event"; -export const transformToExtendedFullSwapERC20 = ( +export const transformToFullSwapERC20Event = ( swap: FullSwapERC20, hash: string, senderWallet: string, timestamp: number, status?: number -): ExtendedFullSwapERC20 => { +): FullSwapERC20Event => { return { hash, senderWallet, diff --git a/src/entities/SubmittedTransaction/SubmittedTransaction.ts b/src/entities/SubmittedTransaction/SubmittedTransaction.ts index 2b3a86172..df0cadcce 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransaction.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransaction.ts @@ -1,5 +1,7 @@ import { OrderERC20, TokenInfo } from "@airswap/utils"; +import { TransactionStatusType } from "../../types/transactionType"; + export interface DepositOrWithdrawOrder { signerToken: string; signerAmount: string; @@ -14,19 +16,12 @@ export type TransactionType = | "Withdraw" | "Cancel"; -export type StatusType = - | "processing" - | "succeeded" - | "reverted" - | "declined" - | "expired"; - export type ProtocolType = "request-for-quote-erc20" | "last-look-erc20"; export interface SubmittedTransaction { type: TransactionType; hash?: string; // LL orders doesn't have hash - status: StatusType; + status: TransactionStatusType; nonce?: string; expiry?: string; timestamp: number; diff --git a/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts b/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts index a9b9d4802..f4ce66852 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts @@ -1,13 +1,11 @@ import { OrderERC20, TokenInfo } from "@airswap/utils"; +import { TransactionStatusType } from "../../types/transactionType"; import { - ProtocolType, - StatusType, SubmittedApprovalTransaction, SubmittedDepositTransaction, SubmittedLastLookOrder, SubmittedRFQOrder, - SubmittedTransactionWithOrder, SubmittedWithdrawTransaction, } from "./SubmittedTransaction"; @@ -15,7 +13,7 @@ export const transformToSubmittedApprovalTransaction = ( hash: string, token: TokenInfo, amount: string, - status: StatusType = "processing" + status: TransactionStatusType = TransactionStatusType.processing ): SubmittedApprovalTransaction => { return { type: "Approval", @@ -33,7 +31,7 @@ export const transformToSubmittedDepositTransaction = ( wrappedToken: TokenInfo, nativeToken: TokenInfo, amount: string, - status: StatusType = "processing" + status: TransactionStatusType = TransactionStatusType.processing ): SubmittedDepositTransaction => { return { type: "Deposit", @@ -56,7 +54,7 @@ export const transformToSubmittedWithdrawTransaction = ( wrappedToken: TokenInfo, nativeToken: TokenInfo, amount: string, - status: StatusType = "processing" + status: TransactionStatusType = TransactionStatusType.processing ): SubmittedWithdrawTransaction => { return { type: "Withdraw", @@ -79,7 +77,7 @@ export const transformToSubmittedRFQOrder = ( order: OrderERC20, signerToken: TokenInfo, senderToken: TokenInfo, - status: StatusType = "processing", + status: TransactionStatusType = TransactionStatusType.processing, timestamp = Date.now() ): SubmittedRFQOrder => { return { @@ -101,7 +99,7 @@ export const transformToSubmittedLastLookOrder = ( order: OrderERC20, signerToken: TokenInfo, senderToken: TokenInfo, - status: StatusType = "processing", + status: TransactionStatusType = TransactionStatusType.processing, timestamp = Date.now() ): SubmittedLastLookOrder => { return { diff --git a/src/features/orders/ordersActions.ts b/src/features/orders/ordersActions.ts index 05d03e8f3..c4af1e53d 100644 --- a/src/features/orders/ordersActions.ts +++ b/src/features/orders/ordersActions.ts @@ -39,6 +39,7 @@ import transformUnknownErrorToAppError from "../../errors/transformUnknownErrorT import getWethAddress from "../../helpers/getWethAddress"; import toRoundedAtomicString from "../../helpers/toRoundedAtomicString"; import i18n from "../../i18n/i18n"; +import { TransactionStatusType } from "../../types/transactionType"; import { allowancesSwapActions, decrementBalanceBy, @@ -66,30 +67,16 @@ import { setStatus, } from "./ordersSlice"; -const failTransaction = ( - hash: string, - heading: string, - dispatch: AppDispatch -): void => { - dispatch(revertTransaction({ hash })); - - notifyError({ - heading, - cta: i18n.t("validatorErrors.unknownError"), - }); -}; - export const handleApproveTransaction = ( transaction: SubmittedApprovalTransaction, - receipt: TransactionReceipt, + status: TransactionStatusType, dispatch: AppDispatch ): void => { - if (receipt.status !== 1) { - failTransaction( - receipt.transactionHash, - i18n.t("toast.approvalFail"), - dispatch - ); + if (status === TransactionStatusType.failed) { + notifyError({ + heading: i18n.t("toast.approvalFail"), + cta: i18n.t("validatorErrors.unknownError"), + }); return; } @@ -99,7 +86,6 @@ export const handleApproveTransaction = ( transaction.token.decimals ); - dispatch(mineTransaction({ hash: transaction.hash })); dispatch( allowancesSwapActions.set({ tokenAddress: transaction.tokenAddress, @@ -112,21 +98,18 @@ export const handleApproveTransaction = ( export const handleSubmittedDepositOrder = ( transaction: SubmittedDepositTransaction, - receipt: TransactionReceipt, + status: TransactionStatusType, dispatch: AppDispatch ): void => { - if (receipt.status !== 1) { - failTransaction( - receipt.transactionHash, - i18n.t("toast.swapFail"), - dispatch - ); + if (status === TransactionStatusType.failed) { + notifyError({ + heading: i18n.t("toast.swapFail"), + cta: i18n.t("validatorErrors.unknownError"), + }); return; } - dispatch(mineTransaction({ hash: transaction.hash })); - // TODO: Balance handling should be done in balancesApi.ts https://github.com/airswap/airswap-web/issues/889 dispatch( @@ -148,21 +131,18 @@ export const handleSubmittedDepositOrder = ( export const handleSubmittedWithdrawOrder = ( transaction: SubmittedWithdrawTransaction, - receipt: TransactionReceipt, + status: TransactionStatusType, dispatch: AppDispatch ): void => { - if (receipt.status !== 1) { - failTransaction( - receipt.transactionHash, - i18n.t("toast.swapFail"), - dispatch - ); + if (status === TransactionStatusType.failed) { + notifyError({ + heading: i18n.t("toast.swapFail"), + cta: i18n.t("validatorErrors.unknownError"), + }); return; } - dispatch(mineTransaction({ hash: transaction.hash })); - // TODO: Balance handling should be done in balancesApi.ts https://github.com/airswap/airswap-web/issues/889 dispatch( @@ -184,15 +164,13 @@ export const handleSubmittedWithdrawOrder = ( export const handleSubmittedRFQOrder = ( transaction: SubmittedTransactionWithOrder, - receipt: TransactionReceipt, - dispatch: AppDispatch + status: TransactionStatusType ): void => { - if (receipt.status !== 1) { - failTransaction( - receipt.transactionHash, - i18n.t("toast.swapFail"), - dispatch - ); + if (status === TransactionStatusType.failed) { + notifyError({ + heading: i18n.t("toast.swapFail"), + cta: i18n.t("validatorErrors.unknownError"), + }); return; } diff --git a/src/features/takeOtc/takeOtcActions.ts b/src/features/takeOtc/takeOtcActions.ts index 7409462c9..17ab04486 100644 --- a/src/features/takeOtc/takeOtcActions.ts +++ b/src/features/takeOtc/takeOtcActions.ts @@ -1,20 +1,21 @@ import { SwapERC20 } from "@airswap/libraries"; import { decompressFullOrderERC20, - isValidFullOrderERC20, FullOrderERC20, + isValidFullOrderERC20, } from "@airswap/utils"; import { createAsyncThunk } from "@reduxjs/toolkit"; import { providers } from "ethers"; import { - notifyRejectedByUserError, - notifyError, notifyConfirmation, + notifyError, + notifyRejectedByUserError, } from "../../components/Toasts/ToastController"; import { SubmittedCancellation } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import i18n from "../../i18n/i18n"; +import { TransactionStatusType } from "../../types/transactionType"; import { removeUserOrder } from "../myOrders/myOrdersSlice"; import { getNonceUsed } from "../orders/ordersHelpers"; import { @@ -96,7 +97,7 @@ export const cancelOrder = createAsyncThunk( const transaction: SubmittedCancellation = { type: "Cancel", - status: "processing", + status: TransactionStatusType.processing, hash: tx.hash, nonce: params.order.nonce, timestamp: Date.now(), diff --git a/src/features/transactions/hooks/useHistoricalTransactions.ts b/src/features/transactions/hooks/useHistoricalTransactions.ts index 31789c708..567db5622 100644 --- a/src/features/transactions/hooks/useHistoricalTransactions.ts +++ b/src/features/transactions/hooks/useHistoricalTransactions.ts @@ -7,6 +7,7 @@ import { transformToSubmittedRFQOrder } from "../../../entities/SubmittedTransac import { getUniqueArrayChildren } from "../../../helpers/array"; import { getOrdersFromLogs } from "../../../helpers/getOrdersFromLogs"; import { compareAddresses } from "../../../helpers/string"; +import { TransactionStatusType } from "../../../types/transactionType"; import { selectAllTokenInfo } from "../../metadata/metadataSlice"; import useSwapLogs from "./useSwapLogs"; @@ -71,7 +72,7 @@ const useHistoricalTransactions = ( order.params, signerToken, senderToken, - "succeeded", + TransactionStatusType.succeeded, order.timestamp ); }); diff --git a/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts b/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts index bee41535a..1f85a103b 100644 --- a/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts +++ b/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts @@ -40,7 +40,6 @@ const useLatestDepositOrWithdrawFromEvents = ( if (account === accountState && chainId === chainIdState) return; const wethContract = WETH.getContract(provider, chainId); - console.log(wethContract); const depositEvent = "Deposit"; const withdrawEvent = "Withdrawal"; diff --git a/src/features/transactions/hooks/useLatestSwapFromEvents.ts b/src/features/transactions/hooks/useLatestSwapFromEvents.ts index 034ffd158..be5ab4dce 100644 --- a/src/features/transactions/hooks/useLatestSwapFromEvents.ts +++ b/src/features/transactions/hooks/useLatestSwapFromEvents.ts @@ -6,21 +6,21 @@ import { useWeb3React } from "@web3-react/core"; import { BigNumber, providers, Event } from "ethers"; -import { ExtendedFullSwapERC20 } from "../../../entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20"; -import { transformToExtendedFullSwapERC20 } from "../../../entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20Transformers"; +import { FullSwapERC20Event } from "../../../entities/ExtendedFullSwapERC20/FullSwapERC20Event"; +import { transformToFullSwapERC20Event } from "../../../entities/ExtendedFullSwapERC20/FullSwapERC20EventTransformers"; import useDebounce from "../../../hooks/useDebounce"; const useLatestSwapFromEvents = ( chainId?: number, account?: string | null -): ExtendedFullSwapERC20 | undefined => { +): FullSwapERC20Event | undefined => { const { library: provider } = useWeb3React(); const [accountState, setAccountState] = useState(); const [chainIdState, setChainIdState] = useState(); - const [latestSwap, setLatestSwap] = useState(); + const [latestSwap, setLatestSwap] = useState(); const [debouncedLatestSwap, setDebouncedLatestSwap] = - useState(); + useState(); useDebounce( () => { @@ -57,7 +57,7 @@ const useLatestSwapFromEvents = ( swap.senderWallet.toLowerCase() === account.toLowerCase() ) { setLatestSwap( - transformToExtendedFullSwapERC20( + transformToFullSwapERC20Event( swap, swapEvent.transactionHash, signerWallet, diff --git a/src/features/transactions/transactionsHelpers.ts b/src/features/transactions/transactionsHelpers.ts index c00b91af9..018303dcf 100644 --- a/src/features/transactions/transactionsHelpers.ts +++ b/src/features/transactions/transactionsHelpers.ts @@ -4,11 +4,13 @@ import { findMatchingApprovalTransaction, isApproveEvent, } from "../../entities/ApproveEvent/ApproveEventHelpers"; -import { ExtendedFullSwapERC20 } from "../../entities/ExtendedFullSwapERC20/ExtendedFullSwapERC20"; +import { FullSwapERC20Event } from "../../entities/ExtendedFullSwapERC20/FullSwapERC20Event"; import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { isApprovalTransaction, isDepositTransaction, + isLastLookOrderTransaction, + isRfqOrderTransaction, isWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { WETHEvent } from "../../entities/WETHEvent/WETHEvent"; @@ -16,6 +18,13 @@ import { findMatchingDepositOrWithdrawTransaction, isWETHEvent, } from "../../entities/WETHEvent/WETHEventHelpers"; +import { TransactionStatusType } from "../../types/transactionType"; +import { + handleApproveTransaction, + handleSubmittedDepositOrder, + handleSubmittedRFQOrder, + handleSubmittedWithdrawOrder, +} from "../orders/ordersActions"; import { setTransactions } from "./transactionsSlice"; export const updateTransaction = @@ -38,7 +47,7 @@ export const updateTransaction = dispatch(setTransactions(updatedTransactions)); }; -type TransactionEvent = ApproveEvent | ExtendedFullSwapERC20 | WETHEvent; +type TransactionEvent = ApproveEvent | FullSwapERC20Event | WETHEvent; const getMatchingTransaction = ( event: TransactionEvent, @@ -91,9 +100,35 @@ export const handleTransactionEvent = { ...matchingTransaction, hash: event.hash, - status: event.status === 1 ? "succeeded" : "declined", + status: + event.status === 1 + ? TransactionStatusType.succeeded + : TransactionStatusType.declined, }, matchingTransaction.hash ) ); }; + +export const handleTransactionComplete = + (transaction: SubmittedTransaction) => + (dispatch: AppDispatch): void => { + if (isApprovalTransaction(transaction)) { + handleApproveTransaction(transaction, transaction.status, dispatch); + } + + if (isDepositTransaction(transaction)) { + handleSubmittedDepositOrder(transaction, transaction.status, dispatch); + } + + if (isWithdrawTransaction(transaction)) { + handleSubmittedWithdrawOrder(transaction, transaction.status, dispatch); + } + + if ( + isRfqOrderTransaction(transaction) || + isLastLookOrderTransaction(transaction) + ) { + handleSubmittedRFQOrder(transaction, transaction.status); + } + }; diff --git a/src/features/transactions/transactionsHooks.ts b/src/features/transactions/transactionsHooks.ts index 7d0cf25bc..37255d37a 100644 --- a/src/features/transactions/transactionsHooks.ts +++ b/src/features/transactions/transactionsHooks.ts @@ -12,8 +12,8 @@ import useLatestDepositOrWithdrawFromEvents from "./hooks/useLatestDepositOrWith import useLatestSucceededTransaction from "./hooks/useLatestSucceededTransaction"; import useLatestSwapFromEvents from "./hooks/useLatestSwapFromEvents"; import { + handleTransactionComplete, handleTransactionEvent, - updateTransaction, } from "./transactionsHelpers"; import { selectTransactions, setTransactions } from "./transactionsSlice"; import { @@ -121,6 +121,8 @@ export const useTransactions = (): void => { if (latestSuccessfulTransaction) { console.log("hop"); console.log(latestSuccessfulTransaction); + + dispatch(handleTransactionComplete(latestSuccessfulTransaction)); } }, [latestSuccessfulTransaction]); }; diff --git a/src/features/transactions/transactionsSlice.ts b/src/features/transactions/transactionsSlice.ts index ed6fcf9a1..e76d07935 100644 --- a/src/features/transactions/transactionsSlice.ts +++ b/src/features/transactions/transactionsSlice.ts @@ -9,7 +9,6 @@ import { AppDispatch, RootState } from "../../app/store"; import { ASSUMED_EXPIRY_NOTIFICATION_BUFFER_MS } from "../../constants/configParams"; import { ProtocolType, - StatusType, SubmittedApprovalTransaction, SubmittedCancellation, SubmittedDepositTransaction, @@ -17,6 +16,7 @@ import { SubmittedTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { ClearOrderType } from "../../types/clearOrderType"; +import { TransactionStatusType } from "../../types/transactionType"; import { setWalletConnected, setWalletDisconnected, @@ -49,7 +49,7 @@ function updateTransaction(params: { nonce?: string; hash?: string; signerWallet?: string; - status: StatusType; + status: TransactionStatusType; protocol?: ProtocolType; }): void { const { state, nonce, hash, signerWallet, status } = params; @@ -164,7 +164,7 @@ export const transactionsSlice = createSlice({ hash: action.payload.hash, nonce: action.payload.nonce, signerWallet: action.payload.signerWallet, - status: "declined", + status: TransactionStatusType.declined, protocol: action.payload.protocol, }); }); @@ -175,7 +175,7 @@ export const transactionsSlice = createSlice({ signerWallet: action.payload.signerWallet, nonce: action.payload.nonce, hash: action.payload.hash, - status: "reverted", + status: TransactionStatusType.reverted, }); }); builder.addCase(expireTransaction, (state, action) => { @@ -185,7 +185,7 @@ export const transactionsSlice = createSlice({ state, signerWallet, nonce, - status: "expired", + status: TransactionStatusType.expired, }); }); builder.addCase(mineTransaction, (state, action) => { @@ -195,7 +195,7 @@ export const transactionsSlice = createSlice({ hash: action.payload.hash, nonce: action.payload.nonce, signerWallet: action.payload.signerWallet, - status: "succeeded", + status: TransactionStatusType.succeeded, protocol: action.payload.protocol, }); }); @@ -230,7 +230,11 @@ export const selectFilteredTransactions = (state: RootState) => { .filter( (transaction) => !clearFailedOrdersDate || - filterTransactionByDate(transaction, clearFailedOrdersDate, "declined") + filterTransactionByDate( + transaction, + clearFailedOrdersDate, + TransactionStatusType.declined + ) ); }; diff --git a/src/features/transactions/transactionsUtils.ts b/src/features/transactions/transactionsUtils.ts index fec7aadc8..a938c9ba2 100644 --- a/src/features/transactions/transactionsUtils.ts +++ b/src/features/transactions/transactionsUtils.ts @@ -2,7 +2,6 @@ import { BaseProvider, TransactionReceipt } from "@ethersproject/providers"; import { AppDispatch } from "../../app/store"; import { - StatusType, SubmittedTransaction, SubmittedTransactionWithOrder, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; @@ -14,6 +13,7 @@ import { isWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { parseJsonArray } from "../../helpers/array"; +import { TransactionStatusType } from "../../types/transactionType"; import { handleApproveTransaction, handleSubmittedDepositOrder, @@ -63,7 +63,7 @@ export const setLocalStorageTransactions = ( export const filterTransactionByDate = ( transaction: SubmittedTransaction, timestamp: number, - status?: StatusType + status?: TransactionStatusType ) => { if (status && transaction.status !== status) { return true; @@ -72,38 +72,6 @@ export const filterTransactionByDate = ( return transaction.timestamp > timestamp; }; -export const handleTransactionReceipt = ( - receipt: TransactionReceipt, - transaction: SubmittedTransaction, - dispatch: AppDispatch -): void => { - dispatch( - updateTransaction({ - ...transaction, - status: receipt.status === 1 ? "succeeded" : "declined", - }) - ); - - if (isApprovalTransaction(transaction)) { - handleApproveTransaction(transaction, receipt, dispatch); - } - - if (isDepositTransaction(transaction)) { - handleSubmittedDepositOrder(transaction, receipt, dispatch); - } - - if (isWithdrawTransaction(transaction)) { - handleSubmittedWithdrawOrder(transaction, receipt, dispatch); - } - - if ( - isRfqOrderTransaction(transaction) || - isLastLookOrderTransaction(transaction) - ) { - handleSubmittedRFQOrder(transaction, receipt, dispatch); - } -}; - const getTransactionReceipt = async ( hash: string, library: BaseProvider @@ -141,17 +109,19 @@ export const listenForTransactionReceipt = async ( const receipt = await getTransactionReceipt(hash, library); - if (receipt?.status !== undefined) { - handleTransactionReceipt(receipt, transaction, dispatch); - + if (receipt?.status === undefined) { return; } - // library.once(hash, async () => { - // const receipt = await getTransactionReceipt(hash as string, library); - // - // if (receipt?.status !== undefined) { - // handleTransactionReceipt(receipt, transaction, dispatch); - // } - // }); + const status = + receipt.status === 1 + ? TransactionStatusType.succeeded + : TransactionStatusType.failed; + + dispatch( + updateTransaction({ + ...transaction, + status, + }) + ); }; diff --git a/src/types/transactionType.ts b/src/types/transactionType.ts new file mode 100644 index 000000000..805e987a2 --- /dev/null +++ b/src/types/transactionType.ts @@ -0,0 +1,16 @@ +export enum TransactionType { + approval = "Approval", + order = "Order", + deposit = "Deposit", + withdraw = "Withdraw", + cancel = "Cancel", +} + +export enum TransactionStatusType { + declined = "declined", + failed = "failed", + expired = "expired", + processing = "processing", + reverted = "reverted", + succeeded = "succeeded", +} From ded90d5ad41e81a100828130a2d458fb34fe5c48 Mon Sep 17 00:00:00 2001 From: piersss Date: Wed, 3 Apr 2024 22:54:04 +0200 Subject: [PATCH 3/7] 869: More type fixes --- .../hooks/useSessionOrderTransaction.ts | 3 +- .../@widgets/SwapWidget/SwapWidget.tsx | 6 ++- .../OrderSubmittedScreen.tsx | 5 +- src/components/Toasts/ToastController.tsx | 15 +++--- src/components/Toasts/TransactionToast.tsx | 12 +++-- .../TransactionsTab/TransactionsTab.tsx | 7 ++- .../WalletTransaction/WalletTransaction.tsx | 11 ++-- .../WalletTransactionStatus.tsx | 10 ++-- .../FullSwapERC20EventHelpers.ts | 8 --- .../FullSwapERC20Event.ts | 0 .../FullSwapERC20EventHelpers.ts | 20 ++++++++ .../FullSwapERC20EventTransformers.ts | 0 .../SubmittedTransaction.ts | 20 +++----- .../SubmittedTransactionHelpers.ts | 19 ++++--- .../SubmittedTransactionTransformers.ts | 15 +++--- src/features/takeOtc/takeOtcActions.ts | 7 ++- .../hooks/useLatestSwapFromEvents.ts | 4 +- .../transactions/transactionsActions.ts | 24 +++++++++ .../transactions/transactionsHelpers.ts | 50 +++++++++++++------ .../transactions/transactionsHooks.ts | 42 ++++++++++------ .../transactions/transactionsSlice.ts | 31 ++++++++---- .../transactions/transactionsUtils.ts | 27 ++-------- src/hooks/useOrderTransactionLink.ts | 4 +- src/types/transactionType.ts | 10 ++-- 24 files changed, 222 insertions(+), 128 deletions(-) delete mode 100644 src/entities/ExtendedFullSwapERC20/FullSwapERC20EventHelpers.ts rename src/entities/{ExtendedFullSwapERC20 => FullSwapERC20Event}/FullSwapERC20Event.ts (100%) create mode 100644 src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts rename src/entities/{ExtendedFullSwapERC20 => FullSwapERC20Event}/FullSwapERC20EventTransformers.ts (100%) diff --git a/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts b/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts index 6868072d9..7dd93acc8 100644 --- a/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts +++ b/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts @@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from "react"; import { useAppSelector } from "../../../../app/hooks"; import { SubmittedTransaction } from "../../../../entities/SubmittedTransaction/SubmittedTransaction"; import { selectOrderTransactions } from "../../../../features/transactions/transactionsSlice"; +import { TransactionStatusType } from "../../../../types/transactionType"; // This hook is very similar to useOrderTransaction but it will only return transactions that have been added since the component was mounted. @@ -21,7 +22,7 @@ const useSessionOrderTransaction = ( if ( transactions[0].nonce === nonce && - transactions[0].status === "processing" + transactions[0].status === TransactionStatusType.processing ) { setProcessingTransactionHash(transactions[0].hash); } diff --git a/src/components/@widgets/SwapWidget/SwapWidget.tsx b/src/components/@widgets/SwapWidget/SwapWidget.tsx index 17d89555f..9c2b49e3d 100644 --- a/src/components/@widgets/SwapWidget/SwapWidget.tsx +++ b/src/components/@widgets/SwapWidget/SwapWidget.tsx @@ -98,6 +98,7 @@ import useTokenInfo from "../../../hooks/useTokenInfo"; import useWithdrawalPending from "../../../hooks/useWithdrawalPending"; import { AppRoutes } from "../../../routes"; import { TokenSelectModalTypes } from "../../../types/tokenSelectModalTypes"; +import { TransactionStatusType } from "../../../types/transactionType"; import ApproveReview from "../../@reviewScreens/ApproveReview/ApproveReview"; import AvailableOrdersWidget from "../../AvailableOrdersWidget/AvailableOrdersWidget"; import { ErrorList } from "../../ErrorList/ErrorList"; @@ -338,7 +339,7 @@ const SwapWidget: FC = () => { }, [hasDepositOrWithdrawalPending]); useEffect(() => { - if (activeTransaction?.status === "processing") { + if (activeTransaction?.status === TransactionStatusType.processing) { setShowOrderSubmitted(true); setIsSwapping(false); } @@ -854,7 +855,8 @@ const SwapWidget: FC = () => { isWrapping={isWrapping} orderSubmitted={showOrderSubmitted} orderCompleted={ - showOrderSubmitted && activeTransaction?.status === "succeeded" + showOrderSubmitted && + activeTransaction?.status === TransactionStatusType.succeeded } requiresApproval={bestRfqOrder && shouldApprove} showViewAllQuotes={indexerOrders.length > 0} diff --git a/src/components/OrderSubmittedScreen/OrderSubmittedScreen.tsx b/src/components/OrderSubmittedScreen/OrderSubmittedScreen.tsx index b319824a0..a37e59401 100644 --- a/src/components/OrderSubmittedScreen/OrderSubmittedScreen.tsx +++ b/src/components/OrderSubmittedScreen/OrderSubmittedScreen.tsx @@ -2,6 +2,7 @@ import React, { FC } from "react"; import { useTranslation } from "react-i18next"; import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; +import { TransactionStatusType } from "../../types/transactionType"; import { InfoSubHeading } from "../Typography/Typography"; import { Container, @@ -31,13 +32,13 @@ const OrderSubmittedScreen: FC = ({ - {transaction.status === "processing" && ( + {transaction.status === TransactionStatusType.processing && ( <> {t("orders.submitted")} {t("orders.trackTransaction")} )} - {transaction.status === "succeeded" && ( + {transaction.status === TransactionStatusType.succeeded && ( <> {t("orders.transactionCompleted")} diff --git a/src/components/Toasts/ToastController.tsx b/src/components/Toasts/ToastController.tsx index 3c21ffbfc..646707d04 100644 --- a/src/components/Toasts/ToastController.tsx +++ b/src/components/Toasts/ToastController.tsx @@ -4,16 +4,15 @@ import { findTokenByAddress, FullOrderERC20, TokenInfo } from "@airswap/utils"; import i18n from "i18next"; -import nativeCurrency from "../../constants/nativeCurrency"; import { SubmittedApprovalTransaction, SubmittedDepositTransaction, SubmittedTransaction, SubmittedTransactionWithOrder, SubmittedWithdrawTransaction, - TransactionType, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import findEthOrTokenByAddress from "../../helpers/findEthOrTokenByAddress"; +import { TransactionType } from "../../types/transactionType"; import ConfirmationToast from "./ConfirmationToast"; import CopyToast from "./CopyToast"; import ErrorToast from "./ErrorToast"; @@ -30,7 +29,9 @@ export const notifyTransaction = ( let token: TokenInfo | null; // TODO: make a switch case to render a different toast for each case if ( - (type === "Order" || type === "Deposit" || type === "Withdraw") && + (type === TransactionType.order || + type === TransactionType.deposit || + type === TransactionType.withdraw) && chainId ) { const tx: SubmittedTransactionWithOrder = @@ -94,7 +95,7 @@ export const notifyApproval = (transaction: SubmittedApprovalTransaction) => { (t) => ( toast.dismiss(t.id)} - type="Approval" + type={TransactionType.approval} transaction={transaction} approvalToken={transaction.token} /> @@ -110,7 +111,7 @@ export const notifyDeposit = (transaction: SubmittedDepositTransaction) => { (t) => ( toast.dismiss(t.id)} - type="Deposit" + type={TransactionType.deposit} transaction={transaction} senderToken={transaction.senderToken} signerToken={transaction.signerToken} @@ -127,7 +128,7 @@ export const notifyWithdrawal = (transaction: SubmittedWithdrawTransaction) => { (t) => ( toast.dismiss(t.id)} - type="Deposit" + type={TransactionType.withdraw} transaction={transaction} senderToken={transaction.senderToken} signerToken={transaction.signerToken} @@ -144,7 +145,7 @@ export const notifyOrder = (transaction: SubmittedTransactionWithOrder) => { (t) => ( toast.dismiss(t.id)} - type="Order" + type={TransactionType.order} transaction={transaction} senderToken={transaction.senderToken} signerToken={transaction.signerToken} diff --git a/src/components/Toasts/TransactionToast.tsx b/src/components/Toasts/TransactionToast.tsx index 5e8d263e4..1dfc4be75 100644 --- a/src/components/Toasts/TransactionToast.tsx +++ b/src/components/Toasts/TransactionToast.tsx @@ -10,8 +10,8 @@ import { SubmittedLastLookOrder, SubmittedRFQOrder, SubmittedTransaction, - TransactionType, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; +import { TransactionType } from "../../types/transactionType"; import { InfoHeading } from "../Typography/Typography"; import { Container, @@ -74,7 +74,9 @@ const TransactionToast = ({ - {type === "Order" || type === "Deposit" || type === "Withdraw" + {type === TransactionType.order || + type === TransactionType.deposit || + type === TransactionType.withdraw ? error ? t("toast.swapFail") : t("toast.swapComplete") @@ -84,7 +86,11 @@ const TransactionToast = ({ {(() => { - if (type === "Order" || type === "Deposit" || type === "Withdraw") { + if ( + type === TransactionType.order || + type === TransactionType.deposit || + type === TransactionType.withdraw + ) { if (transaction && senderToken && signerToken) { const tx = transaction.protocol === "last-look-erc20" diff --git a/src/components/TransactionsTab/TransactionsTab.tsx b/src/components/TransactionsTab/TransactionsTab.tsx index aefe4854d..5a30891de 100644 --- a/src/components/TransactionsTab/TransactionsTab.tsx +++ b/src/components/TransactionsTab/TransactionsTab.tsx @@ -22,6 +22,7 @@ import useMediaQuery from "../../hooks/useMediaQuery"; import useWindowSize from "../../hooks/useWindowSize"; import breakPoints from "../../style/breakpoints"; import { ClearOrderType } from "../../types/clearOrderType"; +import { TransactionStatusType } from "../../types/transactionType"; import Icon from "../Icon/Icon"; import { Container, @@ -145,13 +146,15 @@ const TransactionsTab = ({ const pendingTransactions = useMemo(() => { return transactions.filter( - (transaction) => transaction.status === "processing" + (transaction) => transaction.status === TransactionStatusType.processing ); }, [transactions]); const completedTransactions = useMemo(() => { return transactions - .filter((transaction) => transaction.status !== "processing") + .filter( + (transaction) => transaction.status !== TransactionStatusType.processing + ) .sort((a, b) => b.timestamp - a.timestamp); }, [transactions]); diff --git a/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx b/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx index 5107374ca..56017c416 100644 --- a/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx +++ b/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx @@ -14,6 +14,7 @@ import { isOrderTransaction, isWithdrawTransaction, } from "../../../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; +import { TransactionStatusType } from "../../../../types/transactionType"; import ProgressBar from "../../../ProgressBar/ProgressBar"; import getTimeAgoTranslation from "../../helpers/getTimeAgoTranslation"; import getWalletTransactionStatusText from "../../helpers/getWalletTransactionStatusText"; @@ -120,14 +121,17 @@ const WalletTransaction = ({ return ( - {transaction.status === "processing" && ( + {transaction.status === TransactionStatusType.processing && ( )} {transaction && senderToken && signerToken && ( <> {t( transaction.protocol === "last-look-erc20" @@ -155,7 +159,8 @@ const WalletTransaction = ({ } )} - {hasExpiry && transaction.status === "processing" ? ( + {hasExpiry && + transaction.status === TransactionStatusType.processing ? ( { return ( - {status === "succeeded" ? ( + {status === TransactionStatusType.succeeded ? ( - ) : status === "processing" ? ( + ) : status === TransactionStatusType.processing ? ( - typeof event === "object" && - "hash" in event && - "senderWallet" in event && - "swap" in event && - "timestamp" in event; diff --git a/src/entities/ExtendedFullSwapERC20/FullSwapERC20Event.ts b/src/entities/FullSwapERC20Event/FullSwapERC20Event.ts similarity index 100% rename from src/entities/ExtendedFullSwapERC20/FullSwapERC20Event.ts rename to src/entities/FullSwapERC20Event/FullSwapERC20Event.ts diff --git a/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts b/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts new file mode 100644 index 000000000..ac4e88e8f --- /dev/null +++ b/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts @@ -0,0 +1,20 @@ +import { SubmittedTransactionWithOrder } from "../SubmittedTransaction/SubmittedTransaction"; +import { FullSwapERC20Event } from "./FullSwapERC20Event"; + +export const isFullSwapERC20Event = (event: any): event is FullSwapERC20Event => + typeof event === "object" && + "hash" in event && + "senderWallet" in event && + "swap" in event && + "timestamp" in event; + +export const findMatchingOrderTransaction = ( + transaction: SubmittedTransactionWithOrder, + event: FullSwapERC20Event +): boolean => { + if (transaction.hash === event.hash) { + return true; + } + + return transaction.nonce === event.swap.nonce; +}; diff --git a/src/entities/ExtendedFullSwapERC20/FullSwapERC20EventTransformers.ts b/src/entities/FullSwapERC20Event/FullSwapERC20EventTransformers.ts similarity index 100% rename from src/entities/ExtendedFullSwapERC20/FullSwapERC20EventTransformers.ts rename to src/entities/FullSwapERC20Event/FullSwapERC20EventTransformers.ts diff --git a/src/entities/SubmittedTransaction/SubmittedTransaction.ts b/src/entities/SubmittedTransaction/SubmittedTransaction.ts index df0cadcce..807a8f98c 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransaction.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransaction.ts @@ -1,6 +1,9 @@ import { OrderERC20, TokenInfo } from "@airswap/utils"; -import { TransactionStatusType } from "../../types/transactionType"; +import { + TransactionStatusType, + TransactionType, +} from "../../types/transactionType"; export interface DepositOrWithdrawOrder { signerToken: string; @@ -9,13 +12,6 @@ export interface DepositOrWithdrawOrder { senderAmount: string; } -export type TransactionType = - | "Approval" - | "Order" - | "Deposit" - | "Withdraw" - | "Cancel"; - export type ProtocolType = "request-for-quote-erc20" | "last-look-erc20"; export interface SubmittedTransaction { @@ -29,7 +25,7 @@ export interface SubmittedTransaction { } export interface SubmittedTransactionWithOrder extends SubmittedTransaction { - type: "Order"; + type: TransactionType.order; order: OrderERC20; senderToken: TokenInfo; signerToken: TokenInfo; @@ -44,7 +40,7 @@ export interface SubmittedLastLookOrder extends SubmittedTransactionWithOrder { } export interface SubmittedApprovalTransaction extends SubmittedTransaction { - type: "Approval"; + type: TransactionType.approval; hash: string; amount: string; token: TokenInfo; @@ -56,7 +52,7 @@ export interface SubmittedCancellation extends SubmittedTransaction { } export interface SubmittedDepositTransaction extends SubmittedTransaction { - type: "Deposit"; + type: TransactionType.deposit; hash: string; order: DepositOrWithdrawOrder; senderToken: TokenInfo; @@ -64,7 +60,7 @@ export interface SubmittedDepositTransaction extends SubmittedTransaction { } export interface SubmittedWithdrawTransaction extends SubmittedTransaction { - type: "Withdraw"; + type: TransactionType.withdraw; hash: string; order: DepositOrWithdrawOrder; senderToken: TokenInfo; diff --git a/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts b/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts index 3a8edab64..9f27f2adf 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts @@ -1,7 +1,9 @@ +import { TransactionType } from "../../types/transactionType"; import { SubmittedApprovalTransaction, SubmittedCancellation, SubmittedDepositTransaction, + SubmittedLastLookOrder, SubmittedRFQOrder, SubmittedTransaction, SubmittedTransactionWithOrder, @@ -11,31 +13,34 @@ import { export const isApprovalTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedApprovalTransaction => - transaction.type === "Approval"; + transaction.type === TransactionType.approval; export const isCancelTransaction = ( transaction: SubmittedTransaction -): transaction is SubmittedCancellation => transaction.type === "Cancel"; +): transaction is SubmittedCancellation => + transaction.type === TransactionType.cancel; export const isDepositTransaction = ( transaction: SubmittedTransaction -): transaction is SubmittedDepositTransaction => transaction.type === "Deposit"; +): transaction is SubmittedDepositTransaction => + transaction.type === TransactionType.deposit; export const isWithdrawTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedWithdrawTransaction => - transaction.type === "Withdraw"; + transaction.type === TransactionType.withdraw; export const isRfqOrderTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedRFQOrder => - transaction.type === "Order" && + transaction.type === TransactionType.order && transaction.protocol === "request-for-quote-erc20"; export const isLastLookOrderTransaction = ( transaction: SubmittedTransaction -): transaction is SubmittedRFQOrder => - transaction.type === "Order" && transaction.protocol === "last-look-erc20"; +): transaction is SubmittedLastLookOrder => + transaction.type === TransactionType.order && + transaction.protocol === "last-look-erc20"; export const isOrderTransaction = ( transaction: SubmittedTransaction diff --git a/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts b/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts index f4ce66852..b70814b0b 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts @@ -1,6 +1,9 @@ import { OrderERC20, TokenInfo } from "@airswap/utils"; -import { TransactionStatusType } from "../../types/transactionType"; +import { + TransactionStatusType, + TransactionType, +} from "../../types/transactionType"; import { SubmittedApprovalTransaction, SubmittedDepositTransaction, @@ -16,7 +19,7 @@ export const transformToSubmittedApprovalTransaction = ( status: TransactionStatusType = TransactionStatusType.processing ): SubmittedApprovalTransaction => { return { - type: "Approval", + type: TransactionType.approval, hash: hash, status, token, @@ -34,7 +37,7 @@ export const transformToSubmittedDepositTransaction = ( status: TransactionStatusType = TransactionStatusType.processing ): SubmittedDepositTransaction => { return { - type: "Deposit", + type: TransactionType.deposit, order: { signerAmount: amount, signerToken: nativeToken.address, @@ -57,7 +60,7 @@ export const transformToSubmittedWithdrawTransaction = ( status: TransactionStatusType = TransactionStatusType.processing ): SubmittedWithdrawTransaction => { return { - type: "Withdraw", + type: TransactionType.withdraw, order: { signerAmount: amount, signerToken: wrappedToken.address, @@ -81,7 +84,7 @@ export const transformToSubmittedRFQOrder = ( timestamp = Date.now() ): SubmittedRFQOrder => { return { - type: "Order", + type: TransactionType.order, expiry: order.expiry, hash, nonce: order.nonce, @@ -103,7 +106,7 @@ export const transformToSubmittedLastLookOrder = ( timestamp = Date.now() ): SubmittedLastLookOrder => { return { - type: "Order", + type: TransactionType.order, expiry: order.expiry, hash, nonce: order.nonce, diff --git a/src/features/takeOtc/takeOtcActions.ts b/src/features/takeOtc/takeOtcActions.ts index 17ab04486..9738f8e51 100644 --- a/src/features/takeOtc/takeOtcActions.ts +++ b/src/features/takeOtc/takeOtcActions.ts @@ -15,7 +15,10 @@ import { } from "../../components/Toasts/ToastController"; import { SubmittedCancellation } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import i18n from "../../i18n/i18n"; -import { TransactionStatusType } from "../../types/transactionType"; +import { + TransactionStatusType, + TransactionType, +} from "../../types/transactionType"; import { removeUserOrder } from "../myOrders/myOrdersSlice"; import { getNonceUsed } from "../orders/ordersHelpers"; import { @@ -96,7 +99,7 @@ export const cancelOrder = createAsyncThunk( dispatch(setStatus("open")); const transaction: SubmittedCancellation = { - type: "Cancel", + type: TransactionType.cancel, status: TransactionStatusType.processing, hash: tx.hash, nonce: params.order.nonce, diff --git a/src/features/transactions/hooks/useLatestSwapFromEvents.ts b/src/features/transactions/hooks/useLatestSwapFromEvents.ts index be5ab4dce..b7fe036f1 100644 --- a/src/features/transactions/hooks/useLatestSwapFromEvents.ts +++ b/src/features/transactions/hooks/useLatestSwapFromEvents.ts @@ -6,8 +6,8 @@ import { useWeb3React } from "@web3-react/core"; import { BigNumber, providers, Event } from "ethers"; -import { FullSwapERC20Event } from "../../../entities/ExtendedFullSwapERC20/FullSwapERC20Event"; -import { transformToFullSwapERC20Event } from "../../../entities/ExtendedFullSwapERC20/FullSwapERC20EventTransformers"; +import { FullSwapERC20Event } from "../../../entities/FullSwapERC20Event/FullSwapERC20Event"; +import { transformToFullSwapERC20Event } from "../../../entities/FullSwapERC20Event/FullSwapERC20EventTransformers"; import useDebounce from "../../../hooks/useDebounce"; const useLatestSwapFromEvents = ( diff --git a/src/features/transactions/transactionsActions.ts b/src/features/transactions/transactionsActions.ts index 54d92bf8a..46fdcf48e 100644 --- a/src/features/transactions/transactionsActions.ts +++ b/src/features/transactions/transactionsActions.ts @@ -1,10 +1,14 @@ +import { TransactionReceipt } from "@ethersproject/providers"; import { createAction } from "@reduxjs/toolkit"; +import { AppDispatch, RootState } from "../../app/store"; import { ProtocolType, SubmittedApprovalTransaction, SubmittedTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; +import { TransactionStatusType } from "../../types/transactionType"; +import { updateTransaction } from "./transactionsHelpers"; export const submitTransaction = createAction< SubmittedTransaction | SubmittedApprovalTransaction @@ -40,3 +44,23 @@ export const expireTransaction = createAction<{ export const updateTransactions = createAction( "transactions/updateTransactions" ); + +export const updateTransactionWithReceipt = + (transaction: SubmittedTransaction, receipt: TransactionReceipt) => + (dispatch: AppDispatch): void => { + if (receipt?.status === undefined) { + return; + } + + const status = + receipt.status === 1 + ? TransactionStatusType.succeeded + : TransactionStatusType.failed; + + dispatch( + updateTransaction({ + ...transaction, + status, + }) + ); + }; diff --git a/src/features/transactions/transactionsHelpers.ts b/src/features/transactions/transactionsHelpers.ts index 018303dcf..f247538eb 100644 --- a/src/features/transactions/transactionsHelpers.ts +++ b/src/features/transactions/transactionsHelpers.ts @@ -4,12 +4,21 @@ import { findMatchingApprovalTransaction, isApproveEvent, } from "../../entities/ApproveEvent/ApproveEventHelpers"; -import { FullSwapERC20Event } from "../../entities/ExtendedFullSwapERC20/FullSwapERC20Event"; -import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; +import { FullSwapERC20Event } from "../../entities/FullSwapERC20Event/FullSwapERC20Event"; +import { + findMatchingOrderTransaction, + isFullSwapERC20Event, +} from "../../entities/FullSwapERC20Event/FullSwapERC20EventHelpers"; +import { + SubmittedDepositTransaction, + SubmittedTransaction, + SubmittedWithdrawTransaction, +} from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { isApprovalTransaction, isDepositTransaction, isLastLookOrderTransaction, + isOrderTransaction, isRfqOrderTransaction, isWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; @@ -19,6 +28,7 @@ import { isWETHEvent, } from "../../entities/WETHEvent/WETHEventHelpers"; import { TransactionStatusType } from "../../types/transactionType"; +import { WethEventType } from "../../types/wethEventType"; import { handleApproveTransaction, handleSubmittedDepositOrder, @@ -62,18 +72,26 @@ const getMatchingTransaction = ( } if (isWETHEvent(event)) { - return ( - transactions - .filter(isDepositTransaction) - .find((transaction) => - findMatchingDepositOrWithdrawTransaction(transaction, event) - ) || - transactions - .filter(isWithdrawTransaction) - .find((transaction) => - findMatchingDepositOrWithdrawTransaction(transaction, event) + return transactions + .filter( + event.type === WethEventType.deposit + ? isDepositTransaction + : isWithdrawTransaction + ) + .find((transaction) => + findMatchingDepositOrWithdrawTransaction( + transaction as + | SubmittedWithdrawTransaction + | SubmittedDepositTransaction, + event ) - ); + ); + } + + if (isFullSwapERC20Event(event)) { + return transactions + .filter(isOrderTransaction) + .find((transaction) => findMatchingOrderTransaction(transaction, event)); } return undefined; @@ -84,13 +102,15 @@ export const handleTransactionEvent = (dispatch: AppDispatch, getState: () => RootState): void => { const transactions = getState().transactions.transactions; const pendingTransactions = transactions.filter( - (transaction) => transaction.status === "processing" + (transaction) => transaction.status === TransactionStatusType.processing ); const matchingTransaction = getMatchingTransaction( event, pendingTransactions ); + console.log(matchingTransaction); + if (!matchingTransaction) { return; } @@ -110,7 +130,7 @@ export const handleTransactionEvent = ); }; -export const handleTransactionComplete = +export const handleTransactionResolved = (transaction: SubmittedTransaction) => (dispatch: AppDispatch): void => { if (isApprovalTransaction(transaction)) { diff --git a/src/features/transactions/transactionsHooks.ts b/src/features/transactions/transactionsHooks.ts index 37255d37a..baac2258b 100644 --- a/src/features/transactions/transactionsHooks.ts +++ b/src/features/transactions/transactionsHooks.ts @@ -6,19 +6,21 @@ import { useAppDispatch, useAppSelector } from "../../app/hooks"; import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { sortSubmittedTransactionsByExpiry } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { getUniqueArrayChildren } from "../../helpers/array"; +import { TransactionStatusType } from "../../types/transactionType"; import useHistoricalTransactions from "./hooks/useHistoricalTransactions"; import useLatestApproveFromEvents from "./hooks/useLatestApproveFromEvents"; import useLatestDepositOrWithdrawFromEvents from "./hooks/useLatestDepositOrWithdrawFromEvents"; import useLatestSucceededTransaction from "./hooks/useLatestSucceededTransaction"; import useLatestSwapFromEvents from "./hooks/useLatestSwapFromEvents"; +import { updateTransactionWithReceipt } from "./transactionsActions"; import { - handleTransactionComplete, + handleTransactionResolved, handleTransactionEvent, } from "./transactionsHelpers"; import { selectTransactions, setTransactions } from "./transactionsSlice"; import { getLocalStorageTransactions, - listenForTransactionReceipt, + getTransactionReceipt, setLocalStorageTransactions, } from "./transactionsUtils"; @@ -48,18 +50,27 @@ export const useTransactions = (): void => { setLocalStorageTransactions(account, chainId, transactions); - const newListenerHashes = transactions - .filter( - (transaction) => - transaction.status === "processing" && - transaction.hash && - !activeListenerHashes.includes(transaction.hash) - ) - .map((transaction) => { - listenForTransactionReceipt(transaction, library, dispatch); - - return transaction.hash; - }) + const listenForTransactionReceipt = async ( + transaction: SubmittedTransaction + ) => { + const receipt = await getTransactionReceipt(transaction, library); + + if (receipt) { + dispatch(updateTransactionWithReceipt(transaction, receipt)); + } + }; + + const newProcessingTransactions = transactions.filter( + (transaction) => + transaction.status === TransactionStatusType.processing && + transaction.hash && + !activeListenerHashes.includes(transaction.hash) + ); + + newProcessingTransactions.forEach(listenForTransactionReceipt); + + const newListenerHashes = newProcessingTransactions + .map((transaction) => transaction.hash) .filter(Boolean) as string[]; setActiveListenerHashes([...activeListenerHashes, ...newListenerHashes]); @@ -119,10 +130,9 @@ export const useTransactions = (): void => { useEffect(() => { if (latestSuccessfulTransaction) { - console.log("hop"); console.log(latestSuccessfulTransaction); - dispatch(handleTransactionComplete(latestSuccessfulTransaction)); + dispatch(handleTransactionResolved(latestSuccessfulTransaction)); } }, [latestSuccessfulTransaction]); }; diff --git a/src/features/transactions/transactionsSlice.ts b/src/features/transactions/transactionsSlice.ts index e76d07935..6ca14fc8f 100644 --- a/src/features/transactions/transactionsSlice.ts +++ b/src/features/transactions/transactionsSlice.ts @@ -16,7 +16,10 @@ import { SubmittedTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { ClearOrderType } from "../../types/clearOrderType"; -import { TransactionStatusType } from "../../types/transactionType"; +import { + TransactionStatusType, + TransactionType, +} from "../../types/transactionType"; import { setWalletConnected, setWalletDisconnected, @@ -241,7 +244,7 @@ export const selectFilteredTransactions = (state: RootState) => { export const selectOrderTransactions = createSelector( selectTransactions, (transactions) => { - return transactions.filter((tx) => tx.type === "Order"); + return transactions.filter((tx) => tx.type === TransactionType.order); } ); @@ -249,43 +252,53 @@ export const selectPendingTransactions = ( state: RootState ): SubmittedTransaction[] => state.transactions.transactions.filter( - (tx) => tx.status === "processing" + (tx) => tx.status === TransactionStatusType.processing ) as SubmittedTransaction[]; export const selectSuccessfulTransactions = ( state: RootState ): SubmittedTransaction[] => state.transactions.transactions.filter( - (tx) => tx.status === "succeeded" + (tx) => tx.status === TransactionStatusType.succeeded ) as SubmittedTransaction[]; export const selectPendingDeposits = ( state: RootState ): SubmittedDepositTransaction[] => state.transactions.transactions.filter( - (tx) => tx.status === "processing" && tx.type === "Deposit" + (tx) => + tx.status === TransactionStatusType.processing && + tx.type === TransactionType.deposit ) as SubmittedDepositTransaction[]; export const selectPendingWithdrawals = ( state: RootState ): SubmittedDepositTransaction[] => state.transactions.transactions.filter( - (tx) => tx.status === "processing" && tx.type === "Withdraw" + (tx) => + tx.status === TransactionStatusType.processing && + tx.type === TransactionType.withdraw ) as SubmittedDepositTransaction[]; export const selectPendingApprovals = (state: RootState) => state.transactions.transactions.filter( - (tx) => tx.status === "processing" && tx.type === "Approval" + (tx) => + tx.status === TransactionStatusType.processing && + tx.type === TransactionType.approval ) as SubmittedApprovalTransaction[]; export const selectCancellations = (state: RootState) => state.transactions.transactions.filter( - (tx) => tx.status === "succeeded" && tx.type === "Cancel" + (tx) => + tx.status === TransactionStatusType.processing && + tx.type === TransactionType.cancel ) as SubmittedCancellation[]; export const selectPendingCancellations = (state: RootState) => state.transactions.transactions.filter( - (tx) => tx.status === "processing" && tx.type === "Cancel" + (tx) => + tx.status === TransactionStatusType.processing && + tx.type === TransactionType.cancel ) as SubmittedCancellation[]; export const selectTransactionsFilter = (state: RootState) => { diff --git a/src/features/transactions/transactionsUtils.ts b/src/features/transactions/transactionsUtils.ts index a938c9ba2..80d8124b3 100644 --- a/src/features/transactions/transactionsUtils.ts +++ b/src/features/transactions/transactionsUtils.ts @@ -72,7 +72,7 @@ export const filterTransactionByDate = ( return transaction.timestamp > timestamp; }; -const getTransactionReceipt = async ( +const getTransactionReceiptHelper = async ( hash: string, library: BaseProvider ): Promise => { @@ -90,11 +90,10 @@ const getTransactionReceipt = async ( } }; -export const listenForTransactionReceipt = async ( +export const getTransactionReceipt = async ( transaction: SubmittedTransaction, - library: BaseProvider, - dispatch: AppDispatch -): Promise => { + library: BaseProvider +): Promise => { let hash = transaction.hash; if (isLastLookOrderTransaction(transaction)) { @@ -107,21 +106,5 @@ export const listenForTransactionReceipt = async ( return; } - const receipt = await getTransactionReceipt(hash, library); - - if (receipt?.status === undefined) { - return; - } - - const status = - receipt.status === 1 - ? TransactionStatusType.succeeded - : TransactionStatusType.failed; - - dispatch( - updateTransaction({ - ...transaction, - status, - }) - ); + return getTransactionReceiptHelper(hash, library); }; diff --git a/src/hooks/useOrderTransactionLink.ts b/src/hooks/useOrderTransactionLink.ts index 7fd34dfbf..55f1c3982 100644 --- a/src/hooks/useOrderTransactionLink.ts +++ b/src/hooks/useOrderTransactionLink.ts @@ -7,6 +7,7 @@ import { useWeb3React } from "@web3-react/core"; import { useAppSelector } from "../app/hooks"; import { SubmittedTransaction } from "../entities/SubmittedTransaction/SubmittedTransaction"; import { selectTransactions } from "../features/transactions/transactionsSlice"; +import { TransactionStatusType } from "../types/transactionType"; const useOrderTransactionLink = (nonce: string): string | undefined => { const { chainId } = useWeb3React(); @@ -16,7 +17,8 @@ const useOrderTransactionLink = (nonce: string): string | undefined => { return useMemo(() => { const succeededTransaction = transactions.find( (transaction) => - transaction.nonce === nonce && transaction.status === "succeeded" + transaction.nonce === nonce && + transaction.status === TransactionStatusType.succeeded ); if (!succeededTransaction?.hash || !chainId) { diff --git a/src/types/transactionType.ts b/src/types/transactionType.ts index 805e987a2..400d91fc5 100644 --- a/src/types/transactionType.ts +++ b/src/types/transactionType.ts @@ -1,9 +1,9 @@ export enum TransactionType { - approval = "Approval", - order = "Order", - deposit = "Deposit", - withdraw = "Withdraw", - cancel = "Cancel", + approval = "approval", + order = "order", + deposit = "deposit", + withdraw = "withdraw", + cancel = "cancel", } export enum TransactionStatusType { From 66340e0a86c67c9dcf30f91431dc29800ad6049e Mon Sep 17 00:00:00 2001 From: piersss Date: Thu, 4 Apr 2024 22:29:44 +0200 Subject: [PATCH 4/7] 869: Added useLatestTransactionEvent hook. Added comments to transactionHooks. --- .../hooks/useSessionOrderTransaction.ts | 2 +- .../@widgets/SwapWidget/SwapWidget.tsx | 2 +- .../OrderSubmittedScreen.tsx | 2 +- src/components/Toasts/ToastController.tsx | 18 ++-- src/components/Toasts/TransactionToast.tsx | 16 ++-- .../TransactionsTab/TransactionsTab.tsx | 2 +- .../helpers/getWalletTransactionStatusText.ts | 2 +- .../WalletTransaction/WalletTransaction.tsx | 2 +- .../WalletTransactionStatus.tsx | 2 +- .../SubmittedTransaction.ts | 14 +-- .../SubmittedTransactionHelpers.ts | 14 +-- .../SubmittedTransactionTransformers.ts | 14 +-- src/features/orders/ordersActions.ts | 2 +- src/features/takeOtc/takeOtcActions.ts | 6 +- .../hooks/useHistoricalTransactions.ts | 18 ++-- .../hooks/useLatestApproveFromEvents.ts | 41 ++++----- .../useLatestDepositOrWithdrawFromEvents.ts | 2 + .../hooks/useLatestSwapFromEvents.ts | 17 +--- .../hooks/useLatestTransactionEvent.ts | 43 ++++++++++ .../transactions/transactionsActions.ts | 24 +----- .../transactions/transactionsHelpers.ts | 31 +++++-- .../transactions/transactionsHooks.ts | 85 +++++++------------ .../transactions/transactionsSlice.ts | 16 ++-- .../transactions/transactionsUtils.ts | 2 +- src/hooks/useOrderTransactionLink.ts | 2 +- src/types/transactionType.ts | 16 ---- src/types/transactionTypes.ts | 22 +++++ 27 files changed, 225 insertions(+), 192 deletions(-) create mode 100644 src/features/transactions/hooks/useLatestTransactionEvent.ts delete mode 100644 src/types/transactionType.ts create mode 100644 src/types/transactionTypes.ts diff --git a/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts b/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts index 7dd93acc8..932f6d961 100644 --- a/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts +++ b/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts @@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from "react"; import { useAppSelector } from "../../../../app/hooks"; import { SubmittedTransaction } from "../../../../entities/SubmittedTransaction/SubmittedTransaction"; import { selectOrderTransactions } from "../../../../features/transactions/transactionsSlice"; -import { TransactionStatusType } from "../../../../types/transactionType"; +import { TransactionStatusType } from "../../../../types/transactionTypes"; // This hook is very similar to useOrderTransaction but it will only return transactions that have been added since the component was mounted. diff --git a/src/components/@widgets/SwapWidget/SwapWidget.tsx b/src/components/@widgets/SwapWidget/SwapWidget.tsx index 9c2b49e3d..01f18ebb3 100644 --- a/src/components/@widgets/SwapWidget/SwapWidget.tsx +++ b/src/components/@widgets/SwapWidget/SwapWidget.tsx @@ -98,7 +98,7 @@ import useTokenInfo from "../../../hooks/useTokenInfo"; import useWithdrawalPending from "../../../hooks/useWithdrawalPending"; import { AppRoutes } from "../../../routes"; import { TokenSelectModalTypes } from "../../../types/tokenSelectModalTypes"; -import { TransactionStatusType } from "../../../types/transactionType"; +import { TransactionStatusType } from "../../../types/transactionTypes"; import ApproveReview from "../../@reviewScreens/ApproveReview/ApproveReview"; import AvailableOrdersWidget from "../../AvailableOrdersWidget/AvailableOrdersWidget"; import { ErrorList } from "../../ErrorList/ErrorList"; diff --git a/src/components/OrderSubmittedScreen/OrderSubmittedScreen.tsx b/src/components/OrderSubmittedScreen/OrderSubmittedScreen.tsx index a37e59401..b827c0510 100644 --- a/src/components/OrderSubmittedScreen/OrderSubmittedScreen.tsx +++ b/src/components/OrderSubmittedScreen/OrderSubmittedScreen.tsx @@ -2,7 +2,7 @@ import React, { FC } from "react"; import { useTranslation } from "react-i18next"; import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; -import { TransactionStatusType } from "../../types/transactionType"; +import { TransactionStatusType } from "../../types/transactionTypes"; import { InfoSubHeading } from "../Typography/Typography"; import { Container, diff --git a/src/components/Toasts/ToastController.tsx b/src/components/Toasts/ToastController.tsx index 646707d04..13366db26 100644 --- a/src/components/Toasts/ToastController.tsx +++ b/src/components/Toasts/ToastController.tsx @@ -12,7 +12,7 @@ import { SubmittedWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import findEthOrTokenByAddress from "../../helpers/findEthOrTokenByAddress"; -import { TransactionType } from "../../types/transactionType"; +import { TransactionTypes } from "../../types/transactionTypes"; import ConfirmationToast from "./ConfirmationToast"; import CopyToast from "./CopyToast"; import ErrorToast from "./ErrorToast"; @@ -20,7 +20,7 @@ import OrderToast from "./OrderToast"; import TransactionToast from "./TransactionToast"; export const notifyTransaction = ( - type: TransactionType, + type: TransactionTypes, transaction: SubmittedTransaction, tokens: TokenInfo[], error: boolean, @@ -29,9 +29,9 @@ export const notifyTransaction = ( let token: TokenInfo | null; // TODO: make a switch case to render a different toast for each case if ( - (type === TransactionType.order || - type === TransactionType.deposit || - type === TransactionType.withdraw) && + (type === TransactionTypes.order || + type === TransactionTypes.deposit || + type === TransactionTypes.withdraw) && chainId ) { const tx: SubmittedTransactionWithOrder = @@ -95,7 +95,7 @@ export const notifyApproval = (transaction: SubmittedApprovalTransaction) => { (t) => ( toast.dismiss(t.id)} - type={TransactionType.approval} + type={TransactionTypes.approval} transaction={transaction} approvalToken={transaction.token} /> @@ -111,7 +111,7 @@ export const notifyDeposit = (transaction: SubmittedDepositTransaction) => { (t) => ( toast.dismiss(t.id)} - type={TransactionType.deposit} + type={TransactionTypes.deposit} transaction={transaction} senderToken={transaction.senderToken} signerToken={transaction.signerToken} @@ -128,7 +128,7 @@ export const notifyWithdrawal = (transaction: SubmittedWithdrawTransaction) => { (t) => ( toast.dismiss(t.id)} - type={TransactionType.withdraw} + type={TransactionTypes.withdraw} transaction={transaction} senderToken={transaction.senderToken} signerToken={transaction.signerToken} @@ -145,7 +145,7 @@ export const notifyOrder = (transaction: SubmittedTransactionWithOrder) => { (t) => ( toast.dismiss(t.id)} - type={TransactionType.order} + type={TransactionTypes.order} transaction={transaction} senderToken={transaction.senderToken} signerToken={transaction.signerToken} diff --git a/src/components/Toasts/TransactionToast.tsx b/src/components/Toasts/TransactionToast.tsx index 1dfc4be75..8faa3bb3c 100644 --- a/src/components/Toasts/TransactionToast.tsx +++ b/src/components/Toasts/TransactionToast.tsx @@ -11,7 +11,7 @@ import { SubmittedRFQOrder, SubmittedTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; -import { TransactionType } from "../../types/transactionType"; +import { TransactionTypes } from "../../types/transactionTypes"; import { InfoHeading } from "../Typography/Typography"; import { Container, @@ -37,7 +37,7 @@ export type TransactionToastProps = { /** * Type of transaction the toast will display; */ - type: TransactionType; + type: TransactionTypes; /** * Token Info of sender token */ @@ -74,9 +74,9 @@ const TransactionToast = ({ - {type === TransactionType.order || - type === TransactionType.deposit || - type === TransactionType.withdraw + {type === TransactionTypes.order || + type === TransactionTypes.deposit || + type === TransactionTypes.withdraw ? error ? t("toast.swapFail") : t("toast.swapComplete") @@ -87,9 +87,9 @@ const TransactionToast = ({ {(() => { if ( - type === TransactionType.order || - type === TransactionType.deposit || - type === TransactionType.withdraw + type === TransactionTypes.order || + type === TransactionTypes.deposit || + type === TransactionTypes.withdraw ) { if (transaction && senderToken && signerToken) { const tx = diff --git a/src/components/TransactionsTab/TransactionsTab.tsx b/src/components/TransactionsTab/TransactionsTab.tsx index 5a30891de..fba024ba3 100644 --- a/src/components/TransactionsTab/TransactionsTab.tsx +++ b/src/components/TransactionsTab/TransactionsTab.tsx @@ -22,7 +22,7 @@ import useMediaQuery from "../../hooks/useMediaQuery"; import useWindowSize from "../../hooks/useWindowSize"; import breakPoints from "../../style/breakpoints"; import { ClearOrderType } from "../../types/clearOrderType"; -import { TransactionStatusType } from "../../types/transactionType"; +import { TransactionStatusType } from "../../types/transactionTypes"; import Icon from "../Icon/Icon"; import { Container, diff --git a/src/components/TransactionsTab/helpers/getWalletTransactionStatusText.ts b/src/components/TransactionsTab/helpers/getWalletTransactionStatusText.ts index 7539306ac..c047a0235 100644 --- a/src/components/TransactionsTab/helpers/getWalletTransactionStatusText.ts +++ b/src/components/TransactionsTab/helpers/getWalletTransactionStatusText.ts @@ -1,6 +1,6 @@ import { TFunction } from "react-i18next"; -import { TransactionStatusType } from "../../../types/transactionType"; +import { TransactionStatusType } from "../../../types/transactionTypes"; export default function getWalletTransactionStatusText( status: TransactionStatusType, diff --git a/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx b/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx index 56017c416..0e6d5dc45 100644 --- a/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx +++ b/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx @@ -14,7 +14,7 @@ import { isOrderTransaction, isWithdrawTransaction, } from "../../../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; -import { TransactionStatusType } from "../../../../types/transactionType"; +import { TransactionStatusType } from "../../../../types/transactionTypes"; import ProgressBar from "../../../ProgressBar/ProgressBar"; import getTimeAgoTranslation from "../../helpers/getTimeAgoTranslation"; import getWalletTransactionStatusText from "../../helpers/getWalletTransactionStatusText"; diff --git a/src/components/TransactionsTab/subcomponents/WalletTransactionStatus/WalletTransactionStatus.tsx b/src/components/TransactionsTab/subcomponents/WalletTransactionStatus/WalletTransactionStatus.tsx index 30a7caada..4d5b17234 100644 --- a/src/components/TransactionsTab/subcomponents/WalletTransactionStatus/WalletTransactionStatus.tsx +++ b/src/components/TransactionsTab/subcomponents/WalletTransactionStatus/WalletTransactionStatus.tsx @@ -1,4 +1,4 @@ -import { TransactionStatusType } from "../../../../types/transactionType"; +import { TransactionStatusType } from "../../../../types/transactionTypes"; import { FailedIcon, IconContainer, diff --git a/src/entities/SubmittedTransaction/SubmittedTransaction.ts b/src/entities/SubmittedTransaction/SubmittedTransaction.ts index 807a8f98c..8db978a5a 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransaction.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransaction.ts @@ -2,8 +2,8 @@ import { OrderERC20, TokenInfo } from "@airswap/utils"; import { TransactionStatusType, - TransactionType, -} from "../../types/transactionType"; + TransactionTypes, +} from "../../types/transactionTypes"; export interface DepositOrWithdrawOrder { signerToken: string; @@ -15,7 +15,7 @@ export interface DepositOrWithdrawOrder { export type ProtocolType = "request-for-quote-erc20" | "last-look-erc20"; export interface SubmittedTransaction { - type: TransactionType; + type: TransactionTypes; hash?: string; // LL orders doesn't have hash status: TransactionStatusType; nonce?: string; @@ -25,7 +25,7 @@ export interface SubmittedTransaction { } export interface SubmittedTransactionWithOrder extends SubmittedTransaction { - type: TransactionType.order; + type: TransactionTypes.order; order: OrderERC20; senderToken: TokenInfo; signerToken: TokenInfo; @@ -40,7 +40,7 @@ export interface SubmittedLastLookOrder extends SubmittedTransactionWithOrder { } export interface SubmittedApprovalTransaction extends SubmittedTransaction { - type: TransactionType.approval; + type: TransactionTypes.approval; hash: string; amount: string; token: TokenInfo; @@ -52,7 +52,7 @@ export interface SubmittedCancellation extends SubmittedTransaction { } export interface SubmittedDepositTransaction extends SubmittedTransaction { - type: TransactionType.deposit; + type: TransactionTypes.deposit; hash: string; order: DepositOrWithdrawOrder; senderToken: TokenInfo; @@ -60,7 +60,7 @@ export interface SubmittedDepositTransaction extends SubmittedTransaction { } export interface SubmittedWithdrawTransaction extends SubmittedTransaction { - type: TransactionType.withdraw; + type: TransactionTypes.withdraw; hash: string; order: DepositOrWithdrawOrder; senderToken: TokenInfo; diff --git a/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts b/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts index 9f27f2adf..40b873708 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts @@ -1,4 +1,4 @@ -import { TransactionType } from "../../types/transactionType"; +import { TransactionTypes } from "../../types/transactionTypes"; import { SubmittedApprovalTransaction, SubmittedCancellation, @@ -13,33 +13,33 @@ import { export const isApprovalTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedApprovalTransaction => - transaction.type === TransactionType.approval; + transaction.type === TransactionTypes.approval; export const isCancelTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedCancellation => - transaction.type === TransactionType.cancel; + transaction.type === TransactionTypes.cancel; export const isDepositTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedDepositTransaction => - transaction.type === TransactionType.deposit; + transaction.type === TransactionTypes.deposit; export const isWithdrawTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedWithdrawTransaction => - transaction.type === TransactionType.withdraw; + transaction.type === TransactionTypes.withdraw; export const isRfqOrderTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedRFQOrder => - transaction.type === TransactionType.order && + transaction.type === TransactionTypes.order && transaction.protocol === "request-for-quote-erc20"; export const isLastLookOrderTransaction = ( transaction: SubmittedTransaction ): transaction is SubmittedLastLookOrder => - transaction.type === TransactionType.order && + transaction.type === TransactionTypes.order && transaction.protocol === "last-look-erc20"; export const isOrderTransaction = ( diff --git a/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts b/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts index b70814b0b..574b43be0 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts @@ -2,8 +2,8 @@ import { OrderERC20, TokenInfo } from "@airswap/utils"; import { TransactionStatusType, - TransactionType, -} from "../../types/transactionType"; + TransactionTypes, +} from "../../types/transactionTypes"; import { SubmittedApprovalTransaction, SubmittedDepositTransaction, @@ -19,7 +19,7 @@ export const transformToSubmittedApprovalTransaction = ( status: TransactionStatusType = TransactionStatusType.processing ): SubmittedApprovalTransaction => { return { - type: TransactionType.approval, + type: TransactionTypes.approval, hash: hash, status, token, @@ -37,7 +37,7 @@ export const transformToSubmittedDepositTransaction = ( status: TransactionStatusType = TransactionStatusType.processing ): SubmittedDepositTransaction => { return { - type: TransactionType.deposit, + type: TransactionTypes.deposit, order: { signerAmount: amount, signerToken: nativeToken.address, @@ -60,7 +60,7 @@ export const transformToSubmittedWithdrawTransaction = ( status: TransactionStatusType = TransactionStatusType.processing ): SubmittedWithdrawTransaction => { return { - type: TransactionType.withdraw, + type: TransactionTypes.withdraw, order: { signerAmount: amount, signerToken: wrappedToken.address, @@ -84,7 +84,7 @@ export const transformToSubmittedRFQOrder = ( timestamp = Date.now() ): SubmittedRFQOrder => { return { - type: TransactionType.order, + type: TransactionTypes.order, expiry: order.expiry, hash, nonce: order.nonce, @@ -106,7 +106,7 @@ export const transformToSubmittedLastLookOrder = ( timestamp = Date.now() ): SubmittedLastLookOrder => { return { - type: TransactionType.order, + type: TransactionTypes.order, expiry: order.expiry, hash, nonce: order.nonce, diff --git a/src/features/orders/ordersActions.ts b/src/features/orders/ordersActions.ts index c4af1e53d..51b358b48 100644 --- a/src/features/orders/ordersActions.ts +++ b/src/features/orders/ordersActions.ts @@ -39,7 +39,7 @@ import transformUnknownErrorToAppError from "../../errors/transformUnknownErrorT import getWethAddress from "../../helpers/getWethAddress"; import toRoundedAtomicString from "../../helpers/toRoundedAtomicString"; import i18n from "../../i18n/i18n"; -import { TransactionStatusType } from "../../types/transactionType"; +import { TransactionStatusType } from "../../types/transactionTypes"; import { allowancesSwapActions, decrementBalanceBy, diff --git a/src/features/takeOtc/takeOtcActions.ts b/src/features/takeOtc/takeOtcActions.ts index 9738f8e51..8acde3b01 100644 --- a/src/features/takeOtc/takeOtcActions.ts +++ b/src/features/takeOtc/takeOtcActions.ts @@ -17,8 +17,8 @@ import { SubmittedCancellation } from "../../entities/SubmittedTransaction/Submi import i18n from "../../i18n/i18n"; import { TransactionStatusType, - TransactionType, -} from "../../types/transactionType"; + TransactionTypes, +} from "../../types/transactionTypes"; import { removeUserOrder } from "../myOrders/myOrdersSlice"; import { getNonceUsed } from "../orders/ordersHelpers"; import { @@ -99,7 +99,7 @@ export const cancelOrder = createAsyncThunk( dispatch(setStatus("open")); const transaction: SubmittedCancellation = { - type: TransactionType.cancel, + type: TransactionTypes.cancel, status: TransactionStatusType.processing, hash: tx.hash, nonce: params.order.nonce, diff --git a/src/features/transactions/hooks/useHistoricalTransactions.ts b/src/features/transactions/hooks/useHistoricalTransactions.ts index 567db5622..c8a4fbc47 100644 --- a/src/features/transactions/hooks/useHistoricalTransactions.ts +++ b/src/features/transactions/hooks/useHistoricalTransactions.ts @@ -1,5 +1,7 @@ import { useMemo, useState } from "react"; +import { useWeb3React } from "@web3-react/core"; + import { useAppSelector } from "../../../app/hooks"; import { SubmittedTransaction } from "../../../entities/SubmittedTransaction/SubmittedTransaction"; import { sortSubmittedTransactionsByExpiry } from "../../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; @@ -7,7 +9,7 @@ import { transformToSubmittedRFQOrder } from "../../../entities/SubmittedTransac import { getUniqueArrayChildren } from "../../../helpers/array"; import { getOrdersFromLogs } from "../../../helpers/getOrdersFromLogs"; import { compareAddresses } from "../../../helpers/string"; -import { TransactionStatusType } from "../../../types/transactionType"; +import { TransactionStatusType } from "../../../types/transactionTypes"; import { selectAllTokenInfo } from "../../metadata/metadataSlice"; import useSwapLogs from "./useSwapLogs"; @@ -17,10 +19,16 @@ interface HistoricalTransactionsCollection { transactions: SubmittedTransaction[]; } -const useHistoricalTransactions = ( - chainId?: number, - account?: string | null -): [HistoricalTransactionsCollection | undefined, boolean] => { +// Historical transactions are gathered from contract event logs when a user connects his wallet. This way we can +// still get transaction history even after the user clears his cache. Or if he somehow missed a transaction it will be +// merged into the transaction history. + +const useHistoricalTransactions = (): [ + HistoricalTransactionsCollection | undefined, + boolean +] => { + const { chainId, account, library } = useWeb3React(); + const tokens = useAppSelector(selectAllTokenInfo); const { result: swapLogs, status: swapLogStatus } = useSwapLogs( chainId, diff --git a/src/features/transactions/hooks/useLatestApproveFromEvents.ts b/src/features/transactions/hooks/useLatestApproveFromEvents.ts index 7850a995c..b1f1678b8 100644 --- a/src/features/transactions/hooks/useLatestApproveFromEvents.ts +++ b/src/features/transactions/hooks/useLatestApproveFromEvents.ts @@ -11,7 +11,6 @@ import { useAppSelector } from "../../../app/hooks"; import { ApproveEvent } from "../../../entities/ApproveEvent/ApproveEvent"; import { transformToApproveEvent } from "../../../entities/ApproveEvent/ApproveEventTransformers"; import { compareAddresses } from "../../../helpers/string"; -import useDebounce from "../../../hooks/useDebounce"; import { selectActiveTokens } from "../../metadata/metadataSlice"; const erc20Interface = new ethers.utils.Interface(erc20Abi); @@ -26,21 +25,21 @@ const useLatestApproveFromEvents = ( const [accountState, setAccountState] = useState(); const [chainIdState, setChainIdState] = useState(); const [latestApprove, setLatestApprove] = useState(); - const [debouncedLatestApprove, setDebouncedLatestApprove] = - useState(); - - useDebounce( - () => { - setDebouncedLatestApprove(latestApprove); - }, - 1000, - [latestApprove] - ); useEffect(() => { - if (!chainId || !account || !provider) return; + let shouldCleanup = true; + + if (!chainId || !account || !provider || !tokens.length) { + shouldCleanup = false; + + return; + } - if (account === accountState && chainId === chainIdState) return; + if (account === accountState && chainId === chainIdState) { + shouldCleanup = false; + + return; + } const filter: EventFilter = { topics: [ @@ -50,7 +49,9 @@ const useLatestApproveFromEvents = ( }; const handleEvent = async (event: Event) => { - const transaction = await event.getTransactionReceipt(); + const receipt = await provider.getTransactionReceipt( + event.transactionHash + ); const tokenAddress = event.address; const parsedEvent = erc20Interface.parseLog(event); const spenderAddress = parsedEvent.args[1]; @@ -60,7 +61,7 @@ const useLatestApproveFromEvents = ( if (!isApproval) return; if ( - !tokens.some((token) => compareAddresses(token.address, tokenAddress)) + tokens.every((token) => !compareAddresses(token.address, tokenAddress)) ) return; @@ -72,10 +73,10 @@ const useLatestApproveFromEvents = ( setLatestApprove( transformToApproveEvent( amount.toString(), - transaction.transactionHash, + receipt.transactionHash, spenderAddress, tokenAddress, - transaction.status + receipt.status ) ); }; @@ -86,11 +87,13 @@ const useLatestApproveFromEvents = ( provider.on(filter, handleEvent); return () => { + if (!shouldCleanup) return; + provider.off(filter, handleEvent); }; - }, [chainId, account, provider]); + }, [chainId, account, provider, tokens.length]); - return debouncedLatestApprove; + return latestApprove; }; export default useLatestApproveFromEvents; diff --git a/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts b/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts index 1f85a103b..7cbf35976 100644 --- a/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts +++ b/src/features/transactions/hooks/useLatestDepositOrWithdrawFromEvents.ts @@ -80,6 +80,8 @@ const useLatestDepositOrWithdrawFromEvents = ( handleEvent(WethEventType.withdrawal, from, value, depositEvent); }; + wethContract.off(depositEvent, handleDepositEvent); + wethContract.off(withdrawEvent, handleWithdrawEvent); wethContract.on(depositEvent, handleDepositEvent); wethContract.on(withdrawEvent, handleWithdrawEvent); diff --git a/src/features/transactions/hooks/useLatestSwapFromEvents.ts b/src/features/transactions/hooks/useLatestSwapFromEvents.ts index b7fe036f1..2f21bb88e 100644 --- a/src/features/transactions/hooks/useLatestSwapFromEvents.ts +++ b/src/features/transactions/hooks/useLatestSwapFromEvents.ts @@ -18,17 +18,7 @@ const useLatestSwapFromEvents = ( const [accountState, setAccountState] = useState(); const [chainIdState, setChainIdState] = useState(); - const [latestSwap, setLatestSwap] = useState(); - const [debouncedLatestSwap, setDebouncedLatestSwap] = - useState(); - - useDebounce( - () => { - setDebouncedLatestSwap(latestSwap); - }, - 1000, - [latestSwap] - ); + const [latestSwapEvent, setLatestSwapEvent] = useState(); useEffect(() => { if (!chainId || !account || !provider) return; @@ -56,7 +46,7 @@ const useLatestSwapFromEvents = ( swap.signerWallet.toLowerCase() === account.toLowerCase() || swap.senderWallet.toLowerCase() === account.toLowerCase() ) { - setLatestSwap( + setLatestSwapEvent( transformToFullSwapERC20Event( swap, swapEvent.transactionHash, @@ -68,6 +58,7 @@ const useLatestSwapFromEvents = ( } }; + swapContract.off(swapEvent, handleSwapEvent); swapContract.on(swapEvent, handleSwapEvent); return () => { @@ -83,7 +74,7 @@ const useLatestSwapFromEvents = ( }; }, [chainId, account, provider]); - return debouncedLatestSwap; + return latestSwapEvent; }; export default useLatestSwapFromEvents; diff --git a/src/features/transactions/hooks/useLatestTransactionEvent.ts b/src/features/transactions/hooks/useLatestTransactionEvent.ts new file mode 100644 index 000000000..ab0401fd4 --- /dev/null +++ b/src/features/transactions/hooks/useLatestTransactionEvent.ts @@ -0,0 +1,43 @@ +import { useEffect, useState } from "react"; + +import { useWeb3React } from "@web3-react/core"; + +import { TransactionEvent } from "../../../types/transactionTypes"; +import useLatestApproveFromEvents from "./useLatestApproveFromEvents"; +import useLatestDepositOrWithdrawFromEvents from "./useLatestDepositOrWithdrawFromEvents"; +import useLatestSwapFromEvents from "./useLatestSwapFromEvents"; + +const useLatestTransactionEvent = () => { + const { chainId, account, library } = useWeb3React(); + + const latestSwapEvent = useLatestSwapFromEvents(chainId, account); + const latestApproveEvent = useLatestApproveFromEvents(chainId, account); + const latestDepositOrWithdrawEvent = useLatestDepositOrWithdrawFromEvents( + chainId, + account + ); + + const [latestEvent, setLatestEvent] = useState(); + + useEffect(() => { + if (latestSwapEvent) { + setLatestEvent(latestSwapEvent); + } + }, [latestSwapEvent]); + + useEffect(() => { + if (latestApproveEvent) { + setLatestEvent(latestApproveEvent); + } + }, [latestApproveEvent]); + + useEffect(() => { + if (latestDepositOrWithdrawEvent) { + setLatestEvent(latestDepositOrWithdrawEvent); + } + }, [latestDepositOrWithdrawEvent]); + + return latestEvent; +}; + +export default useLatestTransactionEvent; diff --git a/src/features/transactions/transactionsActions.ts b/src/features/transactions/transactionsActions.ts index 46fdcf48e..a3bda600f 100644 --- a/src/features/transactions/transactionsActions.ts +++ b/src/features/transactions/transactionsActions.ts @@ -1,13 +1,13 @@ import { TransactionReceipt } from "@ethersproject/providers"; import { createAction } from "@reduxjs/toolkit"; -import { AppDispatch, RootState } from "../../app/store"; +import { AppDispatch } from "../../app/store"; import { ProtocolType, SubmittedApprovalTransaction, SubmittedTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; -import { TransactionStatusType } from "../../types/transactionType"; +import { TransactionStatusType } from "../../types/transactionTypes"; import { updateTransaction } from "./transactionsHelpers"; export const submitTransaction = createAction< @@ -44,23 +44,3 @@ export const expireTransaction = createAction<{ export const updateTransactions = createAction( "transactions/updateTransactions" ); - -export const updateTransactionWithReceipt = - (transaction: SubmittedTransaction, receipt: TransactionReceipt) => - (dispatch: AppDispatch): void => { - if (receipt?.status === undefined) { - return; - } - - const status = - receipt.status === 1 - ? TransactionStatusType.succeeded - : TransactionStatusType.failed; - - dispatch( - updateTransaction({ - ...transaction, - status, - }) - ); - }; diff --git a/src/features/transactions/transactionsHelpers.ts b/src/features/transactions/transactionsHelpers.ts index f247538eb..1c9528cfa 100644 --- a/src/features/transactions/transactionsHelpers.ts +++ b/src/features/transactions/transactionsHelpers.ts @@ -1,3 +1,5 @@ +import { TransactionReceipt } from "@ethersproject/providers"; + import { AppDispatch, RootState } from "../../app/store"; import { ApproveEvent } from "../../entities/ApproveEvent/ApproveEvent"; import { @@ -27,7 +29,10 @@ import { findMatchingDepositOrWithdrawTransaction, isWETHEvent, } from "../../entities/WETHEvent/WETHEventHelpers"; -import { TransactionStatusType } from "../../types/transactionType"; +import { + TransactionEvent, + TransactionStatusType, +} from "../../types/transactionTypes"; import { WethEventType } from "../../types/wethEventType"; import { handleApproveTransaction, @@ -57,8 +62,6 @@ export const updateTransaction = dispatch(setTransactions(updatedTransactions)); }; -type TransactionEvent = ApproveEvent | FullSwapERC20Event | WETHEvent; - const getMatchingTransaction = ( event: TransactionEvent, transactions: SubmittedTransaction[] @@ -109,8 +112,6 @@ export const handleTransactionEvent = pendingTransactions ); - console.log(matchingTransaction); - if (!matchingTransaction) { return; } @@ -152,3 +153,23 @@ export const handleTransactionResolved = handleSubmittedRFQOrder(transaction, transaction.status); } }; + +export const updateTransactionWithReceipt = + (transaction: SubmittedTransaction, receipt: TransactionReceipt) => + (dispatch: AppDispatch): void => { + if (receipt?.status === undefined) { + return; + } + + const status = + receipt.status === 1 + ? TransactionStatusType.succeeded + : TransactionStatusType.failed; + + dispatch( + updateTransaction({ + ...transaction, + status, + }) + ); + }; diff --git a/src/features/transactions/transactionsHooks.ts b/src/features/transactions/transactionsHooks.ts index baac2258b..ec169f324 100644 --- a/src/features/transactions/transactionsHooks.ts +++ b/src/features/transactions/transactionsHooks.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { useWeb3React } from "@web3-react/core"; @@ -6,13 +6,11 @@ import { useAppDispatch, useAppSelector } from "../../app/hooks"; import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { sortSubmittedTransactionsByExpiry } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { getUniqueArrayChildren } from "../../helpers/array"; -import { TransactionStatusType } from "../../types/transactionType"; +import { TransactionStatusType } from "../../types/transactionTypes"; import useHistoricalTransactions from "./hooks/useHistoricalTransactions"; -import useLatestApproveFromEvents from "./hooks/useLatestApproveFromEvents"; -import useLatestDepositOrWithdrawFromEvents from "./hooks/useLatestDepositOrWithdrawFromEvents"; import useLatestSucceededTransaction from "./hooks/useLatestSucceededTransaction"; -import useLatestSwapFromEvents from "./hooks/useLatestSwapFromEvents"; -import { updateTransactionWithReceipt } from "./transactionsActions"; +import useLatestTransactionEvent from "./hooks/useLatestTransactionEvent"; +import { updateTransactionWithReceipt } from "./transactionsHelpers"; import { handleTransactionResolved, handleTransactionEvent, @@ -31,26 +29,33 @@ export const useTransactions = (): void => { const transactions: SubmittedTransaction[] = useAppSelector(selectTransactions); - const [activeListenerHashes, setActiveListenerHashes] = useState( - [] - ); - const [historicalTransactions] = useHistoricalTransactions(chainId, account); - const latestSwap = useLatestSwapFromEvents(chainId, account); - const latestApprove = useLatestApproveFromEvents(chainId, account); - const latestDepositOrWithdraw = useLatestDepositOrWithdrawFromEvents( - chainId, - account - ); + const [historicalTransactions] = useHistoricalTransactions(); + const latestTransactionEvent = useLatestTransactionEvent(); + // TODO: Right now only succeeded transactions are handled, we should also handle failed and expired transactions. const latestSuccessfulTransaction = useLatestSucceededTransaction(); + // When the transactions change, we want to save them to local storage. useEffect(() => { if (!account || !chainId || !library) { return; } setLocalStorageTransactions(account, chainId, transactions); + }, [transactions]); + + // When the account or chainId changes, we want to load the transactions from local storage and update the store. + // If there were any processing transactions, we want to try to get the receipt for them. + useEffect(() => { + if (!account || !chainId) { + return; + } - const listenForTransactionReceipt = async ( + const localStorageTransactions = getLocalStorageTransactions( + account, + chainId + ); + + const getTransactionReceiptAndUpdateTransaction = async ( transaction: SubmittedTransaction ) => { const receipt = await getTransactionReceipt(transaction, library); @@ -60,30 +65,16 @@ export const useTransactions = (): void => { } }; - const newProcessingTransactions = transactions.filter( - (transaction) => - transaction.status === TransactionStatusType.processing && - transaction.hash && - !activeListenerHashes.includes(transaction.hash) + const processingTransactions = transactions.filter( + (transaction) => transaction.status === TransactionStatusType.processing ); - newProcessingTransactions.forEach(listenForTransactionReceipt); - - const newListenerHashes = newProcessingTransactions - .map((transaction) => transaction.hash) - .filter(Boolean) as string[]; + processingTransactions.forEach(getTransactionReceiptAndUpdateTransaction); - setActiveListenerHashes([...activeListenerHashes, ...newListenerHashes]); - }, [transactions]); - - useEffect(() => { - if (!account || !chainId) { - return; - } - - dispatch(setTransactions(getLocalStorageTransactions(account, chainId))); + dispatch(setTransactions(localStorageTransactions)); }, [account, chainId]); + // Historical transactions are loaded from the contract logs and merged into the transaction store if needed. useEffect(() => { if (!historicalTransactions) { return; @@ -110,28 +101,16 @@ export const useTransactions = (): void => { } }, [historicalTransactions]); + // If any transaction event is detected like a swap, approve, wrap, unwrap, we want to handle it here. useEffect(() => { - if (latestSwap) { - dispatch(handleTransactionEvent(latestSwap)); + if (latestTransactionEvent) { + dispatch(handleTransactionEvent(latestTransactionEvent)); } - }, [latestSwap]); - - useEffect(() => { - if (latestApprove) { - dispatch(handleTransactionEvent(latestApprove)); - } - }, [latestApprove]); - - useEffect(() => { - if (latestDepositOrWithdraw) { - dispatch(handleTransactionEvent(latestDepositOrWithdraw)); - } - }, [latestDepositOrWithdraw]); + }, [latestTransactionEvent]); + // If a transaction is successful, we want to handle it here. useEffect(() => { if (latestSuccessfulTransaction) { - console.log(latestSuccessfulTransaction); - dispatch(handleTransactionResolved(latestSuccessfulTransaction)); } }, [latestSuccessfulTransaction]); diff --git a/src/features/transactions/transactionsSlice.ts b/src/features/transactions/transactionsSlice.ts index 6ca14fc8f..2cfdf2a8e 100644 --- a/src/features/transactions/transactionsSlice.ts +++ b/src/features/transactions/transactionsSlice.ts @@ -18,8 +18,8 @@ import { import { ClearOrderType } from "../../types/clearOrderType"; import { TransactionStatusType, - TransactionType, -} from "../../types/transactionType"; + TransactionTypes, +} from "../../types/transactionTypes"; import { setWalletConnected, setWalletDisconnected, @@ -244,7 +244,7 @@ export const selectFilteredTransactions = (state: RootState) => { export const selectOrderTransactions = createSelector( selectTransactions, (transactions) => { - return transactions.filter((tx) => tx.type === TransactionType.order); + return transactions.filter((tx) => tx.type === TransactionTypes.order); } ); @@ -268,7 +268,7 @@ export const selectPendingDeposits = ( state.transactions.transactions.filter( (tx) => tx.status === TransactionStatusType.processing && - tx.type === TransactionType.deposit + tx.type === TransactionTypes.deposit ) as SubmittedDepositTransaction[]; export const selectPendingWithdrawals = ( @@ -277,28 +277,28 @@ export const selectPendingWithdrawals = ( state.transactions.transactions.filter( (tx) => tx.status === TransactionStatusType.processing && - tx.type === TransactionType.withdraw + tx.type === TransactionTypes.withdraw ) as SubmittedDepositTransaction[]; export const selectPendingApprovals = (state: RootState) => state.transactions.transactions.filter( (tx) => tx.status === TransactionStatusType.processing && - tx.type === TransactionType.approval + tx.type === TransactionTypes.approval ) as SubmittedApprovalTransaction[]; export const selectCancellations = (state: RootState) => state.transactions.transactions.filter( (tx) => tx.status === TransactionStatusType.processing && - tx.type === TransactionType.cancel + tx.type === TransactionTypes.cancel ) as SubmittedCancellation[]; export const selectPendingCancellations = (state: RootState) => state.transactions.transactions.filter( (tx) => tx.status === TransactionStatusType.processing && - tx.type === TransactionType.cancel + tx.type === TransactionTypes.cancel ) as SubmittedCancellation[]; export const selectTransactionsFilter = (state: RootState) => { diff --git a/src/features/transactions/transactionsUtils.ts b/src/features/transactions/transactionsUtils.ts index 80d8124b3..be0d4f9d6 100644 --- a/src/features/transactions/transactionsUtils.ts +++ b/src/features/transactions/transactionsUtils.ts @@ -13,7 +13,7 @@ import { isWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { parseJsonArray } from "../../helpers/array"; -import { TransactionStatusType } from "../../types/transactionType"; +import { TransactionStatusType } from "../../types/transactionTypes"; import { handleApproveTransaction, handleSubmittedDepositOrder, diff --git a/src/hooks/useOrderTransactionLink.ts b/src/hooks/useOrderTransactionLink.ts index 55f1c3982..7cb24bc0b 100644 --- a/src/hooks/useOrderTransactionLink.ts +++ b/src/hooks/useOrderTransactionLink.ts @@ -7,7 +7,7 @@ import { useWeb3React } from "@web3-react/core"; import { useAppSelector } from "../app/hooks"; import { SubmittedTransaction } from "../entities/SubmittedTransaction/SubmittedTransaction"; import { selectTransactions } from "../features/transactions/transactionsSlice"; -import { TransactionStatusType } from "../types/transactionType"; +import { TransactionStatusType } from "../types/transactionTypes"; const useOrderTransactionLink = (nonce: string): string | undefined => { const { chainId } = useWeb3React(); diff --git a/src/types/transactionType.ts b/src/types/transactionType.ts deleted file mode 100644 index 400d91fc5..000000000 --- a/src/types/transactionType.ts +++ /dev/null @@ -1,16 +0,0 @@ -export enum TransactionType { - approval = "approval", - order = "order", - deposit = "deposit", - withdraw = "withdraw", - cancel = "cancel", -} - -export enum TransactionStatusType { - declined = "declined", - failed = "failed", - expired = "expired", - processing = "processing", - reverted = "reverted", - succeeded = "succeeded", -} diff --git a/src/types/transactionTypes.ts b/src/types/transactionTypes.ts new file mode 100644 index 000000000..fb1c6db03 --- /dev/null +++ b/src/types/transactionTypes.ts @@ -0,0 +1,22 @@ +import { ApproveEvent } from "../entities/ApproveEvent/ApproveEvent"; +import { FullSwapERC20Event } from "../entities/FullSwapERC20Event/FullSwapERC20Event"; +import { WETHEvent } from "../entities/WETHEvent/WETHEvent"; + +export type TransactionEvent = FullSwapERC20Event | ApproveEvent | WETHEvent; + +export enum TransactionTypes { + approval = "approval", + order = "order", + deposit = "deposit", + withdraw = "withdraw", + cancel = "cancel", +} + +export enum TransactionStatusType { + declined = "declined", + failed = "failed", + expired = "expired", + processing = "processing", + reverted = "reverted", + succeeded = "succeeded", +} From ab90ca5687dfb2524fbc5fae2fb1e5479e7f8c87 Mon Sep 17 00:00:00 2001 From: piersss Date: Fri, 5 Apr 2024 23:08:58 +0200 Subject: [PATCH 5/7] 869: Added cancel event and helpers. --- src/App.tsx | 2 + .../@widgets/CancelWidget/CancelWidget.tsx | 13 +--- src/components/Page/Page.tsx | 6 -- src/components/Routes/Routes.tsx | 3 + src/entities/ApproveEvent/ApproveEvent.ts | 1 + .../ApproveEvent/ApproveEventHelpers.ts | 11 ++- .../ApproveEvent/ApproveEventTransformers.ts | 1 + src/entities/CancelEvent/CancelEvent.ts | 7 ++ .../CancelEvent/CancelEventHelpers.ts | 17 +++++ .../CancelEvent/CancelEventTransformers.ts | 16 +++++ .../FullSwapERC20Event/FullSwapERC20Event.ts | 1 + .../FullSwapERC20EventHelpers.ts | 11 ++- .../FullSwapERC20EventTransformers.ts | 1 + src/entities/WETHEvent/WETHEvent.ts | 1 + src/entities/WETHEvent/WETHEventHelpers.ts | 13 ++-- .../WETHEvent/WETHEventTransformers.ts | 1 + src/features/orders/ordersActions.ts | 17 +++++ src/features/takeOtc/takeOtcActions.ts | 25 +------ src/features/takeOtc/takeOtcSlice.ts | 20 +----- .../hooks/useLatestCancelFromEvents.ts | 72 +++++++++++++++++++ .../hooks/useLatestSwapFromEvents.ts | 29 ++++---- .../hooks/useLatestTransactionEvent.ts | 8 +++ .../transactions/transactionsActions.ts | 4 -- .../transactions/transactionsHelpers.ts | 16 +++++ .../transactions/transactionsHooks.ts | 6 +- .../transactions/transactionsSlice.ts | 4 +- src/hooks/useCancellationSuccess.ts | 7 +- src/types/transactionTypes.ts | 7 +- 28 files changed, 217 insertions(+), 103 deletions(-) create mode 100644 src/entities/CancelEvent/CancelEvent.ts create mode 100644 src/entities/CancelEvent/CancelEventHelpers.ts create mode 100644 src/entities/CancelEvent/CancelEventTransformers.ts create mode 100644 src/features/transactions/hooks/useLatestCancelFromEvents.ts diff --git a/src/App.tsx b/src/App.tsx index 424756195..cbe05f3f6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -13,6 +13,8 @@ import PageLoader from "./components/PageLoader/PageLoader"; import Routes from "./components/Routes/Routes"; import InterfaceProvider from "./contexts/interface/Interface"; import LastLookProvider from "./contexts/lastLook/LastLook"; +import useTransactionsFilterFromLocalStorage from "./features/transactions/hooks/useTransactionsFilterFromLocalStorage"; +import { useTransactions } from "./features/transactions/transactionsHooks"; import { selectTheme } from "./features/userSettings/userSettingsSlice"; import useCustomServer from "./hooks/useCustomServer"; import useSystemTheme from "./hooks/useSystemTheme"; diff --git a/src/components/@widgets/CancelWidget/CancelWidget.tsx b/src/components/@widgets/CancelWidget/CancelWidget.tsx index a116cce20..2e92d2f25 100644 --- a/src/components/@widgets/CancelWidget/CancelWidget.tsx +++ b/src/components/@widgets/CancelWidget/CancelWidget.tsx @@ -8,11 +8,7 @@ import { useWeb3React } from "@web3-react/core"; import { useAppDispatch, useAppSelector } from "../../../app/hooks"; import { cancelOrder } from "../../../features/takeOtc/takeOtcActions"; -import { - selectTakeOtcReducer, - selectTakeOtcStatus, - setIsCancelSuccessFull, -} from "../../../features/takeOtc/takeOtcSlice"; +import { selectTakeOtcStatus } from "../../../features/takeOtc/takeOtcSlice"; import useCancellationPending from "../../../hooks/useCancellationPending"; import useCancellationSuccess from "../../../hooks/useCancellationSuccess"; import { AppRoutes } from "../../../routes"; @@ -43,7 +39,6 @@ export const CancelWidget: FC = ({ order, library }) => { const { chainId } = useWeb3React(); const dispatch = useAppDispatch(); - const { isCancelSuccessFull } = useAppSelector(selectTakeOtcReducer); const status = useAppSelector(selectTakeOtcStatus); const params = useParams<{ compressedOrder: string }>(); @@ -61,14 +56,12 @@ export const CancelWidget: FC = ({ order, library }) => { }; useEffect(() => { - if (isCancelSuccessFull && isCancelSuccess) { + if (isCancelSuccess) { history.push({ pathname: `/${AppRoutes.order}/${params.compressedOrder}`, }); - - dispatch(setIsCancelSuccessFull(false)); } - }, [history, params, isCancelSuccessFull, isCancelSuccess, dispatch]); + }, [isCancelSuccess]); const handleCancelClick = async () => { await dispatch( diff --git a/src/components/Page/Page.tsx b/src/components/Page/Page.tsx index 9577291bf..e40c95de8 100644 --- a/src/components/Page/Page.tsx +++ b/src/components/Page/Page.tsx @@ -9,9 +9,6 @@ import { useAppDispatch } from "../../app/hooks"; import { WalletProvider } from "../../constants/supportedWalletProviders"; import { InterfaceContext } from "../../contexts/interface/Interface"; import { clear, setResetStatus } from "../../features/orders/ordersSlice"; -import useHistoricalTransactions from "../../features/transactions/hooks/useHistoricalTransactions"; -import useTransactionsFilterFromLocalStorage from "../../features/transactions/hooks/useTransactionsFilterFromLocalStorage"; -import { useTransactions } from "../../features/transactions/transactionsHooks"; import { Wallet } from "../../features/wallet/Wallet"; import { setActiveProvider } from "../../features/wallet/walletSlice"; import useAppRouteParams from "../../hooks/useAppRouteParams"; @@ -45,9 +42,6 @@ const Page: FC = ({ children, className }): ReactElement => { setShowWalletList, } = useContext(InterfaceContext); - useTransactions(); - useTransactionsFilterFromLocalStorage(); - useKeyPress(() => setShowMobileToolbar(false), ["Escape"]); const reset = () => { diff --git a/src/components/Routes/Routes.tsx b/src/components/Routes/Routes.tsx index 406a4e6df..ea7db240d 100644 --- a/src/components/Routes/Routes.tsx +++ b/src/components/Routes/Routes.tsx @@ -1,6 +1,7 @@ import React, { FC } from "react"; import { Route, Switch } from "react-router-dom"; +import { useTransactions } from "../../features/transactions/transactionsHooks"; import Cancel from "../../pages/Cancel/Cancel"; import MakePage from "../../pages/Make/Make"; import MySwapsPage from "../../pages/MyOrders/MyOrders"; @@ -9,6 +10,8 @@ import SwapPage from "../../pages/Swap/Swap"; import { AppRoutes } from "../../routes"; const Routes: FC = () => { + useTransactions(); + return ( diff --git a/src/entities/ApproveEvent/ApproveEvent.ts b/src/entities/ApproveEvent/ApproveEvent.ts index d2c115548..90adb8704 100644 --- a/src/entities/ApproveEvent/ApproveEvent.ts +++ b/src/entities/ApproveEvent/ApproveEvent.ts @@ -1,5 +1,6 @@ export interface ApproveEvent { amount: string; + name: "Approve"; hash: string; spenderAddress: string; status?: number; diff --git a/src/entities/ApproveEvent/ApproveEventHelpers.ts b/src/entities/ApproveEvent/ApproveEventHelpers.ts index 35bb8ef28..47dbb458b 100644 --- a/src/entities/ApproveEvent/ApproveEventHelpers.ts +++ b/src/entities/ApproveEvent/ApproveEventHelpers.ts @@ -1,12 +1,11 @@ +import { TransactionEvent } from "../../types/transactionTypes"; import { SubmittedApprovalTransaction } from "../SubmittedTransaction/SubmittedTransaction"; import { ApproveEvent } from "./ApproveEvent"; -export const isApproveEvent = (event: any): event is ApproveEvent => - typeof event === "object" && - "amount" in event && - "hash" in event && - "spenderAddress" in event && - "tokenAddress" in event; +export const isApproveEvent = ( + event: TransactionEvent +): event is ApproveEvent => + typeof event === "object" && "name" in event && event.name === "Approve"; export const findMatchingApprovalTransaction = ( transaction: SubmittedApprovalTransaction, diff --git a/src/entities/ApproveEvent/ApproveEventTransformers.ts b/src/entities/ApproveEvent/ApproveEventTransformers.ts index 88456175d..d63372ae2 100644 --- a/src/entities/ApproveEvent/ApproveEventTransformers.ts +++ b/src/entities/ApproveEvent/ApproveEventTransformers.ts @@ -9,6 +9,7 @@ export const transformToApproveEvent = ( ): ApproveEvent => { return { amount, + name: "Approve", hash, spenderAddress, tokenAddress, diff --git a/src/entities/CancelEvent/CancelEvent.ts b/src/entities/CancelEvent/CancelEvent.ts new file mode 100644 index 000000000..a1d332a57 --- /dev/null +++ b/src/entities/CancelEvent/CancelEvent.ts @@ -0,0 +1,7 @@ +export interface CancelEvent { + name: "Cancel"; + hash: string; + nonce: string; + signerAddress: string; + status?: number; +} diff --git a/src/entities/CancelEvent/CancelEventHelpers.ts b/src/entities/CancelEvent/CancelEventHelpers.ts new file mode 100644 index 000000000..7723cd736 --- /dev/null +++ b/src/entities/CancelEvent/CancelEventHelpers.ts @@ -0,0 +1,17 @@ +import { TransactionEvent } from "../../types/transactionTypes"; +import { SubmittedCancellation } from "../SubmittedTransaction/SubmittedTransaction"; +import { CancelEvent } from "./CancelEvent"; + +export const isCancelEvent = (event: TransactionEvent): event is CancelEvent => + typeof event === "object" && "name" in event && event.name === "Cancel"; + +export const findMatchingCancelTransaction = ( + transaction: SubmittedCancellation, + event: CancelEvent +): boolean => { + if (transaction.hash === event.hash) { + return true; + } + + return transaction.nonce === event.nonce; +}; diff --git a/src/entities/CancelEvent/CancelEventTransformers.ts b/src/entities/CancelEvent/CancelEventTransformers.ts new file mode 100644 index 000000000..b6ed82760 --- /dev/null +++ b/src/entities/CancelEvent/CancelEventTransformers.ts @@ -0,0 +1,16 @@ +import { CancelEvent } from "./CancelEvent"; + +export const transformToCancelEvent = ( + hash: string, + nonce: string, + signerAddress: string, + status?: number +): CancelEvent => { + return { + name: "Cancel", + hash, + nonce, + signerAddress, + status, + }; +}; diff --git a/src/entities/FullSwapERC20Event/FullSwapERC20Event.ts b/src/entities/FullSwapERC20Event/FullSwapERC20Event.ts index ef69c9b6a..2849f4603 100644 --- a/src/entities/FullSwapERC20Event/FullSwapERC20Event.ts +++ b/src/entities/FullSwapERC20Event/FullSwapERC20Event.ts @@ -1,6 +1,7 @@ import { FullSwapERC20 } from "@airswap/utils/build/src/swap-erc20"; export interface FullSwapERC20Event { + name: "Swap"; hash: string; senderWallet: string; status?: number; diff --git a/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts b/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts index ac4e88e8f..2e104dd35 100644 --- a/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts +++ b/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts @@ -1,12 +1,11 @@ +import { TransactionEvent } from "../../types/transactionTypes"; import { SubmittedTransactionWithOrder } from "../SubmittedTransaction/SubmittedTransaction"; import { FullSwapERC20Event } from "./FullSwapERC20Event"; -export const isFullSwapERC20Event = (event: any): event is FullSwapERC20Event => - typeof event === "object" && - "hash" in event && - "senderWallet" in event && - "swap" in event && - "timestamp" in event; +export const isFullSwapERC20Event = ( + event: TransactionEvent +): event is FullSwapERC20Event => + typeof event === "object" && "name" in event && event.name === "Swap"; export const findMatchingOrderTransaction = ( transaction: SubmittedTransactionWithOrder, diff --git a/src/entities/FullSwapERC20Event/FullSwapERC20EventTransformers.ts b/src/entities/FullSwapERC20Event/FullSwapERC20EventTransformers.ts index 4b57481ab..ef040399c 100644 --- a/src/entities/FullSwapERC20Event/FullSwapERC20EventTransformers.ts +++ b/src/entities/FullSwapERC20Event/FullSwapERC20EventTransformers.ts @@ -10,6 +10,7 @@ export const transformToFullSwapERC20Event = ( status?: number ): FullSwapERC20Event => { return { + name: "Swap", hash, senderWallet, swap, diff --git a/src/entities/WETHEvent/WETHEvent.ts b/src/entities/WETHEvent/WETHEvent.ts index c73ddaf84..fb6c5d764 100644 --- a/src/entities/WETHEvent/WETHEvent.ts +++ b/src/entities/WETHEvent/WETHEvent.ts @@ -3,6 +3,7 @@ import { WethEventType } from "../../types/wethEventType"; export interface WETHEvent { type: WethEventType; amount: string; + name: "Deposit" | "Withdrawal"; hash: string; status?: number; } diff --git a/src/entities/WETHEvent/WETHEventHelpers.ts b/src/entities/WETHEvent/WETHEventHelpers.ts index f4cb7871d..6422618aa 100644 --- a/src/entities/WETHEvent/WETHEventHelpers.ts +++ b/src/entities/WETHEvent/WETHEventHelpers.ts @@ -1,19 +1,14 @@ -import { WethEventType } from "../../types/wethEventType"; -import { ApproveEvent } from "../ApproveEvent/ApproveEvent"; +import { TransactionEvent } from "../../types/transactionTypes"; import { - SubmittedApprovalTransaction, SubmittedDepositTransaction, SubmittedWithdrawTransaction, } from "../SubmittedTransaction/SubmittedTransaction"; import { WETHEvent } from "./WETHEvent"; -export const isWETHEvent = (event: any): event is WETHEvent => +export const isWETHEvent = (event: TransactionEvent): event is WETHEvent => typeof event === "object" && - "type" in event && - (event.type === WethEventType.deposit || - event.type === WethEventType.withdrawal) && - "amount" in event && - "hash" in event; + "name" in event && + (event.name === "Deposit" || event.name === "Withdrawal"); export const findMatchingDepositOrWithdrawTransaction = ( transaction: SubmittedDepositTransaction | SubmittedWithdrawTransaction, diff --git a/src/entities/WETHEvent/WETHEventTransformers.ts b/src/entities/WETHEvent/WETHEventTransformers.ts index c9affa782..7b5e576de 100644 --- a/src/entities/WETHEvent/WETHEventTransformers.ts +++ b/src/entities/WETHEvent/WETHEventTransformers.ts @@ -10,6 +10,7 @@ export const transformToDepositOrWithdrawEvent = ( return { type, amount, + name: type === WethEventType.deposit ? "Deposit" : "Withdrawal", hash, status, }; diff --git a/src/features/orders/ordersActions.ts b/src/features/orders/ordersActions.ts index 51b358b48..62b2b5a07 100644 --- a/src/features/orders/ordersActions.ts +++ b/src/features/orders/ordersActions.ts @@ -11,6 +11,7 @@ import { Dispatch } from "@reduxjs/toolkit"; import { AppDispatch } from "../../app/store"; import { notifyApproval, + notifyConfirmation, notifyDeposit, notifyError, notifyOrder, @@ -24,6 +25,7 @@ import { import nativeCurrency from "../../constants/nativeCurrency"; import { SubmittedApprovalTransaction, + SubmittedCancellation, SubmittedDepositTransaction, SubmittedTransactionWithOrder, SubmittedWithdrawTransaction, @@ -178,6 +180,21 @@ export const handleSubmittedRFQOrder = ( notifyOrder(transaction); }; +export const handleSubmittedCancelOrder = ( + status: TransactionStatusType +): void => { + if (status === TransactionStatusType.failed) { + notifyError({ + heading: i18n.t("toast.cancelFailed"), + cta: i18n.t("validatorErrors.unknownError"), + }); + + return; + } + + notifyConfirmation({ heading: i18n.t("toast.cancelComplete"), cta: "" }); +}; + // replaces WETH to ETH on Wrapper orders const refactorOrder = (order: OrderERC20, chainId: number) => { let newOrder = { ...order }; diff --git a/src/features/takeOtc/takeOtcActions.ts b/src/features/takeOtc/takeOtcActions.ts index 8acde3b01..ddc7a9aec 100644 --- a/src/features/takeOtc/takeOtcActions.ts +++ b/src/features/takeOtc/takeOtcActions.ts @@ -22,16 +22,10 @@ import { import { removeUserOrder } from "../myOrders/myOrdersSlice"; import { getNonceUsed } from "../orders/ordersHelpers"; import { - mineTransaction, revertTransaction, submitTransaction, } from "../transactions/transactionsActions"; -import { - reset, - setActiveOrder, - setIsCancelSuccessFull, - setStatus, -} from "./takeOtcSlice"; +import { reset, setActiveOrder, setStatus } from "./takeOtcSlice"; export const decompressAndSetActiveOrder = createAsyncThunk( "take-otc/decompressAndSetActiveOrder", @@ -105,22 +99,7 @@ export const cancelOrder = createAsyncThunk( nonce: params.order.nonce, timestamp: Date.now(), }; - dispatch(submitTransaction(transaction)); - - await tx.wait(); - // post-cancel clean up - const isCancelled = await getNonceUsed(params.order, params.library); - dispatch(mineTransaction(tx)); - - if (isCancelled) { - dispatch(setIsCancelSuccessFull(true)); - notifyConfirmation({ heading: i18n.t("toast.cancelComplete"), cta: "" }); - } else { - notifyError({ - heading: i18n.t("toast.cancelFailed"), - cta: i18n.t("validatorErrors.unknownError"), - }); - } + dispatch(submitTransaction(transaction)); } ); diff --git a/src/features/takeOtc/takeOtcSlice.ts b/src/features/takeOtc/takeOtcSlice.ts index 86f4bd93d..7fd63b496 100644 --- a/src/features/takeOtc/takeOtcSlice.ts +++ b/src/features/takeOtc/takeOtcSlice.ts @@ -5,14 +5,12 @@ import { RootState } from "../../app/store"; import { AppError } from "../../errors/appError"; export interface TakeOtcState { - isCancelSuccessFull: boolean; activeOrder?: FullOrderERC20; status: "idle" | "not-found" | "open" | "taken" | "signing" | "failed"; errors: AppError[]; } const initialState: TakeOtcState = { - isCancelSuccessFull: false, status: "idle", errors: [], }; @@ -30,15 +28,6 @@ export const takeOtcSlice = createSlice({ activeOrder: action.payload, }; }, - setIsCancelSuccessFull: ( - state, - action: PayloadAction - ): TakeOtcState => { - return { - ...state, - isCancelSuccessFull: action.payload, - }; - }, setStatus: ( state, action: PayloadAction @@ -60,13 +49,8 @@ export const takeOtcSlice = createSlice({ }, }); -export const { - setActiveOrder, - setStatus, - setErrors, - setIsCancelSuccessFull, - reset, -} = takeOtcSlice.actions; +export const { setActiveOrder, setStatus, setErrors, reset } = + takeOtcSlice.actions; export const selectTakeOtcReducer = (state: RootState) => state.takeOtc; diff --git a/src/features/transactions/hooks/useLatestCancelFromEvents.ts b/src/features/transactions/hooks/useLatestCancelFromEvents.ts new file mode 100644 index 000000000..79f7b9f81 --- /dev/null +++ b/src/features/transactions/hooks/useLatestCancelFromEvents.ts @@ -0,0 +1,72 @@ +import { useEffect, useState } from "react"; + +import { SwapERC20 } from "@airswap/libraries"; +import { useWeb3React } from "@web3-react/core"; + +import { BigNumber, providers, Event } from "ethers"; + +import { CancelEvent } from "../../../entities/CancelEvent/CancelEvent"; +import { transformToCancelEvent } from "../../../entities/CancelEvent/CancelEventTransformers"; +import { FullSwapERC20Event } from "../../../entities/FullSwapERC20Event/FullSwapERC20Event"; +import { compareAddresses } from "../../../helpers/string"; + +const useLatestCancelFromEvents = ( + chainId?: number, + account?: string | null +): CancelEvent | undefined => { + const { library: provider } = useWeb3React(); + + const [accountState, setAccountState] = useState(); + const [chainIdState, setChainIdState] = useState(); + const [latestCancelEvent, setLatestCancelEvent] = useState(); + + useEffect(() => { + if (!chainId || !account || !provider) return; + + if (account === accountState && chainId === chainIdState) return; + + const swapContract = SwapERC20.getContract(provider, chainId); + const cancelEvent = "Cancel"; + + swapContract.protocolFeeWallet().then(() => { + const handleCancelEvent = async ( + nonce: BigNumber, + signerAddress: string, + swapEvent: Event + ) => { + const receipt = await swapEvent.getTransactionReceipt(); + + if (!compareAddresses(signerAddress, account)) { + return; + } + + setLatestCancelEvent( + transformToCancelEvent( + receipt.transactionHash, + nonce.toString(), + signerAddress, + receipt.status + ) + ); + }; + + swapContract.off(cancelEvent, handleCancelEvent); + swapContract.on(cancelEvent, handleCancelEvent); + + return () => { + swapContract.off(cancelEvent, handleCancelEvent); + }; + }); + + setAccountState(account); + setChainIdState(chainId); + + return () => { + swapContract.off(cancelEvent, () => {}); + }; + }, [chainId, account, provider]); + + return latestCancelEvent; +}; + +export default useLatestCancelFromEvents; diff --git a/src/features/transactions/hooks/useLatestSwapFromEvents.ts b/src/features/transactions/hooks/useLatestSwapFromEvents.ts index 2f21bb88e..31192dcc8 100644 --- a/src/features/transactions/hooks/useLatestSwapFromEvents.ts +++ b/src/features/transactions/hooks/useLatestSwapFromEvents.ts @@ -8,6 +8,7 @@ import { BigNumber, providers, Event } from "ethers"; import { FullSwapERC20Event } from "../../../entities/FullSwapERC20Event/FullSwapERC20Event"; import { transformToFullSwapERC20Event } from "../../../entities/FullSwapERC20Event/FullSwapERC20EventTransformers"; +import { compareAddresses } from "../../../helpers/string"; import useDebounce from "../../../hooks/useDebounce"; const useLatestSwapFromEvents = ( @@ -31,31 +32,33 @@ const useLatestSwapFromEvents = ( swapContract.protocolFeeWallet().then((feeReceiver: string) => { const handleSwapEvent = async ( nonce: BigNumber, - signerWallet: string, + signerAddress: string, swapEvent: Event ) => { const receipt = await swapEvent.getTransactionReceipt(); const swap = await getFullSwapERC20( nonce.toString(), - signerWallet, + signerAddress, feeReceiver, receipt.logs ); if ( - swap.signerWallet.toLowerCase() === account.toLowerCase() || - swap.senderWallet.toLowerCase() === account.toLowerCase() + !compareAddresses(swap.signerWallet, account) && + !compareAddresses(swap.senderWallet, account) ) { - setLatestSwapEvent( - transformToFullSwapERC20Event( - swap, - swapEvent.transactionHash, - signerWallet, - swapEvent.blockNumber, - receipt.status - ) - ); + return; } + + setLatestSwapEvent( + transformToFullSwapERC20Event( + swap, + swapEvent.transactionHash, + signerAddress, + swapEvent.blockNumber, + receipt.status + ) + ); }; swapContract.off(swapEvent, handleSwapEvent); diff --git a/src/features/transactions/hooks/useLatestTransactionEvent.ts b/src/features/transactions/hooks/useLatestTransactionEvent.ts index ab0401fd4..4d5ffc438 100644 --- a/src/features/transactions/hooks/useLatestTransactionEvent.ts +++ b/src/features/transactions/hooks/useLatestTransactionEvent.ts @@ -4,6 +4,7 @@ import { useWeb3React } from "@web3-react/core"; import { TransactionEvent } from "../../../types/transactionTypes"; import useLatestApproveFromEvents from "./useLatestApproveFromEvents"; +import useLatestCancelFromEvents from "./useLatestCancelFromEvents"; import useLatestDepositOrWithdrawFromEvents from "./useLatestDepositOrWithdrawFromEvents"; import useLatestSwapFromEvents from "./useLatestSwapFromEvents"; @@ -16,6 +17,7 @@ const useLatestTransactionEvent = () => { chainId, account ); + const latestCancelEvent = useLatestCancelFromEvents(chainId, account); const [latestEvent, setLatestEvent] = useState(); @@ -37,6 +39,12 @@ const useLatestTransactionEvent = () => { } }, [latestDepositOrWithdrawEvent]); + useEffect(() => { + if (latestCancelEvent) { + setLatestEvent(latestCancelEvent); + } + }, [latestCancelEvent]); + return latestEvent; }; diff --git a/src/features/transactions/transactionsActions.ts b/src/features/transactions/transactionsActions.ts index a3bda600f..54d92bf8a 100644 --- a/src/features/transactions/transactionsActions.ts +++ b/src/features/transactions/transactionsActions.ts @@ -1,14 +1,10 @@ -import { TransactionReceipt } from "@ethersproject/providers"; import { createAction } from "@reduxjs/toolkit"; -import { AppDispatch } from "../../app/store"; import { ProtocolType, SubmittedApprovalTransaction, SubmittedTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; -import { TransactionStatusType } from "../../types/transactionTypes"; -import { updateTransaction } from "./transactionsHelpers"; export const submitTransaction = createAction< SubmittedTransaction | SubmittedApprovalTransaction diff --git a/src/features/transactions/transactionsHelpers.ts b/src/features/transactions/transactionsHelpers.ts index 1c9528cfa..875c611fb 100644 --- a/src/features/transactions/transactionsHelpers.ts +++ b/src/features/transactions/transactionsHelpers.ts @@ -6,6 +6,10 @@ import { findMatchingApprovalTransaction, isApproveEvent, } from "../../entities/ApproveEvent/ApproveEventHelpers"; +import { + findMatchingCancelTransaction, + isCancelEvent, +} from "../../entities/CancelEvent/CancelEventHelpers"; import { FullSwapERC20Event } from "../../entities/FullSwapERC20Event/FullSwapERC20Event"; import { findMatchingOrderTransaction, @@ -18,6 +22,7 @@ import { } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { isApprovalTransaction, + isCancelTransaction, isDepositTransaction, isLastLookOrderTransaction, isOrderTransaction, @@ -36,6 +41,7 @@ import { import { WethEventType } from "../../types/wethEventType"; import { handleApproveTransaction, + handleSubmittedCancelOrder, handleSubmittedDepositOrder, handleSubmittedRFQOrder, handleSubmittedWithdrawOrder, @@ -97,6 +103,12 @@ const getMatchingTransaction = ( .find((transaction) => findMatchingOrderTransaction(transaction, event)); } + if (isCancelEvent(event)) { + return transactions + .filter(isCancelTransaction) + .find((transaction) => findMatchingCancelTransaction(transaction, event)); + } + return undefined; }; @@ -152,6 +164,10 @@ export const handleTransactionResolved = ) { handleSubmittedRFQOrder(transaction, transaction.status); } + + if (isCancelTransaction(transaction)) { + handleSubmittedCancelOrder(transaction.status); + } }; export const updateTransactionWithReceipt = diff --git a/src/features/transactions/transactionsHooks.ts b/src/features/transactions/transactionsHooks.ts index ec169f324..bf1619b71 100644 --- a/src/features/transactions/transactionsHooks.ts +++ b/src/features/transactions/transactionsHooks.ts @@ -10,6 +10,7 @@ import { TransactionStatusType } from "../../types/transactionTypes"; import useHistoricalTransactions from "./hooks/useHistoricalTransactions"; import useLatestSucceededTransaction from "./hooks/useLatestSucceededTransaction"; import useLatestTransactionEvent from "./hooks/useLatestTransactionEvent"; +import useTransactionsFilterFromLocalStorage from "./hooks/useTransactionsFilterFromLocalStorage"; import { updateTransactionWithReceipt } from "./transactionsHelpers"; import { handleTransactionResolved, @@ -31,8 +32,9 @@ export const useTransactions = (): void => { const [historicalTransactions] = useHistoricalTransactions(); const latestTransactionEvent = useLatestTransactionEvent(); - // TODO: Right now only succeeded transactions are handled, we should also handle failed and expired transactions. + // TODO: Right now only succeeded transactions are handled, we should also handle expired transactions here. const latestSuccessfulTransaction = useLatestSucceededTransaction(); + useTransactionsFilterFromLocalStorage(); // When the transactions change, we want to save them to local storage. useEffect(() => { @@ -65,7 +67,7 @@ export const useTransactions = (): void => { } }; - const processingTransactions = transactions.filter( + const processingTransactions = localStorageTransactions.filter( (transaction) => transaction.status === TransactionStatusType.processing ); diff --git a/src/features/transactions/transactionsSlice.ts b/src/features/transactions/transactionsSlice.ts index 2cfdf2a8e..21d6aa609 100644 --- a/src/features/transactions/transactionsSlice.ts +++ b/src/features/transactions/transactionsSlice.ts @@ -289,9 +289,7 @@ export const selectPendingApprovals = (state: RootState) => export const selectCancellations = (state: RootState) => state.transactions.transactions.filter( - (tx) => - tx.status === TransactionStatusType.processing && - tx.type === TransactionTypes.cancel + (tx) => tx.type === TransactionTypes.cancel ) as SubmittedCancellation[]; export const selectPendingCancellations = (state: RootState) => diff --git a/src/hooks/useCancellationSuccess.ts b/src/hooks/useCancellationSuccess.ts index 908534307..fb06196f6 100644 --- a/src/hooks/useCancellationSuccess.ts +++ b/src/hooks/useCancellationSuccess.ts @@ -2,15 +2,18 @@ import { useMemo } from "react"; import { useAppSelector } from "../app/hooks"; import { selectCancellations } from "../features/transactions/transactionsSlice"; +import { TransactionStatusType } from "../types/transactionTypes"; const useCancellationSuccess = (nonce: string): boolean => { const canceledTransactions = useAppSelector(selectCancellations); - return useMemo(() => { - return canceledTransactions.some( + const transaction = useMemo(() => { + return canceledTransactions.find( (transaction) => transaction.nonce === nonce ); }, [nonce, canceledTransactions]); + + return transaction?.status === TransactionStatusType.succeeded; }; export default useCancellationSuccess; diff --git a/src/types/transactionTypes.ts b/src/types/transactionTypes.ts index fb1c6db03..e6a9b01ad 100644 --- a/src/types/transactionTypes.ts +++ b/src/types/transactionTypes.ts @@ -1,8 +1,13 @@ import { ApproveEvent } from "../entities/ApproveEvent/ApproveEvent"; +import { CancelEvent } from "../entities/CancelEvent/CancelEvent"; import { FullSwapERC20Event } from "../entities/FullSwapERC20Event/FullSwapERC20Event"; import { WETHEvent } from "../entities/WETHEvent/WETHEvent"; -export type TransactionEvent = FullSwapERC20Event | ApproveEvent | WETHEvent; +export type TransactionEvent = + | FullSwapERC20Event + | ApproveEvent + | WETHEvent + | CancelEvent; export enum TransactionTypes { approval = "approval", From e36e1791d802781986623efb5bb2c8a74d66bced Mon Sep 17 00:00:00 2001 From: piersss Date: Fri, 5 Apr 2024 23:15:44 +0200 Subject: [PATCH 6/7] 869: Fixed Transaction link in cancel transaction --- .../subcomponents/WalletTransaction/WalletTransaction.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx b/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx index 0e6d5dc45..41c2b0da2 100644 --- a/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx +++ b/src/components/TransactionsTab/subcomponents/WalletTransaction/WalletTransaction.tsx @@ -91,7 +91,11 @@ const WalletTransaction = ({ - + ); } From ed5df74e213f81faa22de2d4cecdbf2ee9e479ff Mon Sep 17 00:00:00 2001 From: piersss Date: Fri, 5 Apr 2024 23:23:11 +0200 Subject: [PATCH 7/7] 869: Added TODO to transactionHooks --- src/features/transactions/transactionsHooks.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/transactions/transactionsHooks.ts b/src/features/transactions/transactionsHooks.ts index bf1619b71..d298a3d0a 100644 --- a/src/features/transactions/transactionsHooks.ts +++ b/src/features/transactions/transactionsHooks.ts @@ -32,7 +32,7 @@ export const useTransactions = (): void => { const [historicalTransactions] = useHistoricalTransactions(); const latestTransactionEvent = useLatestTransactionEvent(); - // TODO: Right now only succeeded transactions are handled, we should also handle expired transactions here. + // TODO: Right now only succeeded transactions are handled, we should also handle expired transactions here. https://github.com/airswap/airswap-web/issues/891 const latestSuccessfulTransaction = useLatestSucceededTransaction(); useTransactionsFilterFromLocalStorage();