diff --git a/src/components/trade/tradeWidget/useBuySell.ts b/src/components/trade/tradeWidget/useBuySell.ts index f5e5a8a4d..becfd6aac 100644 --- a/src/components/trade/tradeWidget/useBuySell.ts +++ b/src/components/trade/tradeWidget/useBuySell.ts @@ -39,7 +39,6 @@ export const useBuySell = ({ const [isLiquidityError, setIsLiquidityError] = useState(false); const [isSourceEmptyError, setIsSourceEmptyError] = useState(false); const [isTargetEmptyError, setIsTargetEmptyError] = useState(false); - const [isAwaiting, setIsAwaiting] = useState(false); const { calcMaxInput } = useTradeAction({ source, @@ -75,12 +74,11 @@ export const useBuySell = ({ target, ]); - const { trade, approval } = useTradeAction({ + const { trade, isAwaiting, approval } = useTradeAction({ source, sourceInput, isTradeBySource, onSuccess: (txHash: string) => { - setIsAwaiting(false); clearInputs(); buy ? carbonEvents.trade.tradeBuy({ @@ -236,14 +234,12 @@ export const useBuySell = ({ isTradeBySource, sourceInput: sourceInput, targetInput: targetInput, - setIsAwaiting, }); if (approval.approvalRequired) { openModal('txConfirm', { approvalTokens: approval.tokens, onConfirm: () => { - setIsAwaiting(true); tradeFn(); }, buttonLabel: 'Confirm Trade', @@ -256,7 +252,6 @@ export const useBuySell = ({ context: 'trade', }); } else { - setIsAwaiting(true); void tradeFn(); } }, [ diff --git a/src/components/trade/tradeWidget/useTradeAction.ts b/src/components/trade/tradeWidget/useTradeAction.ts index 64f65547c..a418543a9 100644 --- a/src/components/trade/tradeWidget/useTradeAction.ts +++ b/src/components/trade/tradeWidget/useTradeAction.ts @@ -1,10 +1,9 @@ import { useWagmi } from 'libs/wagmi'; -import { Dispatch, SetStateAction, useCallback, useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import config from 'config'; -import { PopulatedTransaction } from 'ethers'; -import { TradeActionBNStr, carbonSDK } from 'libs/sdk'; +import { TradeActionBNStr } from 'libs/sdk'; import { SafeDecimal } from 'libs/safedecimal'; -import { QueryKey, useQueryClient } from 'libs/queries'; +import { QueryKey, useQueryClient, useTradeQuery } from 'libs/queries'; import { useNotifications } from 'hooks/useNotifications'; import { useStore } from 'store'; import { Token } from 'libs/tokens'; @@ -17,7 +16,6 @@ type TradeProps = { isTradeBySource: boolean; sourceInput: string; targetInput: string; - setIsAwaiting: Dispatch>; }; type Props = Pick & { @@ -60,79 +58,64 @@ export const useTradeAction = ({ return deadlineInMs.plus(Date.now()).toString(); }, []); - const trade = useCallback( - async ({ - source, - target, - isTradeBySource, - sourceInput, - targetInput, - tradeActions, - setIsAwaiting, - }: TradeProps) => { - if (!user || !signer) { - throw new Error('No user or signer'); - } - try { - let unsignedTx: PopulatedTransaction; - if (isTradeBySource) { - unsignedTx = await carbonSDK.composeTradeBySourceTransaction( - source.address, - target.address, - tradeActions, - calcDeadline(deadline), - calcMinReturn(targetInput) - ); - } else { - unsignedTx = await carbonSDK.composeTradeByTargetTransaction( - source.address, - target.address, - tradeActions, - calcDeadline(deadline), - calcMaxInput(sourceInput) - ); - } + const mutation = useTradeQuery(); + + const trade = async ({ + source, + target, + isTradeBySource, + sourceInput, + targetInput, + tradeActions, + }: TradeProps) => { + if (!user || !signer) { + throw new Error('No user or signer'); + } - const tx = await signer.sendTransaction(unsignedTx); - dispatchNotification('trade', { - txHash: tx.hash, - amount: sourceInput, - from: source.symbol, - to: target.symbol, - }); + return mutation.mutate( + { + sourceAddress: source.address, + targetAddress: target.address, + isTradeBySource, + tradeActions, + sourceInput, + targetInput, + deadline, + calcDeadline, + calcMaxInput, + calcMinReturn, + }, + { + onSuccess: async (tx) => { + dispatchNotification('trade', { + txHash: tx.hash, + amount: sourceInput, + from: source.symbol, + to: target.symbol, + }); - await tx.wait(); - onSuccess?.(tx.hash); - void cache.invalidateQueries({ - queryKey: QueryKey.balance(user, source.address), - }); - void cache.invalidateQueries({ - queryKey: QueryKey.balance(user, target.address), - }); - void cache.invalidateQueries({ - queryKey: QueryKey.approval( - user, - source.address, - config.addresses.carbon.carbonController - ), - }); - } catch (error) { - console.error(error); - setIsAwaiting(false); + await tx.wait(); + onSuccess?.(tx.hash); + cache.invalidateQueries({ + queryKey: QueryKey.balance(user, source.address), + }); + cache.invalidateQueries({ + queryKey: QueryKey.balance(user, target.address), + }); + cache.invalidateQueries({ + queryKey: QueryKey.approval( + user, + source.address, + config.addresses.carbon.carbonController + ), + }); + }, + onError: (e: any) => { + console.error(e); + }, } - }, - [ - cache, - calcDeadline, - calcMaxInput, - calcMinReturn, - deadline, - dispatchNotification, - onSuccess, - signer, - user, - ] - ); + ); + }; const approvalTokens = useMemo( () => [ @@ -153,5 +136,6 @@ export const useTradeAction = ({ calcMinReturn, calcDeadline, approval, + isAwaiting: mutation.isPending, }; }; diff --git a/src/libs/modals/modals/ModalTradeRouting/ModalTradeRouting.tsx b/src/libs/modals/modals/ModalTradeRouting/ModalTradeRouting.tsx index 2773c5619..26045cb6c 100644 --- a/src/libs/modals/modals/ModalTradeRouting/ModalTradeRouting.tsx +++ b/src/libs/modals/modals/ModalTradeRouting/ModalTradeRouting.tsx @@ -1,4 +1,4 @@ -import { FormEvent, KeyboardEvent, useId, useRef, useState } from 'react'; +import { FormEvent, KeyboardEvent, useId, useRef } from 'react'; import { ModalFC } from '../../modals.types'; import { Action } from 'libs/sdk'; import { Token } from 'libs/tokens'; @@ -30,7 +30,6 @@ export const ModalTradeRouting: ModalFC = ({ }) => { const sourceInputId = useId(); const table = useRef(null); - const [isAwaiting, setIsAwaiting] = useState(false); const { source, target } = data; const { selected, @@ -43,9 +42,10 @@ export const ModalTradeRouting: ModalFC = ({ disabledCTA, buttonText, errorMsg, + isAwaiting, } = useModalTradeRouting({ id, - data: { ...data, setIsAwaiting }, + data, }); const selectedSorted = selected.sort((a, b) => { diff --git a/src/libs/modals/modals/ModalTradeRouting/useModalTradeRouting.ts b/src/libs/modals/modals/ModalTradeRouting/useModalTradeRouting.ts index 0ebb0d506..126b4a4c4 100644 --- a/src/libs/modals/modals/ModalTradeRouting/useModalTradeRouting.ts +++ b/src/libs/modals/modals/ModalTradeRouting/useModalTradeRouting.ts @@ -1,10 +1,4 @@ -import { - Dispatch, - SetStateAction, - useCallback, - useMemo, - useState, -} from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { Action } from '@bancor/carbon-sdk'; import { useWagmi } from 'libs/wagmi'; import { ModalTradeRoutingData } from 'libs/modals/modals/ModalTradeRouting/ModalTradeRouting'; @@ -16,9 +10,7 @@ import { SafeDecimal } from 'libs/safedecimal'; type Props = { id: string; - data: ModalTradeRoutingData & { - setIsAwaiting: Dispatch>; - }; + data: ModalTradeRoutingData; }; export const useModalTradeRouting = ({ @@ -32,7 +24,6 @@ export const useModalTradeRouting = ({ onSuccess, buy = false, sourceBalance, - setIsAwaiting, }, }: Props) => { const { user, provider } = useWagmi(); @@ -65,14 +56,13 @@ export const useModalTradeRouting = ({ }); const sourceInput = data?.totalSourceAmount || '0'; - const { trade, calcMaxInput, approval } = useTradeAction({ + const { trade, calcMaxInput, isAwaiting, approval } = useTradeAction({ source, isTradeBySource, sourceInput, onSuccess: () => { onSuccess(); closeModal(id); - setIsAwaiting(false); }, }); @@ -93,14 +83,12 @@ export const useModalTradeRouting = ({ isTradeBySource, sourceInput, targetInput: data.totalTargetAmount, - setIsAwaiting, }); if (approval.approvalRequired) { openModal('txConfirm', { approvalTokens: approval.tokens, onConfirm: () => { - setIsAwaiting(true); tradeFn(); }, buttonLabel: 'Confirm Trade', @@ -118,7 +106,6 @@ export const useModalTradeRouting = ({ }, }); } else { - setIsAwaiting(true); void tradeFn(); } }, [ @@ -136,7 +123,6 @@ export const useModalTradeRouting = ({ data?.totalSourceAmount, data?.totalTargetAmount, isTradeBySource, - setIsAwaiting, buy, getFiatValueSource, provider?.network?.name, @@ -181,5 +167,6 @@ export const useModalTradeRouting = ({ disabledCTA, buttonText, errorMsg, + isAwaiting, }; }; diff --git a/src/libs/queries/sdk/trade.ts b/src/libs/queries/sdk/trade.ts index ea3900f69..fac124a91 100644 --- a/src/libs/queries/sdk/trade.ts +++ b/src/libs/queries/sdk/trade.ts @@ -1,10 +1,11 @@ -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { QueryKey } from 'libs/queries'; import { SafeDecimal } from 'libs/safedecimal'; import { useCarbonInit } from 'hooks/useCarbonInit'; import { Action, TradeActionBNStr } from 'libs/sdk'; -import { MatchActionBNStr } from '@bancor/carbon-sdk'; +import { MatchActionBNStr, PopulatedTransaction } from '@bancor/carbon-sdk'; import { carbonSDK } from 'libs/sdk'; +import { useWagmi } from 'libs/wagmi'; type GetTradeDataResult = { tradeActions: TradeActionBNStr[]; @@ -23,6 +24,48 @@ type Props = { enabled?: boolean; }; +export interface TradeParams { + sourceAddress: string; + targetAddress: string; + tradeActions: TradeActionBNStr[]; + isTradeBySource: boolean; + sourceInput: string; + targetInput: string; + deadline: string; + calcDeadline: (value: string) => string; + calcMaxInput: (amount: string) => string; + calcMinReturn: (amount: string) => string; +} + +export const useTradeQuery = () => { + const { signer } = useWagmi(); + + return useMutation({ + mutationFn: async (params: TradeParams) => { + const { calcDeadline, calcMinReturn, calcMaxInput } = params; + let unsignedTx: PopulatedTransaction; + if (params.isTradeBySource) { + unsignedTx = await carbonSDK.composeTradeBySourceTransaction( + params.sourceAddress, + params.targetAddress, + params.tradeActions, + calcDeadline(params.deadline), + calcMinReturn(params.targetInput) + ); + } else { + unsignedTx = await carbonSDK.composeTradeByTargetTransaction( + params.sourceAddress, + params.targetAddress, + params.tradeActions, + calcDeadline(params.deadline), + calcMaxInput(params.sourceInput) + ); + } + return signer!.sendTransaction(unsignedTx); + }, + }); +}; + export const useGetTradeData = ({ isTradeBySource, input,