From fc81ada70cdc9a064d04b5ad78fe0a737c31f0e0 Mon Sep 17 00:00:00 2001 From: piersss <86911296+piersss@users.noreply.github.com> Date: Wed, 17 Apr 2024 22:44:20 +0200 Subject: [PATCH] 891: Fix LastLook expired transactions (#895) --- package.json | 1 + .../hooks/useSessionOrderTransaction.ts | 4 +- .../@widgets/SwapWidget/SwapWidget.tsx | 32 ++---- .../SwapWidgetHeader/SwapWidgetHeader.tsx | 9 +- src/components/Toasts/ToastController.tsx | 15 ++- src/components/Toasts/TransactionToast.tsx | 72 ++++++------- .../TransactionsTab/TransactionsTab.tsx | 13 +-- .../AnimatedWalletTransaction.tsx | 14 +-- .../WalletTransaction/WalletTransaction.tsx | 37 ++++--- src/constants/configParams.ts | 2 +- src/contexts/lastLook/LastLook.tsx | 31 ++---- .../FullSwapERC20EventHelpers.ts | 12 ++- .../SubmittedTransaction.ts | 35 +++--- .../SubmittedTransactionHelpers.ts | 54 +++++++--- .../SubmittedTransactionTransformers.ts | 54 ++++------ src/features/orders/ordersActions.ts | 23 ++-- src/features/orders/ordersSlice.ts | 44 +++++--- .../hooks/useHistoricalTransactions.ts | 14 ++- .../hooks/useLatestExpiredTransaction.ts | 58 ++++++++++ .../transactions/transactionsActions.ts | 18 +--- .../transactions/transactionsHelpers.ts | 35 +++--- .../transactions/transactionsHooks.ts | 22 +++- .../transactions/transactionsSlice.ts | 100 ++---------------- .../transactions/transactionsUtils.ts | 23 +--- src/features/wallet/Wallet.tsx | 5 +- src/hooks/useOrderTransaction.ts | 6 +- src/hooks/useOrderTransactionLink.ts | 4 +- yarn.lock | 7 ++ 28 files changed, 361 insertions(+), 383 deletions(-) create mode 100644 src/features/transactions/hooks/useLatestExpiredTransaction.ts diff --git a/package.json b/package.json index beb5528dd..0c3794fa5 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "tls-browserify": "^0.2.2", "truncate-eth-address": "^1.0.2", "typescript": "^4.4.3", + "usehooks-ts": "^3.1.0", "zlib-browserify": "^0.0.3" }, "devDependencies": { diff --git a/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts b/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts index 932f6d961..760dbabcb 100644 --- a/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts +++ b/src/components/@widgets/OrderDetailWidget/hooks/useSessionOrderTransaction.ts @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from "react"; import { useAppSelector } from "../../../../app/hooks"; import { SubmittedTransaction } from "../../../../entities/SubmittedTransaction/SubmittedTransaction"; +import { isSubmittedOrder } from "../../../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { selectOrderTransactions } from "../../../../features/transactions/transactionsSlice"; import { TransactionStatusType } from "../../../../types/transactionTypes"; @@ -21,7 +22,8 @@ const useSessionOrderTransaction = ( } if ( - transactions[0].nonce === nonce && + isSubmittedOrder(transactions[0]) && + transactions[0].order.nonce === nonce && 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 01f18ebb3..9a5881316 100644 --- a/src/components/@widgets/SwapWidget/SwapWidget.tsx +++ b/src/components/@widgets/SwapWidget/SwapWidget.tsx @@ -30,7 +30,6 @@ import nativeCurrency, { } from "../../../constants/nativeCurrency"; import { InterfaceContext } from "../../../contexts/interface/Interface"; import { LastLookContext } from "../../../contexts/lastLook/LastLook"; -import { ProtocolType } from "../../../entities/SubmittedTransaction/SubmittedTransaction"; import { AppErrorType } from "../../../errors/appError"; import transformUnknownErrorToAppError from "../../../errors/transformUnknownErrorToAppError"; import { @@ -592,28 +591,15 @@ const SwapWidget: FC = () => { setIsSwapping(false); return; } - const accepted = await LastLook.sendOrderForConsideration({ + + LastLook.sendOrderForConsideration({ locator: bestTradeOption!.pricing!.locator, order: order, }); - setIsSwapping(false); - if (accepted) { - setShowOrderSubmitted(true); - LastLook.unsubscribeAllServers(); - } else { - notifyError({ - heading: t("orders.swapRejected"), - cta: t("orders.swapRejectedCallToAction"), - }); - dispatch( - declineTransaction({ - signerWallet: order.signerWallet, - nonce: order.nonce, - reason: "Pricing expired", - }) - ); - } + setIsSwapping(false); + setShowOrderSubmitted(true); + LastLook.unsubscribeAllServers(); } catch (e: any) { setIsSwapping(false); dispatch(clearTradeTermsQuoteAmount()); @@ -671,10 +657,10 @@ const SwapWidget: FC = () => { ]); const takeBestOption = async () => { - if (bestTradeOption!.protocol === "request-for-quote-erc20") { - await swapWithRequestForQuote(); - } else { + if (bestTradeOption?.isLastLook) { await swapWithLastLook(); + } else { + await swapWithRequestForQuote(); } }; @@ -809,10 +795,10 @@ const SwapWidget: FC = () => { <> setShowGasFeeInfo(true)} - protocol={bestTradeOption?.protocol as ProtocolType} expiry={bestTradeOption?.order?.expiry} /> {!isApproving && !isSwapping && !showOrderSubmitted && ( diff --git a/src/components/@widgets/SwapWidget/subcomponents/SwapWidgetHeader/SwapWidgetHeader.tsx b/src/components/@widgets/SwapWidget/subcomponents/SwapWidgetHeader/SwapWidgetHeader.tsx index f05dcc27f..9c175f4f5 100644 --- a/src/components/@widgets/SwapWidget/subcomponents/SwapWidgetHeader/SwapWidgetHeader.tsx +++ b/src/components/@widgets/SwapWidget/subcomponents/SwapWidgetHeader/SwapWidgetHeader.tsx @@ -2,7 +2,6 @@ import { FC, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { RFQ_EXPIRY_BUFFER_MS } from "../../../../../constants/configParams"; -import { ProtocolType } from "../../../../../entities/SubmittedTransaction/SubmittedTransaction"; import { WidgetHeader } from "../../../../../styled-components/WidgetHeader/WidgetHeader"; import { Title } from "../../../../Typography/Typography"; import { @@ -14,18 +13,18 @@ import { } from "./SwapWidgetHeader.styles"; interface SwapWidgetHeaderProps { + isLastLook: boolean; title: string; isQuote: boolean; onGasFreeTradeButtonClick: () => void; - protocol?: ProtocolType; expiry?: string; } const SwapWidgetHeader: FC = ({ + isLastLook, title, isQuote, onGasFreeTradeButtonClick, - protocol, expiry, }) => { const { t } = useTranslation(); @@ -40,14 +39,14 @@ const SwapWidgetHeader: FC = ({ {title} - {protocol === "last-look-erc20" && isQuote && ( + {isLastLook && isQuote && ( )} - {protocol === "request-for-quote-erc20" && isQuote && ( + {!isLastLook && isQuote && expiryTime && ( {t("orders.newQuoteIn")} {expiryTime && } diff --git a/src/components/Toasts/ToastController.tsx b/src/components/Toasts/ToastController.tsx index 13366db26..b6a7482bc 100644 --- a/src/components/Toasts/ToastController.tsx +++ b/src/components/Toasts/ToastController.tsx @@ -8,8 +8,9 @@ import { SubmittedApprovalTransaction, SubmittedDepositTransaction, SubmittedTransaction, - SubmittedTransactionWithOrder, + SubmittedOrder, SubmittedWithdrawTransaction, + SubmittedOrderUnderConsideration, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import findEthOrTokenByAddress from "../../helpers/findEthOrTokenByAddress"; import { TransactionTypes } from "../../types/transactionTypes"; @@ -34,8 +35,7 @@ export const notifyTransaction = ( type === TransactionTypes.withdraw) && chainId ) { - const tx: SubmittedTransactionWithOrder = - transaction as SubmittedTransactionWithOrder; + const tx: SubmittedOrder = transaction as SubmittedOrder; /* TODO: fix toaster for multiple tabs or apps now that we have a listener, you can have multiple tabs open that receives the same order event. Only one redux @@ -140,7 +140,7 @@ export const notifyWithdrawal = (transaction: SubmittedWithdrawTransaction) => { ); }; -export const notifyOrder = (transaction: SubmittedTransactionWithOrder) => { +export const notifyOrder = (transaction: SubmittedOrder) => { toast( (t) => ( { ); }; +export const notifyOrderExpiry = () => { + notifyError({ + heading: i18n.t("orders.swapRejected"), + cta: i18n.t("orders.swapRejectedCallToAction"), + }); +}; + export const notifyCopySuccess = () => { toast( (t) => ( diff --git a/src/components/Toasts/TransactionToast.tsx b/src/components/Toasts/TransactionToast.tsx index 8faa3bb3c..12e8be5ca 100644 --- a/src/components/Toasts/TransactionToast.tsx +++ b/src/components/Toasts/TransactionToast.tsx @@ -6,11 +6,14 @@ import { TokenInfo } from "@airswap/utils"; import { formatUnits } from "ethers/lib/utils"; +import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { - SubmittedLastLookOrder, - SubmittedRFQOrder, - SubmittedTransaction, -} from "../../entities/SubmittedTransaction/SubmittedTransaction"; + isApprovalTransaction, + isDepositTransaction, + isLastLookOrderTransaction, + isSubmittedOrder, + isWithdrawTransaction, +} from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { TransactionTypes } from "../../types/transactionTypes"; import { InfoHeading } from "../Typography/Typography"; import { @@ -62,6 +65,13 @@ const TransactionToast = ({ approvalToken, }: TransactionToastProps) => { const { t } = useTranslation(); + const order = + isSubmittedOrder(transaction) || + isWithdrawTransaction(transaction) || + isDepositTransaction(transaction) + ? transaction.order + : undefined; + const isApproval = isApprovalTransaction(transaction); return ( @@ -74,9 +84,7 @@ const TransactionToast = ({ - {type === TransactionTypes.order || - type === TransactionTypes.deposit || - type === TransactionTypes.withdraw + {!isApproval ? error ? t("toast.swapFail") : t("toast.swapComplete") @@ -86,37 +94,29 @@ const TransactionToast = ({ {(() => { - if ( - type === TransactionTypes.order || - type === TransactionTypes.deposit || - type === TransactionTypes.withdraw - ) { - if (transaction && senderToken && signerToken) { - const tx = - transaction.protocol === "last-look-erc20" - ? (transaction as SubmittedLastLookOrder) - : (transaction as SubmittedRFQOrder); - let translationKey = "wallet.transaction"; - if (tx.protocol === "last-look-erc20") { - translationKey = "wallet.lastLookTransaction"; - } - // @ts-ignore dynamic translation key - return t(translationKey, { - senderAmount: parseFloat( - Number( - formatUnits(tx.order.senderAmount, senderToken.decimals) - ).toFixed(5) - ), - senderToken: senderToken.symbol, - signerAmount: parseFloat( - Number( - formatUnits(tx.order.signerAmount, signerToken.decimals) - ).toFixed(5) - ), - signerToken: signerToken.symbol, - }); + if (order && senderToken && signerToken) { + let translationKey = "wallet.transaction"; + if (isLastLookOrderTransaction(transaction)) { + translationKey = "wallet.lastLookTransaction"; } + + // @ts-ignore dynamic translation key + return t(translationKey, { + senderAmount: parseFloat( + Number( + formatUnits(order.senderAmount, senderToken.decimals) + ).toFixed(5) + ), + senderToken: senderToken.symbol, + signerAmount: parseFloat( + Number( + formatUnits(order.signerAmount, signerToken.decimals) + ).toFixed(5) + ), + signerToken: signerToken.symbol, + }); } + return t("toast.approve", { symbol: approvalToken?.symbol }); })()} diff --git a/src/components/TransactionsTab/TransactionsTab.tsx b/src/components/TransactionsTab/TransactionsTab.tsx index fba024ba3..08ce7a188 100644 --- a/src/components/TransactionsTab/TransactionsTab.tsx +++ b/src/components/TransactionsTab/TransactionsTab.tsx @@ -15,6 +15,7 @@ import { formatUnits } from "ethers/lib/utils"; import { AnimatePresence, useReducedMotion } from "framer-motion"; import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; +import { getSubmittedTransactionKey } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { BalancesState } from "../../features/balances/balancesSlice"; import useAddressOrEnsName from "../../hooks/useAddressOrEnsName"; import { useKeyPress } from "../../hooks/useKeyPress"; @@ -53,6 +54,7 @@ type TransactionsTabType = { address: string; chainId: number; open: boolean; + protocolFee: number; setTransactionsTabOpen: (x: boolean) => void; onClearTransactionsChange: (value: ClearOrderType) => void; /** @@ -60,7 +62,6 @@ type TransactionsTabType = { */ onDisconnectWalletClicked: () => void; transactions: SubmittedTransaction[]; - tokens: TokenInfo[]; balances: BalancesState; isUnsupportedNetwork?: boolean; }; @@ -69,11 +70,11 @@ const TransactionsTab = ({ address = "", chainId, open, + protocolFee, setTransactionsTabOpen, onClearTransactionsChange, onDisconnectWalletClicked, transactions = [], - tokens = [], balances, isUnsupportedNetwork = false, }: TransactionsTabType) => { @@ -225,9 +226,9 @@ const TransactionsTab = ({ {pendingTransactions.map((transaction) => ( ))} @@ -243,9 +244,9 @@ const TransactionsTab = ({ {completedTransactions.map((transaction) => ( ))} diff --git a/src/components/TransactionsTab/subcomponents/AnimatedWalletTransaction/AnimatedWalletTransaction.tsx b/src/components/TransactionsTab/subcomponents/AnimatedWalletTransaction/AnimatedWalletTransaction.tsx index c8b921011..9d19603a3 100644 --- a/src/components/TransactionsTab/subcomponents/AnimatedWalletTransaction/AnimatedWalletTransaction.tsx +++ b/src/components/TransactionsTab/subcomponents/AnimatedWalletTransaction/AnimatedWalletTransaction.tsx @@ -11,23 +11,14 @@ import { walletTransactionHeight } from "../WalletTransaction/WalletTransaction. import { Container } from "./AnimatedWalletTransaction.styles"; interface AnimatedWalletTransactionProps { - /** - * The parent object of SubmittedOrder and SubmittedApproval - */ + protocolFee: number; transaction: SubmittedTransaction; - /** - * All token metadata - */ - tokens: TokenInfo[]; - /** - * chainId of current Ethereum net - */ chainId: number; } const AnimatedWalletTransaction = ({ + protocolFee, transaction, - tokens, chainId, }: AnimatedWalletTransactionProps) => { const theme = useTheme(); @@ -71,6 +62,7 @@ const AnimatedWalletTransaction = ({ { - /** - * The parent object of SubmittedOrder and SubmittedApproval - */ + protocolFee: number; transaction: SubmittedTransaction; - /** - * chainId of current Ethereum net - */ chainId: number; } const WalletTransaction = ({ + protocolFee, transaction, chainId, animate, @@ -60,7 +58,9 @@ const WalletTransaction = ({ return ( - {t("wallet.approve")} + + {t("wallet.approve", { symbol: transaction.token.symbol })} + {statusText} ยท {timeBetween} @@ -101,19 +101,22 @@ const WalletTransaction = ({ } if ( - isOrderTransaction(transaction) || + isSubmittedOrder(transaction) || + isSubmittedOrderUnderConsideration(transaction) || isWithdrawTransaction(transaction) || isDepositTransaction(transaction) ) { const { signerToken, senderToken } = transaction; - const hasExpiry = !!transaction.expiry; + const expiry = isSubmittedOrder(transaction) + ? transaction.order.expiry + : undefined; // For last look transactions, the user has sent the signer amount plus // the fee: let signerAmountWithFee: string | null = null; - if (transaction.protocol === "last-look-erc20") { + if (isLastLookOrderTransaction(transaction)) { signerAmountWithFee = new BigNumber(transaction.order.signerAmount) - .multipliedBy(1.0007) + .multipliedBy(1 + protocolFee / 10000) .integerValue(BigNumber.ROUND_FLOOR) .toString(); } @@ -133,12 +136,12 @@ const WalletTransaction = ({ <> {t( - transaction.protocol === "last-look-erc20" + isSubmittedOrder(transaction) && transaction.isLastLook ? "wallet.lastLookTransaction" : "wallet.transaction", { @@ -163,11 +166,11 @@ const WalletTransaction = ({ } )} - {hasExpiry && + {!!expiry && transaction.status === TransactionStatusType.processing ? ( ) : ( @@ -178,7 +181,7 @@ const WalletTransaction = ({ )} - {transaction.hash && ( + {!isSubmittedOrderUnderConsideration(transaction) && ( { .multipliedBy(10 ** terms.baseToken.decimals) // Note that we remove the signer fee from the amount that we send. // This was already done to determine quoteAmount. - .dividedBy(terms.side === "sell" ? 1.0007 : 1) + .dividedBy(terms.side === "sell" ? 1 + protocolFee / 10000 : 1) .integerValue(BigNumber.ROUND_CEIL) .toString(); const quoteAmountAtomic = new BigNumber(terms.quoteAmount!) @@ -182,19 +176,14 @@ const LastLookProvider: FC = ({ children }) => { const signerToken = tokens.find((t) => t.address === order.signerToken); const senderToken = tokens.find((t) => t.address === order.senderToken); - const transaction = transformToSubmittedLastLookOrder( - undefined, - order, - signerToken!, - senderToken! - ); + const transaction = + transformToSubmittedTransactionWithOrderUnderConsideration( + order, + signerToken!, + senderToken! + ); - dispatch( - submitTransactionWithExpiry({ - transaction, - signerWallet: unsignedOrder.signerWallet, - }) - ); + dispatch(submitTransaction(transaction)); return { order, diff --git a/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts b/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts index 2e104dd35..b759041ed 100644 --- a/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts +++ b/src/entities/FullSwapERC20Event/FullSwapERC20EventHelpers.ts @@ -1,5 +1,9 @@ import { TransactionEvent } from "../../types/transactionTypes"; -import { SubmittedTransactionWithOrder } from "../SubmittedTransaction/SubmittedTransaction"; +import { + SubmittedOrder, + SubmittedOrderUnderConsideration, +} from "../SubmittedTransaction/SubmittedTransaction"; +import { isSubmittedOrder } from "../SubmittedTransaction/SubmittedTransactionHelpers"; import { FullSwapERC20Event } from "./FullSwapERC20Event"; export const isFullSwapERC20Event = ( @@ -8,12 +12,12 @@ export const isFullSwapERC20Event = ( typeof event === "object" && "name" in event && event.name === "Swap"; export const findMatchingOrderTransaction = ( - transaction: SubmittedTransactionWithOrder, + transaction: SubmittedOrder | SubmittedOrderUnderConsideration, event: FullSwapERC20Event ): boolean => { - if (transaction.hash === event.hash) { + if (isSubmittedOrder(transaction) && transaction.hash === event.hash) { return true; } - return transaction.nonce === event.swap.nonce; + return transaction.order.nonce === event.swap.nonce; }; diff --git a/src/entities/SubmittedTransaction/SubmittedTransaction.ts b/src/entities/SubmittedTransaction/SubmittedTransaction.ts index 8db978a5a..a2eb0c070 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransaction.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransaction.ts @@ -12,34 +12,36 @@ export interface DepositOrWithdrawOrder { senderAmount: string; } -export type ProtocolType = "request-for-quote-erc20" | "last-look-erc20"; - export interface SubmittedTransaction { type: TransactionTypes; - hash?: string; // LL orders doesn't have hash + hash?: string; status: TransactionStatusType; - nonce?: string; - expiry?: string; timestamp: number; - protocol?: ProtocolType; } -export interface SubmittedTransactionWithOrder extends SubmittedTransaction { +export interface SubmittedTransactionWithHash extends SubmittedTransaction { + hash: string; +} + +export interface SubmittedOrder extends SubmittedTransactionWithHash { + isLastLook?: boolean; type: TransactionTypes.order; order: OrderERC20; senderToken: TokenInfo; signerToken: TokenInfo; } -export interface SubmittedRFQOrder extends SubmittedTransactionWithOrder { - protocol: "request-for-quote-erc20"; +export interface SubmittedOrderUnderConsideration + extends Omit { + isLastLook: true; } -export interface SubmittedLastLookOrder extends SubmittedTransactionWithOrder { - protocol: "last-look-erc20"; +export interface SubmittedLastLookOrder extends SubmittedOrder { + isLastLook: true; } -export interface SubmittedApprovalTransaction extends SubmittedTransaction { +export interface SubmittedApprovalTransaction + extends SubmittedTransactionWithHash { type: TransactionTypes.approval; hash: string; amount: string; @@ -47,11 +49,13 @@ export interface SubmittedApprovalTransaction extends SubmittedTransaction { tokenAddress: string; } -export interface SubmittedCancellation extends SubmittedTransaction { +export interface SubmittedCancellation extends SubmittedTransactionWithHash { hash: string; + nonce: string; } -export interface SubmittedDepositTransaction extends SubmittedTransaction { +export interface SubmittedDepositTransaction + extends SubmittedTransactionWithHash { type: TransactionTypes.deposit; hash: string; order: DepositOrWithdrawOrder; @@ -59,7 +63,8 @@ export interface SubmittedDepositTransaction extends SubmittedTransaction { signerToken: TokenInfo; } -export interface SubmittedWithdrawTransaction extends SubmittedTransaction { +export interface SubmittedWithdrawTransaction + extends SubmittedTransactionWithHash { type: TransactionTypes.withdraw; hash: string; order: DepositOrWithdrawOrder; diff --git a/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts b/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts index 40b873708..95c5b0c43 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransactionHelpers.ts @@ -4,10 +4,10 @@ import { SubmittedCancellation, SubmittedDepositTransaction, SubmittedLastLookOrder, - SubmittedRFQOrder, SubmittedTransaction, - SubmittedTransactionWithOrder, + SubmittedOrder, SubmittedWithdrawTransaction, + SubmittedOrderUnderConsideration, } from "./SubmittedTransaction"; export const isApprovalTransaction = ( @@ -30,24 +30,25 @@ export const isWithdrawTransaction = ( ): transaction is SubmittedWithdrawTransaction => transaction.type === TransactionTypes.withdraw; -export const isRfqOrderTransaction = ( +export const isSubmittedOrder = ( transaction: SubmittedTransaction -): transaction is SubmittedRFQOrder => - transaction.type === TransactionTypes.order && - transaction.protocol === "request-for-quote-erc20"; +): transaction is SubmittedOrder => { + return transaction.type === TransactionTypes.order && !!transaction.hash; +}; -export const isLastLookOrderTransaction = ( +export const isSubmittedOrderUnderConsideration = ( transaction: SubmittedTransaction -): transaction is SubmittedLastLookOrder => - transaction.type === TransactionTypes.order && - transaction.protocol === "last-look-erc20"; +): transaction is SubmittedOrderUnderConsideration => { + return transaction.type === TransactionTypes.order && !transaction.hash; +}; -export const isOrderTransaction = ( +export const isLastLookOrderTransaction = ( transaction: SubmittedTransaction -): transaction is SubmittedTransactionWithOrder => { +): transaction is SubmittedLastLookOrder => { return ( - isRfqOrderTransaction(transaction) || - isLastLookOrderTransaction(transaction) + isSubmittedOrder(transaction) && + !!transaction.hash && + !!transaction.isLastLook ); }; @@ -57,3 +58,28 @@ export const sortSubmittedTransactionsByExpiry = ( ) => { return b.timestamp - a.timestamp; }; + +export const getSubmittedTransactionKey = ( + transaction: SubmittedTransaction +) => { + if (isSubmittedOrderUnderConsideration(transaction)) { + return `${transaction.order.signerWallet}-${transaction.order.nonce}-${transaction.timestamp}`; + } + + return transaction.hash; +}; + +export const doesTransactionsMatch = ( + transaction: SubmittedTransaction, + match: SubmittedTransaction, + hash?: string +): boolean => { + if ( + isSubmittedOrderUnderConsideration(transaction) && + isSubmittedOrderUnderConsideration(match) + ) { + return transaction.order.nonce === match.order.nonce; + } + + return transaction.hash === match.hash || transaction.hash === hash; +}; diff --git a/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts b/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts index 574b43be0..f20085c8d 100644 --- a/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts +++ b/src/entities/SubmittedTransaction/SubmittedTransactionTransformers.ts @@ -8,7 +8,8 @@ import { SubmittedApprovalTransaction, SubmittedDepositTransaction, SubmittedLastLookOrder, - SubmittedRFQOrder, + SubmittedOrder, + SubmittedOrderUnderConsideration, SubmittedWithdrawTransaction, } from "./SubmittedTransaction"; @@ -75,46 +76,35 @@ export const transformToSubmittedWithdrawTransaction = ( }; }; -export const transformToSubmittedRFQOrder = ( +export const transformToSubmittedTransactionWithOrder = ( hash: string, order: OrderERC20, signerToken: TokenInfo, senderToken: TokenInfo, status: TransactionStatusType = TransactionStatusType.processing, timestamp = Date.now() -): SubmittedRFQOrder => { - return { - type: TransactionTypes.order, - expiry: order.expiry, - hash, - nonce: order.nonce, - order, - protocol: "request-for-quote-erc20", - senderToken, - signerToken, - status, - timestamp, - }; -}; +): SubmittedOrder => ({ + type: TransactionTypes.order, + hash, + order, + senderToken, + signerToken, + status, + timestamp, +}); -export const transformToSubmittedLastLookOrder = ( - hash: string | undefined, +export const transformToSubmittedTransactionWithOrderUnderConsideration = ( order: OrderERC20, signerToken: TokenInfo, senderToken: TokenInfo, status: TransactionStatusType = TransactionStatusType.processing, timestamp = Date.now() -): SubmittedLastLookOrder => { - return { - type: TransactionTypes.order, - expiry: order.expiry, - hash, - nonce: order.nonce, - order, - protocol: "last-look-erc20", - senderToken, - signerToken, - status, - timestamp, - }; -}; +): SubmittedOrderUnderConsideration => ({ + isLastLook: true, + type: TransactionTypes.order, + order, + senderToken, + signerToken, + status, + timestamp, +}); diff --git a/src/features/orders/ordersActions.ts b/src/features/orders/ordersActions.ts index 62b2b5a07..e4cc04d65 100644 --- a/src/features/orders/ordersActions.ts +++ b/src/features/orders/ordersActions.ts @@ -27,13 +27,13 @@ import { SubmittedApprovalTransaction, SubmittedCancellation, SubmittedDepositTransaction, - SubmittedTransactionWithOrder, + SubmittedOrder, SubmittedWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { transformToSubmittedApprovalTransaction, transformToSubmittedDepositTransaction, - transformToSubmittedRFQOrder, + transformToSubmittedTransactionWithOrder, transformToSubmittedWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransactionTransformers"; import { AppErrorType, isAppError } from "../../errors/appError"; @@ -49,11 +49,9 @@ import { } from "../balances/balancesSlice"; import { declineTransaction, - mineTransaction, revertTransaction, submitTransaction, } from "../transactions/transactionsActions"; -import { submitTransactionWithExpiry } from "../transactions/transactionsSlice"; import { approveToken, depositETH, @@ -165,7 +163,7 @@ export const handleSubmittedWithdrawOrder = ( }; export const handleSubmittedRFQOrder = ( - transaction: SubmittedTransactionWithOrder, + transaction: SubmittedOrder, status: TransactionStatusType ): void => { if (status === TransactionStatusType.failed) { @@ -410,11 +408,10 @@ export const approve = export const take = ( order: OrderERC20 | FullOrderERC20, - senderToken: TokenInfo, signerToken: TokenInfo, + senderToken: TokenInfo, library: Web3Provider, - contractType: "Swap" | "Wrapper", - onExpired?: () => void + contractType: "Swap" | "Wrapper" ) => async (dispatch: AppDispatch): Promise => { dispatch(setStatus("signing")); @@ -461,19 +458,13 @@ export const take = ? order : refactorOrder(order, library._network.chainId); - const transaction = transformToSubmittedRFQOrder( + const transaction = transformToSubmittedTransactionWithOrder( tx.hash, updatedOrder, signerToken, senderToken ); - dispatch( - submitTransactionWithExpiry({ - transaction, - signerWallet: order.signerWallet, - onExpired, - }) - ); + dispatch(submitTransaction(transaction)); dispatch(setStatus("idle")); }; diff --git a/src/features/orders/ordersSlice.ts b/src/features/orders/ordersSlice.ts index 7ea54610d..ae07388c8 100644 --- a/src/features/orders/ordersSlice.ts +++ b/src/features/orders/ordersSlice.ts @@ -97,36 +97,49 @@ export const selectSortedOrders = (state: RootState) => export const selectFirstOrder = (state: RootState) => state.orders.orders[0]; +interface BestTradeOption { + quoteAmount: string; + isLastLook?: boolean; + pricing?: { + pricing: Levels; + locator: string; + quoteAmount: string; + }; + order?: OrderERC20; +} + export const selectBestOption = createSelector( selectTradeTerms, selectBestOrder, selectBestPricing, selectGasPriceInQuoteTokens, - (terms, bestRfqOrder, bestPricing, gasPriceInQuoteTokens) => { - if (!terms) return null; + ( + terms, + bestRfqOrder, + bestPricing, + gasPriceInQuoteTokens + ): BestTradeOption | undefined => { + if (!terms) return undefined; if (terms.side === "buy") { console.error(`Buy orders not implemented yet`); - return null; + return undefined; } - let pricing = bestPricing as unknown as { - pricing: Levels; - locator: string; - quoteAmount: string; - } | null; - - // TODO: Delete this - // Temp disable bestRfqOrder - // @ts-ignore - // bestRfqOrder = null; + let pricing = bestPricing as unknown as + | { + pricing: Levels; + locator: string; + quoteAmount: string; + } + | undefined; - if (!bestRfqOrder && !pricing) return null; + if (!bestRfqOrder && !pricing) return undefined; let lastLookOrder; if (pricing) { lastLookOrder = { + isLastLook: true, quoteAmount: pricing!.quoteAmount, - protocol: "last-look-erc20", pricing: pricing!, }; if (!bestRfqOrder) return lastLookOrder; @@ -140,7 +153,6 @@ export const selectBestOption = createSelector( ); rfqOrder = { quoteAmount: bestRFQQuoteTokens.toString(), - protocol: "request-for-quote-erc20", order: bestRfqOrder, }; if (!lastLookOrder) return rfqOrder; diff --git a/src/features/transactions/hooks/useHistoricalTransactions.ts b/src/features/transactions/hooks/useHistoricalTransactions.ts index c8a4fbc47..3f8683c3b 100644 --- a/src/features/transactions/hooks/useHistoricalTransactions.ts +++ b/src/features/transactions/hooks/useHistoricalTransactions.ts @@ -5,7 +5,7 @@ import { useWeb3React } from "@web3-react/core"; import { useAppSelector } from "../../../app/hooks"; import { SubmittedTransaction } from "../../../entities/SubmittedTransaction/SubmittedTransaction"; import { sortSubmittedTransactionsByExpiry } from "../../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; -import { transformToSubmittedRFQOrder } from "../../../entities/SubmittedTransaction/SubmittedTransactionTransformers"; +import { transformToSubmittedTransactionWithOrder } from "../../../entities/SubmittedTransaction/SubmittedTransactionTransformers"; import { getUniqueArrayChildren } from "../../../helpers/array"; import { getOrdersFromLogs } from "../../../helpers/getOrdersFromLogs"; import { compareAddresses } from "../../../helpers/string"; @@ -55,11 +55,9 @@ const useHistoricalTransactions = (): [ setTransactions(undefined); const getTransactionsFromLogs = async () => { - const rfqOrders = await getOrdersFromLogs(chainId, swapLogs.swapLogs); - // TODO: Add support for lastLook orders https://github.com/airswap/airswap-web/issues/891 - // const lastLookOrders = await getOrdersFromLogs(swapLogs.swapLogs); + const orders = await getOrdersFromLogs(chainId, swapLogs.swapLogs); - const rfqSubmittedTransactions = rfqOrders + const submittedTransactions = orders .filter( (order) => compareAddresses(order.params.signerWallet, account) || @@ -75,7 +73,7 @@ const useHistoricalTransactions = (): [ if (!signerToken || !senderToken) return; - return transformToSubmittedRFQOrder( + return transformToSubmittedTransactionWithOrder( order.hash, order.params, signerToken, @@ -85,12 +83,12 @@ const useHistoricalTransactions = (): [ ); }); - const transactions = rfqSubmittedTransactions.filter( + const transactions = submittedTransactions.filter( (order) => !!order ) as SubmittedTransaction[]; const uniqueTransactions = getUniqueArrayChildren( transactions, - "nonce" + "hash" ); const sortedTransactions = uniqueTransactions.sort( diff --git a/src/features/transactions/hooks/useLatestExpiredTransaction.ts b/src/features/transactions/hooks/useLatestExpiredTransaction.ts new file mode 100644 index 000000000..413838f5a --- /dev/null +++ b/src/features/transactions/hooks/useLatestExpiredTransaction.ts @@ -0,0 +1,58 @@ +import { useMemo, useState } from "react"; + +import { useInterval } from "usehooks-ts"; + +import { useAppSelector } from "../../../app/hooks"; +import { + ASSUMED_EXPIRY_NOTIFICATION_BUFFER_SEC, + LAST_LOOK_ORDER_EXPIRY_SEC, +} from "../../../constants/configParams"; +import { + SubmittedOrder, + SubmittedOrderUnderConsideration, + SubmittedTransaction, +} from "../../../entities/SubmittedTransaction/SubmittedTransaction"; +import { + isSubmittedOrder, + isSubmittedOrderUnderConsideration, +} from "../../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; +import { TransactionStatusType } from "../../../types/transactionTypes"; +import { selectTransactions } from "../transactionsSlice"; + +const useLatestExpiredTransaction = (): + | SubmittedOrder + | SubmittedOrderUnderConsideration + | undefined => { + const transactions: SubmittedTransaction[] = + useAppSelector(selectTransactions); + const [currentTime, setCurrentTime] = useState(0); + + useInterval(() => { + setCurrentTime(Math.round(new Date().getTime() / 1000)); + }, 1000); + + const lastExpiredOrderUnderConsideration = useMemo(() => { + return transactions + .filter(isSubmittedOrderUnderConsideration) + .find((transaction) => { + return ( + transaction.status === TransactionStatusType.processing && + +transaction.order.expiry + LAST_LOOK_ORDER_EXPIRY_SEC < currentTime + ); + }); + }, [transactions, currentTime]); + + const lastExpiredOrderWithOrder = useMemo(() => { + return transactions.filter(isSubmittedOrder).find((transaction) => { + return ( + transaction.status === TransactionStatusType.processing && + +transaction.order.expiry + ASSUMED_EXPIRY_NOTIFICATION_BUFFER_SEC < + currentTime + ); + }); + }, [transactions, currentTime]); + + return lastExpiredOrderUnderConsideration || lastExpiredOrderWithOrder; +}; + +export default useLatestExpiredTransaction; diff --git a/src/features/transactions/transactionsActions.ts b/src/features/transactions/transactionsActions.ts index 54d92bf8a..9d6e553a1 100644 --- a/src/features/transactions/transactionsActions.ts +++ b/src/features/transactions/transactionsActions.ts @@ -1,13 +1,12 @@ import { createAction } from "@reduxjs/toolkit"; import { - ProtocolType, - SubmittedApprovalTransaction, + SubmittedOrderUnderConsideration, SubmittedTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; export const submitTransaction = createAction< - SubmittedTransaction | SubmittedApprovalTransaction + SubmittedTransaction | SubmittedOrderUnderConsideration >("transaction/submitTransaction"); export const declineTransaction = createAction<{ @@ -15,16 +14,8 @@ export const declineTransaction = createAction<{ signerWallet?: string; nonce?: string; reason?: string; - protocol?: ProtocolType; }>("transactions/declineTransaction"); -export const mineTransaction = createAction<{ - protocol?: ProtocolType; - signerWallet?: string; - hash?: string; - nonce?: string; -}>("transaction/mineTransaction"); - export const revertTransaction = createAction<{ hash?: string; signerWallet?: string; @@ -32,11 +23,6 @@ export const revertTransaction = createAction<{ reason?: string; }>("transactions/revertTransaction"); -export const expireTransaction = createAction<{ - signerWallet: string; - nonce: string; -}>("transactions/expireTransaction"); - export const updateTransactions = createAction( "transactions/updateTransactions" ); diff --git a/src/features/transactions/transactionsHelpers.ts b/src/features/transactions/transactionsHelpers.ts index 875c611fb..0f040c0f2 100644 --- a/src/features/transactions/transactionsHelpers.ts +++ b/src/features/transactions/transactionsHelpers.ts @@ -1,7 +1,6 @@ import { TransactionReceipt } from "@ethersproject/providers"; import { AppDispatch, RootState } from "../../app/store"; -import { ApproveEvent } from "../../entities/ApproveEvent/ApproveEvent"; import { findMatchingApprovalTransaction, isApproveEvent, @@ -10,26 +9,26 @@ import { findMatchingCancelTransaction, isCancelEvent, } from "../../entities/CancelEvent/CancelEventHelpers"; -import { FullSwapERC20Event } from "../../entities/FullSwapERC20Event/FullSwapERC20Event"; import { findMatchingOrderTransaction, isFullSwapERC20Event, } from "../../entities/FullSwapERC20Event/FullSwapERC20EventHelpers"; import { SubmittedDepositTransaction, + SubmittedOrder, + SubmittedOrderUnderConsideration, SubmittedTransaction, SubmittedWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { + doesTransactionsMatch, isApprovalTransaction, isCancelTransaction, isDepositTransaction, - isLastLookOrderTransaction, - isOrderTransaction, - isRfqOrderTransaction, + isSubmittedOrder, + isSubmittedOrderUnderConsideration, isWithdrawTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; -import { WETHEvent } from "../../entities/WETHEvent/WETHEvent"; import { findMatchingDepositOrWithdrawTransaction, isWETHEvent, @@ -52,10 +51,8 @@ export const updateTransaction = (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.hash === previousHash + const transactionIndex = transactions.findIndex((transaction) => + doesTransactionsMatch(transaction, updatedTransaction, previousHash) ); if (transactionIndex === -1) { @@ -98,9 +95,14 @@ const getMatchingTransaction = ( } if (isFullSwapERC20Event(event)) { - return transactions - .filter(isOrderTransaction) - .find((transaction) => findMatchingOrderTransaction(transaction, event)); + const orderUnderConsiderationTransactions = transactions.filter( + isSubmittedOrderUnderConsideration + ); + const orderTransactions = transactions.filter(isSubmittedOrder); + + return [...orderTransactions, ...orderUnderConsiderationTransactions].find( + (transaction) => findMatchingOrderTransaction(transaction, event) + ); } if (isCancelEvent(event)) { @@ -124,6 +126,8 @@ export const handleTransactionEvent = pendingTransactions ); + console.log(matchingTransaction, event); + if (!matchingTransaction) { return; } @@ -158,10 +162,7 @@ export const handleTransactionResolved = handleSubmittedWithdrawOrder(transaction, transaction.status, dispatch); } - if ( - isRfqOrderTransaction(transaction) || - isLastLookOrderTransaction(transaction) - ) { + if (isSubmittedOrder(transaction)) { handleSubmittedRFQOrder(transaction, transaction.status); } diff --git a/src/features/transactions/transactionsHooks.ts b/src/features/transactions/transactionsHooks.ts index d298a3d0a..a439eb65d 100644 --- a/src/features/transactions/transactionsHooks.ts +++ b/src/features/transactions/transactionsHooks.ts @@ -3,15 +3,20 @@ import { useEffect } from "react"; import { useWeb3React } from "@web3-react/core"; import { useAppDispatch, useAppSelector } from "../../app/hooks"; +import { notifyOrderExpiry } from "../../components/Toasts/ToastController"; import { SubmittedTransaction } from "../../entities/SubmittedTransaction/SubmittedTransaction"; import { sortSubmittedTransactionsByExpiry } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { getUniqueArrayChildren } from "../../helpers/array"; import { TransactionStatusType } from "../../types/transactionTypes"; import useHistoricalTransactions from "./hooks/useHistoricalTransactions"; +import useLatestExpiredTransaction from "./hooks/useLatestExpiredTransaction"; import useLatestSucceededTransaction from "./hooks/useLatestSucceededTransaction"; import useLatestTransactionEvent from "./hooks/useLatestTransactionEvent"; import useTransactionsFilterFromLocalStorage from "./hooks/useTransactionsFilterFromLocalStorage"; -import { updateTransactionWithReceipt } from "./transactionsHelpers"; +import { + updateTransaction, + updateTransactionWithReceipt, +} from "./transactionsHelpers"; import { handleTransactionResolved, handleTransactionEvent, @@ -32,8 +37,8 @@ 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. https://github.com/airswap/airswap-web/issues/891 const latestSuccessfulTransaction = useLatestSucceededTransaction(); + const latestExpiredTransaction = useLatestExpiredTransaction(); useTransactionsFilterFromLocalStorage(); // When the transactions change, we want to save them to local storage. @@ -110,6 +115,19 @@ export const useTransactions = (): void => { } }, [latestTransactionEvent]); + // If a transaction was not taken in time then it should be expired. + useEffect(() => { + if (latestExpiredTransaction) { + dispatch( + updateTransaction({ + ...latestExpiredTransaction, + status: TransactionStatusType.expired, + }) + ); + notifyOrderExpiry(); + } + }, [latestExpiredTransaction]); + // If a transaction is successful, we want to handle it here. useEffect(() => { if (latestSuccessfulTransaction) { diff --git a/src/features/transactions/transactionsSlice.ts b/src/features/transactions/transactionsSlice.ts index 21d6aa609..c42662cf2 100644 --- a/src/features/transactions/transactionsSlice.ts +++ b/src/features/transactions/transactionsSlice.ts @@ -1,20 +1,14 @@ -import { - createAsyncThunk, - createSelector, - createSlice, - PayloadAction, -} from "@reduxjs/toolkit"; +import { createSelector, createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { AppDispatch, RootState } from "../../app/store"; -import { ASSUMED_EXPIRY_NOTIFICATION_BUFFER_MS } from "../../constants/configParams"; +import { RootState } from "../../app/store"; import { - ProtocolType, SubmittedApprovalTransaction, SubmittedCancellation, SubmittedDepositTransaction, - SubmittedLastLookOrder, SubmittedTransaction, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; +import { isSubmittedOrder } from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; +import { compareAddresses } from "../../helpers/string"; import { ClearOrderType } from "../../types/clearOrderType"; import { TransactionStatusType, @@ -26,8 +20,6 @@ import { } from "../wallet/walletSlice"; import { declineTransaction, - expireTransaction, - mineTransaction, revertTransaction, submitTransaction, updateTransactions, @@ -53,15 +45,14 @@ function updateTransaction(params: { hash?: string; signerWallet?: string; status: TransactionStatusType; - protocol?: ProtocolType; }): void { const { state, nonce, hash, signerWallet, status } = params; if (!!signerWallet && !!nonce) { const swap = state.transactions.find( (s) => - s.nonce === nonce && - (s as SubmittedLastLookOrder).order.signerWallet.toLowerCase() === - signerWallet!.toLowerCase() + isSubmittedOrder(s) && + s.order.nonce === nonce && + compareAddresses(s.order.signerWallet, signerWallet) ); if (swap) { swap.timestamp = Date.now(); @@ -83,59 +74,6 @@ function updateTransaction(params: { } } -const expiryTimeouts: Record = {}; - -const clearExpiry = (signerWallet?: string, nonce?: string) => { - const uniqueKey = `${signerWallet}/${nonce}`; - - if (expiryTimeouts[uniqueKey]) { - clearTimeout(expiryTimeouts[uniqueKey]); - delete expiryTimeouts[uniqueKey]; - } -}; - -export const submitTransactionWithExpiry = createAsyncThunk< - // Return type of the payload creator - void, - // Params - { - transaction: SubmittedTransaction; - signerWallet: string; - onExpired?: () => void; - }, - // Types for ThunkAPI - { - dispatch: AppDispatch; - state: RootState; - } ->( - "orders/approve", - async ({ transaction, signerWallet, onExpired }, { getState, dispatch }) => { - dispatch(submitTransaction(transaction)); - if (!transaction.expiry) { - console.warn( - "submitTransactionWithExpiry called with transaction that has no expiry" - ); - return; - } - - const expiresAtMs = parseInt(transaction.expiry) * 1000; - const timeToExpiryNotification = - expiresAtMs - Date.now() + ASSUMED_EXPIRY_NOTIFICATION_BUFFER_MS; - const uniqueTransactionKey = `${signerWallet}/${transaction.nonce}`; - - expiryTimeouts[uniqueTransactionKey] = window.setTimeout(() => { - dispatch( - expireTransaction({ - signerWallet, - nonce: transaction.nonce!, - }) - ); - onExpired && onExpired(); - }, timeToExpiryNotification); - } -); - export const transactionsSlice = createSlice({ name: "transactions", initialState, @@ -161,18 +99,15 @@ export const transactionsSlice = createSlice({ state.transactions.unshift(action.payload); }); builder.addCase(declineTransaction, (state, action) => { - clearExpiry(action.payload.signerWallet, action.payload.nonce); updateTransaction({ state, hash: action.payload.hash, nonce: action.payload.nonce, signerWallet: action.payload.signerWallet, status: TransactionStatusType.declined, - protocol: action.payload.protocol, }); }); builder.addCase(revertTransaction, (state, action) => { - clearExpiry(action.payload.signerWallet, action.payload.nonce); updateTransaction({ state, signerWallet: action.payload.signerWallet, @@ -181,27 +116,6 @@ export const transactionsSlice = createSlice({ status: TransactionStatusType.reverted, }); }); - builder.addCase(expireTransaction, (state, action) => { - const { signerWallet, nonce } = action.payload; - clearExpiry(signerWallet, nonce); - updateTransaction({ - state, - signerWallet, - nonce, - status: TransactionStatusType.expired, - }); - }); - builder.addCase(mineTransaction, (state, action) => { - clearExpiry(action.payload.signerWallet, action.payload.nonce); - updateTransaction({ - state, - hash: action.payload.hash, - nonce: action.payload.nonce, - signerWallet: action.payload.signerWallet, - status: TransactionStatusType.succeeded, - protocol: action.payload.protocol, - }); - }); builder.addCase(updateTransactions, (state, action): TransactionsState => { return { ...state, diff --git a/src/features/transactions/transactionsUtils.ts b/src/features/transactions/transactionsUtils.ts index be0d4f9d6..e06b26529 100644 --- a/src/features/transactions/transactionsUtils.ts +++ b/src/features/transactions/transactionsUtils.ts @@ -1,30 +1,15 @@ import { BaseProvider, TransactionReceipt } from "@ethersproject/providers"; -import { AppDispatch } from "../../app/store"; import { SubmittedTransaction, - SubmittedTransactionWithOrder, + SubmittedOrder, } from "../../entities/SubmittedTransaction/SubmittedTransaction"; -import { - isApprovalTransaction, - isDepositTransaction, - isLastLookOrderTransaction, - isRfqOrderTransaction, - isWithdrawTransaction, -} from "../../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { parseJsonArray } from "../../helpers/array"; import { TransactionStatusType } from "../../types/transactionTypes"; -import { - handleApproveTransaction, - handleSubmittedDepositOrder, - handleSubmittedRFQOrder, - handleSubmittedWithdrawOrder, -} from "../orders/ordersActions"; -import { updateTransaction } from "./transactionsHelpers"; export const isTransactionWithOrder = ( transaction: SubmittedTransaction -): transaction is SubmittedTransactionWithOrder => { +): transaction is SubmittedOrder => { return "order" in transaction; }; @@ -96,10 +81,6 @@ export const getTransactionReceipt = async ( ): Promise => { let hash = transaction.hash; - if (isLastLookOrderTransaction(transaction)) { - hash = transaction.order.nonce; - } - if (!hash) { console.error("Transaction hash is not found"); diff --git a/src/features/wallet/Wallet.tsx b/src/features/wallet/Wallet.tsx index ea2b7278d..43829b1c4 100644 --- a/src/features/wallet/Wallet.tsx +++ b/src/features/wallet/Wallet.tsx @@ -43,6 +43,7 @@ import { selectActiveTokens, selectAllTokenInfo, selectMetaDataReducer, + selectProtocolFee, } from "../metadata/metadataSlice"; import { fetchSupportedTokens } from "../registry/registryActions"; import { @@ -81,8 +82,8 @@ export const Wallet: FC = ({ const { providerName } = useAppSelector(selectWallet); const transactions = useAppSelector(selectFilteredTransactions); const pendingTransactions = useAppSelector(selectPendingTransactions); + const protocolFee = useAppSelector(selectProtocolFee); const { isFetchingAllTokens } = useAppSelector(selectMetaDataReducer); - const allTokens = useAppSelector(selectAllTokenInfo); // Interface context const { transactionsTabIsOpen, setShowWalletList, setTransactionsTabIsOpen } = @@ -285,6 +286,7 @@ export const Wallet: FC = ({ address={account!} chainId={chainId!} open={transactionsTabIsOpen} + protocolFee={protocolFee} setTransactionsTabOpen={setTransactionsTabIsOpen} onClearTransactionsChange={handleClearTransactionsChange} onDisconnectWalletClicked={() => { @@ -296,7 +298,6 @@ export const Wallet: FC = ({ setTransactionsTabIsOpen(false); }} transactions={transactions} - tokens={allTokens} balances={balances!} isUnsupportedNetwork={error && error instanceof UnsupportedChainIdError} /> diff --git a/src/hooks/useOrderTransaction.ts b/src/hooks/useOrderTransaction.ts index f96743eed..698c19917 100644 --- a/src/hooks/useOrderTransaction.ts +++ b/src/hooks/useOrderTransaction.ts @@ -2,6 +2,7 @@ import { useMemo } from "react"; import { useAppSelector } from "../app/hooks"; import { SubmittedTransaction } from "../entities/SubmittedTransaction/SubmittedTransaction"; +import { isSubmittedOrder } from "../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { selectOrderTransactions } from "../features/transactions/transactionsSlice"; const useOrderTransaction = ( @@ -10,7 +11,10 @@ const useOrderTransaction = ( const transactions = useAppSelector(selectOrderTransactions); return useMemo(() => { - return transactions.find((transaction) => transaction.nonce === nonce); + return transactions.find( + (transaction) => + isSubmittedOrder(transaction) && transaction.order.nonce === nonce + ); }, [transactions, nonce]); }; diff --git a/src/hooks/useOrderTransactionLink.ts b/src/hooks/useOrderTransactionLink.ts index 7cb24bc0b..bd8afeb35 100644 --- a/src/hooks/useOrderTransactionLink.ts +++ b/src/hooks/useOrderTransactionLink.ts @@ -6,6 +6,7 @@ import { useWeb3React } from "@web3-react/core"; import { useAppSelector } from "../app/hooks"; import { SubmittedTransaction } from "../entities/SubmittedTransaction/SubmittedTransaction"; +import { isSubmittedOrder } from "../entities/SubmittedTransaction/SubmittedTransactionHelpers"; import { selectTransactions } from "../features/transactions/transactionsSlice"; import { TransactionStatusType } from "../types/transactionTypes"; @@ -17,7 +18,8 @@ const useOrderTransactionLink = (nonce: string): string | undefined => { return useMemo(() => { const succeededTransaction = transactions.find( (transaction) => - transaction.nonce === nonce && + isSubmittedOrder(transaction) && + transaction.order.nonce === nonce && transaction.status === TransactionStatusType.succeeded ); diff --git a/yarn.lock b/yarn.lock index 4447bc2b6..fe6be223e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11537,6 +11537,13 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +usehooks-ts@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/usehooks-ts/-/usehooks-ts-3.1.0.tgz#156119f36efc85f1b1952616c02580f140950eca" + integrity sha512-bBIa7yUyPhE1BCc0GmR96VU/15l/9gP1Ch5mYdLcFBaFGQsdmXkvjV0TtOqW1yUd6VjIwDunm+flSciCQXujiw== + dependencies: + lodash.debounce "^4.0.8" + utf-8-validate@^5.0.2: version "5.0.10" resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2"