From b5e0c635813cea5265ce733d49043adefec0ba06 Mon Sep 17 00:00:00 2001 From: piersss <86911296+piersss@users.noreply.github.com> Date: Thu, 25 Apr 2024 20:34:53 +0200 Subject: [PATCH] 899: Consolidated all balance and allowance dispatches in balancesHooks (#898) --- src/components/Routes/Routes.tsx | 2 + src/components/TokenList/TokenList.tsx | 10 +- src/features/balances/balancesApi.ts | 106 +---------------- src/features/balances/balancesHooks.ts | 67 +++++++++++ src/features/balances/balancesSlice.ts | 16 --- src/features/orders/ordersActions.ts | 58 +--------- .../transactions/transactionsHelpers.ts | 9 +- .../transactions/transactionsHooks.ts | 2 +- src/features/wallet/Wallet.tsx | 107 +----------------- 9 files changed, 82 insertions(+), 295 deletions(-) create mode 100644 src/features/balances/balancesHooks.ts diff --git a/src/components/Routes/Routes.tsx b/src/components/Routes/Routes.tsx index ea7db240..3f872978 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 useBalances from "../../features/balances/balancesHooks"; import { useTransactions } from "../../features/transactions/transactionsHooks"; import Cancel from "../../pages/Cancel/Cancel"; import MakePage from "../../pages/Make/Make"; @@ -10,6 +11,7 @@ import SwapPage from "../../pages/Swap/Swap"; import { AppRoutes } from "../../routes"; const Routes: FC = () => { + useBalances(); useTransactions(); return ( diff --git a/src/components/TokenList/TokenList.tsx b/src/components/TokenList/TokenList.tsx index 26d6d4a7..3f4f3aa3 100644 --- a/src/components/TokenList/TokenList.tsx +++ b/src/components/TokenList/TokenList.tsx @@ -8,12 +8,7 @@ import { useWeb3React } from "@web3-react/core"; import { useAppDispatch } from "../../app/hooks"; import nativeCurrency from "../../constants/nativeCurrency"; -import { - BalancesState, - requestActiveTokenAllowancesSwap, - requestActiveTokenAllowancesWrapper, - requestActiveTokenBalances, -} from "../../features/balances/balancesSlice"; +import { BalancesState } from "../../features/balances/balancesSlice"; import { addActiveToken, addCustomToken, @@ -164,9 +159,6 @@ const TokenList = ({ dispatch(addCustomToken(address)); } await dispatch(addActiveToken(address)); - dispatch(requestActiveTokenBalances({ provider: library })); - dispatch(requestActiveTokenAllowancesSwap({ provider: library })); - dispatch(requestActiveTokenAllowancesWrapper({ provider: library })); onAfterAddActiveToken && onAfterAddActiveToken(address); } diff --git a/src/features/balances/balancesApi.ts b/src/features/balances/balancesApi.ts index ff9096eb..89a0c60d 100644 --- a/src/features/balances/balancesApi.ts +++ b/src/features/balances/balancesApi.ts @@ -4,23 +4,6 @@ import erc20Abi from "erc-20-abi"; import { BigNumber, ethers, EventFilter, Event } from "ethers"; import { hexZeroPad, id } from "ethers/lib/utils"; -interface SubscribeParams { - activeTokenAddresses: string[]; - walletAddress: string; - spenderAddress: string; - provider: ethers.providers.Web3Provider; - onBalanceChange: ( - tokenAddress: string, - amount: BigNumber, - direction: "in" | "out" - ) => void; - onApproval: ( - tokenAddress: string, - spenderAddress: string, - amount: BigNumber - ) => void; -} - interface WalletParams { chainId: number; provider: ethers.providers.Web3Provider; @@ -28,8 +11,6 @@ interface WalletParams { tokenAddresses: string[]; } -const erc20Interface = new ethers.utils.Interface(erc20Abi); - /** * Fetches balances or allowances for a wallet using the airswap utility * contract `BalanceChecker.sol`. Balances are returned in base units. @@ -71,89 +52,4 @@ const fetchAllowancesWrapper = fetchBalancesOrAllowances.bind( "Wrapper" ); -// event Transfer(address indexed _from, address indexed _to, uint256 _value) -// event Approval(address indexed _owner, address indexed _spender, uint256 _value) -let subscribeToTransfersAndApprovals: (params: SubscribeParams) => () => void; -subscribeToTransfersAndApprovals = ({ - activeTokenAddresses, - walletAddress, - provider, - onBalanceChange, - spenderAddress, - onApproval, -}) => { - // event Transfer(address indexed _from, address indexed _to, uint256 _value) - const filters: { - in: EventFilter; - out: EventFilter; - } = { - // Tokens being transferred out of our account or approved by our account - out: { - topics: [ - [ - id("Transfer(address,address,uint256)"), - id("Approval(address,address,uint256)"), - ], // event name - hexZeroPad(walletAddress, 32), // from - ], - }, - - // Tokens being transferred in to our account - in: { - topics: [ - id("Transfer(address,address,uint256)"), // event name - [], - hexZeroPad(walletAddress, 32), // to - ], - }, - }; - - const tearDowns: (() => void)[] = []; - - Object.keys(filters).forEach((direction) => { - // in or out? - const typedDirection = direction as keyof typeof filters; - const filter = filters[typedDirection]; - - function listener(event: Event) { - const { address } = event; - const lowerCasedTokenAddress = address.toLowerCase(); - - // Ignore transactions for non-active tokens. - if (!activeTokenAddresses.includes(lowerCasedTokenAddress)) return; - - const parsedEvent = erc20Interface.parseLog(event); - const isApproval = parsedEvent.name === "Approval"; - - // Ignore approvals for other spenders. - const approvalAddress = parsedEvent.args[1].toLowerCase(); - const wrapperAddress = ( - Wrapper.getAddress(provider.network.chainId) || "" - ).toLowerCase(); - if ( - isApproval && - approvalAddress !== spenderAddress.toLowerCase() && - approvalAddress !== wrapperAddress - ) - return; - - const amount: BigNumber = parsedEvent.args[2]; - isApproval - ? onApproval(lowerCasedTokenAddress, approvalAddress, amount) - : onBalanceChange(lowerCasedTokenAddress, amount, typedDirection); - } - provider.on(filter, listener); - tearDowns.push(provider.off.bind(provider, filter, listener)); - }); - - return () => { - tearDowns.forEach((fn) => fn()); - }; -}; - -export { - fetchBalances, - fetchAllowancesSwap, - fetchAllowancesWrapper, - subscribeToTransfersAndApprovals, -}; +export { fetchBalances, fetchAllowancesSwap, fetchAllowancesWrapper }; diff --git a/src/features/balances/balancesHooks.ts b/src/features/balances/balancesHooks.ts new file mode 100644 index 00000000..3a8519a4 --- /dev/null +++ b/src/features/balances/balancesHooks.ts @@ -0,0 +1,67 @@ +import { useEffect, useState } from "react"; + +import { Web3Provider } from "@ethersproject/providers"; +import { useWeb3React } from "@web3-react/core"; + +import { useAppDispatch, useAppSelector } from "../../app/hooks"; +import { TransactionTypes } from "../../types/transactionTypes"; +import { fetchUnkownTokens } from "../metadata/metadataActions"; +import { selectActiveTokens } from "../metadata/metadataSlice"; +import useLatestSucceededTransaction from "../transactions/hooks/useLatestSucceededTransaction"; +import { + requestActiveTokenAllowancesSwap, + requestActiveTokenAllowancesWrapper, + requestActiveTokenBalances, +} from "./balancesSlice"; + +export const useBalances = () => { + const { library, account, chainId } = useWeb3React(); + const dispatch = useAppDispatch(); + + const activeTokens = useAppSelector(selectActiveTokens); + const latestSuccessfulTransaction = useLatestSucceededTransaction(); + + const [activeAccount, setActiveAccount] = useState(); + const [activeChainId, setActiveChainId] = useState(); + + useEffect(() => { + if (!library || !activeTokens.length || !account) { + return; + } + + if (activeAccount === account && activeChainId === chainId) { + return; + } + + setActiveAccount(account); + setActiveChainId(chainId); + + dispatch(requestActiveTokenBalances({ provider: library })); + dispatch(requestActiveTokenAllowancesSwap({ provider: library })); + dispatch(requestActiveTokenAllowancesWrapper({ provider: library })); + dispatch(fetchUnkownTokens({ provider: library })); + }, [account, chainId, library, activeTokens]); + + useEffect(() => { + if (!latestSuccessfulTransaction || !library) { + return; + } + + const { type } = latestSuccessfulTransaction; + + if ( + type === TransactionTypes.order || + type === TransactionTypes.withdraw || + type === TransactionTypes.deposit + ) { + dispatch(requestActiveTokenBalances({ provider: library })); + } + + if (type === TransactionTypes.approval) { + dispatch(requestActiveTokenAllowancesSwap({ provider: library })); + dispatch(requestActiveTokenAllowancesWrapper({ provider: library })); + } + }, [latestSuccessfulTransaction]); +}; + +export default useBalances; diff --git a/src/features/balances/balancesSlice.ts b/src/features/balances/balancesSlice.ts index e19b723e..63513f5b 100644 --- a/src/features/balances/balancesSlice.ts +++ b/src/features/balances/balancesSlice.ts @@ -232,22 +232,6 @@ export const allowancesWrapperSlice = getSlice( requestActiveTokenAllowancesWrapper ); -export const { - incrementBy: incrementBalanceBy, - decrementBy: decrementBalanceBy, - set: setBalance, -} = balancesSlice.actions; -export const { - incrementBy: incrementAllowanceSwapBy, - decrementBy: decrementAllowanceSwapBy, - set: setAllowanceSwap, -} = allowancesSwapSlice.actions; -export const { - incrementBy: incrementAllowanceWrapperBy, - decrementBy: decreementAllowanceWrapperBy, - set: setAllowanceWrapper, -} = allowancesWrapperSlice.actions; - export const balancesActions = balancesSlice.actions; export const allowancesSwapActions = allowancesSwapSlice.actions; export const allowancesWrapperActions = allowancesWrapperSlice.actions; diff --git a/src/features/orders/ordersActions.ts b/src/features/orders/ordersActions.ts index 3877527f..e50fb0f0 100644 --- a/src/features/orders/ordersActions.ts +++ b/src/features/orders/ordersActions.ts @@ -42,11 +42,6 @@ import getWethAddress from "../../helpers/getWethAddress"; import toRoundedAtomicString from "../../helpers/toRoundedAtomicString"; import i18n from "../../i18n/i18n"; import { TransactionStatusType } from "../../types/transactionTypes"; -import { - allowancesSwapActions, - decrementBalanceBy, - incrementBalanceBy, -} from "../balances/balancesSlice"; import { declineTransaction, revertTransaction, @@ -69,8 +64,7 @@ import { export const handleApproveTransaction = ( transaction: SubmittedApprovalTransaction, - status: TransactionStatusType, - dispatch: AppDispatch + status: TransactionStatusType ): void => { if (status === TransactionStatusType.failed) { notifyError({ @@ -81,25 +75,12 @@ export const handleApproveTransaction = ( return; } - const amount = toRoundedAtomicString( - transaction.amount, - transaction.token.decimals - ); - - dispatch( - allowancesSwapActions.set({ - tokenAddress: transaction.tokenAddress, - amount: amount, - }) - ); - notifyApproval(transaction); }; export const handleSubmittedDepositOrder = ( transaction: SubmittedDepositTransaction, - status: TransactionStatusType, - dispatch: AppDispatch + status: TransactionStatusType ): void => { if (status === TransactionStatusType.failed) { notifyError({ @@ -110,29 +91,12 @@ export const handleSubmittedDepositOrder = ( return; } - // TODO: Balance handling should be done in balancesApi.ts https://github.com/airswap/airswap-web/issues/889 - - dispatch( - incrementBalanceBy({ - tokenAddress: transaction.signerToken.address, - amount: transaction.order.signerAmount, - }) - ); - - dispatch( - decrementBalanceBy({ - tokenAddress: transaction.senderToken.address, - amount: transaction.order.senderAmount, - }) - ); - notifyDeposit(transaction); }; export const handleSubmittedWithdrawOrder = ( transaction: SubmittedWithdrawTransaction, - status: TransactionStatusType, - dispatch: AppDispatch + status: TransactionStatusType ): void => { if (status === TransactionStatusType.failed) { notifyError({ @@ -143,22 +107,6 @@ export const handleSubmittedWithdrawOrder = ( return; } - // TODO: Balance handling should be done in balancesApi.ts https://github.com/airswap/airswap-web/issues/889 - - dispatch( - incrementBalanceBy({ - tokenAddress: transaction.signerToken.address, - amount: transaction.order.signerAmount, - }) - ); - - dispatch( - decrementBalanceBy({ - tokenAddress: transaction.senderToken.address, - amount: transaction.order.senderAmount, - }) - ); - notifyWithdrawal(transaction); }; diff --git a/src/features/transactions/transactionsHelpers.ts b/src/features/transactions/transactionsHelpers.ts index e34b6ba7..836c7e98 100644 --- a/src/features/transactions/transactionsHelpers.ts +++ b/src/features/transactions/transactionsHelpers.ts @@ -147,18 +147,17 @@ export const handleTransactionEvent = }; export const handleTransactionResolved = - (transaction: SubmittedTransaction) => - (dispatch: AppDispatch): void => { + (transaction: SubmittedTransaction) => (): void => { if (isApprovalTransaction(transaction)) { - handleApproveTransaction(transaction, transaction.status, dispatch); + handleApproveTransaction(transaction, transaction.status); } if (isDepositTransaction(transaction)) { - handleSubmittedDepositOrder(transaction, transaction.status, dispatch); + handleSubmittedDepositOrder(transaction, transaction.status); } if (isWithdrawTransaction(transaction)) { - handleSubmittedWithdrawOrder(transaction, transaction.status, dispatch); + handleSubmittedWithdrawOrder(transaction, transaction.status); } if (isSubmittedOrder(transaction)) { diff --git a/src/features/transactions/transactionsHooks.ts b/src/features/transactions/transactionsHooks.ts index a439eb65..b5ab616c 100644 --- a/src/features/transactions/transactionsHooks.ts +++ b/src/features/transactions/transactionsHooks.ts @@ -131,7 +131,7 @@ export const useTransactions = (): void => { // If a transaction is successful, we want to handle it here. useEffect(() => { if (latestSuccessfulTransaction) { - dispatch(handleTransactionResolved(latestSuccessfulTransaction)); + handleTransactionResolved(latestSuccessfulTransaction); } }, [latestSuccessfulTransaction]); }; diff --git a/src/features/wallet/Wallet.tsx b/src/features/wallet/Wallet.tsx index 5d8cff52..55fdfa97 100644 --- a/src/features/wallet/Wallet.tsx +++ b/src/features/wallet/Wallet.tsx @@ -1,7 +1,6 @@ import React, { FC, useContext, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { SwapERC20, WETH, Wrapper } from "@airswap/libraries"; import { Web3Provider } from "@ethersproject/providers"; import { UnsupportedChainIdError, useWeb3React } from "@web3-react/core"; import { WalletConnectConnector } from "@web3-react/walletconnect-connector"; @@ -23,25 +22,9 @@ import { TopBar, } from "../../styled-components/TopBar/Topbar"; import { ClearOrderType } from "../../types/clearOrderType"; -import { subscribeToTransfersAndApprovals } from "../balances/balancesApi"; +import { selectBalances } from "../balances/balancesSlice"; +import { fetchAllTokens, fetchProtocolFee } from "../metadata/metadataActions"; import { - decrementBalanceBy, - incrementBalanceBy, - requestActiveTokenAllowancesSwap, - requestActiveTokenAllowancesWrapper, - requestActiveTokenBalances, - selectBalances, - setAllowanceSwap, - setAllowanceWrapper, -} from "../balances/balancesSlice"; -import { - fetchAllTokens, - fetchProtocolFee, - fetchUnkownTokens, -} from "../metadata/metadataActions"; -import { - selectActiveTokens, - selectAllTokenInfo, selectMetaDataReducer, selectProtocolFee, } from "../metadata/metadataSlice"; @@ -77,7 +60,6 @@ export const Wallet: FC = ({ // Redux const dispatch = useAppDispatch(); - const activeTokens = useAppSelector(selectActiveTokens); const balances = useAppSelector(selectBalances); const { providerName } = useAppSelector(selectWallet); const transactions = useAppSelector(selectFilteredTransactions); @@ -153,96 +135,13 @@ export const Wallet: FC = ({ provider: library, } as any) ), - ]).then(() => { - dispatch( - requestActiveTokenBalances({ - provider: library, - }) - ); - dispatch( - requestActiveTokenAllowancesSwap({ - provider: library, - }) - ); - dispatch( - requestActiveTokenAllowancesWrapper({ - provider: library, - }) - ); - dispatch( - fetchUnkownTokens({ - provider: library, - } as any) - ); - }); + ]); } else if (!active) { dispatch(setWalletDisconnected()); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [account, chainId, active]); - // Subscribe to changes in balance - - useEffect(() => { - if ( - !library || - !account || - !connector || - chainId === undefined || - !activeTokens.length || - balances.lastFetch === null || - balances.status !== "idle" - ) - return; - - let teardownTransferListener: () => void; - if (activeTokens.length) { - teardownTransferListener = subscribeToTransfersAndApprovals({ - activeTokenAddresses: activeTokens.map((t) => t.address), - provider: library, - walletAddress: account, - spenderAddress: SwapERC20.getAddress(chainId) || "", - onBalanceChange: (tokenAddress, amount, direction) => { - const actionCreator = - direction === "in" ? incrementBalanceBy : decrementBalanceBy; - dispatch( - actionCreator({ - tokenAddress, - amount: amount.toString(), - }) - ); - }, - onApproval: (tokenAddress, spenderAddress, amount) => { - const actionCreator = - spenderAddress === Wrapper.getAddress(chainId) - ? setAllowanceWrapper - : setAllowanceSwap; - dispatch( - actionCreator({ - tokenAddress, - amount: amount.toString(), - }) - ); - }, - }); - } - - return () => { - if (teardownTransferListener) { - teardownTransferListener(); - } - }; - }, [ - activeTokens, - account, - library, - connector, - dispatch, - chainId, - balances.lastFetch, - balances.status, - ]); - return ( <>