Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 118 additions & 10 deletions src/apps/the-exchange/components/ExchangeAction/ExchangeAction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ import { formatEther } from 'viem';
import {
Token,
chainNameToChainIdTokensData,
convertPortfolioAPIResponseToToken,
} from '../../../../services/tokensData';

// hooks
import useBottomMenuModal from '../../../../hooks/useBottomMenuModal';
import useGlobalTransactionsBatch from '../../../../hooks/useGlobalTransactionsBatch';
import { useTransactionDebugLogger } from '../../../../hooks/useTransactionDebugLogger';
import useOffer from '../../hooks/useOffer';
import useOffer, { getNativeBalanceFromPortfolio } from '../../hooks/useOffer';
import { useAppSelector } from '../../hooks/useReducerHooks';

// types
import { PortfolioData } from '../../../../types/api';
import { SwapOffer } from '../../utils/types';

// utils
Expand All @@ -33,6 +35,12 @@ import NumberText from '../Typography/NumberText';

// images
import ArrowRight from '../../images/arrow-right.png';
import {
getFeeAmount,
getFeeSymbol,
isERC20FeeTx,
isNativeFeeTx,
} from '../../utils/blockchain';

const ExchangeAction = () => {
const bestOffer = useAppSelector(
Expand All @@ -49,18 +57,96 @@ const ExchangeAction = () => {

const [errorMessage, setErrorMessage] = useState<string>('');
const [isAddingToBatch, setIsAddingToBatch] = useState<boolean>(false);
const [feeInfo, setFeeInfo] = useState<{
amount: string;
symbol: string;
recipient: string;
warning?: string;
} | null>(null);
const { addToBatch } = useGlobalTransactionsBatch();
const { showSend, setShowBatchSendModal } = useBottomMenuModal();
const { getStepTransactions } = useOffer();
const walletAddress = useWalletAddress();
const { transactionDebugLog } = useTransactionDebugLogger();
const walletPortfolio = useAppSelector(
(state) => state.swap.walletPortfolio as PortfolioData | undefined
);

useEffect(() => {
const feeReceiver = import.meta.env.VITE_SWAP_FEE_RECEIVER;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you let me know what this value should be please 🙏


const isNoValidOffer =
!bestOffer ||
!bestOffer.tokenAmountToReceive ||
Number(bestOffer.tokenAmountToReceive) === 0;

const fetchFeeInfo = async () => {
setFeeInfo(null);
setErrorMessage('');
if (!bestOffer || !swapToken || !walletAddress) return;
try {
// Extract cached native balance for the relevant chain
const nativeBalance = getNativeBalanceFromPortfolio(
walletPortfolio
? convertPortfolioAPIResponseToToken(walletPortfolio)
: undefined,
bestOffer.offer.fromChainId
);
// Only get the fee transaction, not the whole batch
const stepTxs = await getStepTransactions(
swapToken,
bestOffer.offer,
walletAddress,
nativeBalance
);
transactionDebugLog(
'Step transactions:',
stepTxs,
'Fee receiver:',
feeReceiver
);
// Find the fee transfer (to feeReceiver for native, or ERC20 transfer for stablecoin)
const feeTx = stepTxs.find(
(tx) => isNativeFeeTx(tx, feeReceiver) || isERC20FeeTx(tx, swapToken)
);
if (feeTx) {
const amount = getFeeAmount(feeTx, swapToken, swapToken.decimals);
const symbol = getFeeSymbol(
feeTx,
swapToken,
bestOffer.offer.fromChainId
);
setFeeInfo({
amount,
symbol,
recipient: String(feeReceiver),
warning: undefined,
});
} else {
transactionDebugLog(
'No fee transaction found in stepTxs for feeReceiver:',
feeReceiver
);
setFeeInfo({
amount: '0',
symbol: swapToken.symbol,
recipient: String(feeReceiver),
warning:
'Unable to prepare the swap. Please check your wallet, refresh the page and try again.',
});
}
} catch (e) {
setFeeInfo(null);
transactionDebugLog('Fee estimation error:', e);
setErrorMessage(
'Unable to prepare the swap. Please check your wallet, refresh the page and try again.'
);
}
};

transactionDebugLog('The Exchange - Offer:', bestOffer);
useEffect(() => {
fetchFeeInfo();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [bestOffer]);
}, [bestOffer, swapToken, walletAddress]);

const getTransactionTitle = (
index: number,
Expand All @@ -85,7 +171,7 @@ const ExchangeAction = () => {
return;
}

if (!bestOffer) {
if (isNoValidOffer) {
setErrorMessage(
'No offer was found! Please try changing the amounts to try again.'
);
Expand All @@ -95,10 +181,18 @@ const ExchangeAction = () => {
try {
setIsAddingToBatch(true);

// Extract cached native balance for the relevant chain
const nativeBalance = getNativeBalanceFromPortfolio(
walletPortfolio
? convertPortfolioAPIResponseToToken(walletPortfolio)
: undefined,
bestOffer.offer.fromChainId
);
const stepTransactions = await getStepTransactions(
swapToken,
bestOffer.offer,
walletAddress as `0x${string}`
walletAddress as `0x${string}`,
nativeBalance
);

transactionDebugLog(
Expand Down Expand Up @@ -146,7 +240,7 @@ const ExchangeAction = () => {
}
setIsAddingToBatch(false);
} catch (error) {
console.error('Something went wrong. Please try again', error);
transactionDebugLog('Swap batch error:', error);
setErrorMessage(
'We were not able to add this to the queue at the moment. Please try again.'
);
Expand All @@ -160,15 +254,17 @@ const ExchangeAction = () => {
className="flex flex-col w-full tablet:max-w-[420px] desktop:max-w-[420px] mb-20"
>
<div
className={`flex flex-col gap-4 rounded-t-[3px] p-4 border-b border-black_grey ${bestOffer?.tokenAmountToReceive && !isOfferLoading ? 'bg-white' : 'bg-white/[.6]'}`}
className={`flex flex-col gap-4 rounded-t-[3px] p-4 border-b border-black_grey ${!isNoValidOffer && !isOfferLoading ? 'bg-white' : 'bg-white/[.6]'}`}
>
<Body className="font-normal">You receive</Body>
<div className="flex justify-between items-end">
{isOfferLoading ? (
<CircularProgress size={64.5} sx={{ color: '#343434' }} />
) : (
<NumberText className="font-normal text-[43px]">
{formatTokenAmount(bestOffer?.tokenAmountToReceive)}
{isNoValidOffer
? '0'
: formatTokenAmount(bestOffer?.tokenAmountToReceive)}
</NumberText>
)}
<div className="flex gap-1 items-center">
Expand All @@ -180,11 +276,23 @@ const ExchangeAction = () => {
<Body className="font-normal">{receiveToken?.symbol ?? ''}</Body>
</div>
</div>
{!isNoValidOffer && feeInfo && (
<div className="mt-2 p-2 bg-gray-50 rounded border border-gray-200">
<BodySmall className="font-normal text-black_grey">
Fee: {feeInfo.amount} {feeInfo.symbol}
</BodySmall>
{feeInfo.warning && (
<BodySmall className="text-orange-600 font-normal mt-1">
{feeInfo.warning}
</BodySmall>
)}
</div>
)}
</div>
<div
id="exchange-action-button"
onClick={onClickToExchange}
className={`flex gap-4 rounded-b-[3px] p-4 gap-2 items-center ${bestOffer?.tokenAmountToReceive && !isOfferLoading ? 'bg-white cursor-pointer' : 'bg-white/[.6]'}`}
className={`flex gap-4 rounded-b-[3px] p-4 gap-2 items-center ${!isNoValidOffer && !isOfferLoading ? 'bg-white cursor-pointer' : 'bg-white/[.6]'}`}
>
<Body>Exchange</Body>
{errorMessage && <BodySmall>{errorMessage}</BodySmall>}
Expand Down
Loading