Skip to content

Commit 6fb8a0a

Browse files
authored
Swaps flow improvements (#2527)
1 parent 15e258d commit 6fb8a0a

21 files changed

+786
-192
lines changed

src/components/infoTooltips/DarkTooltip.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
import { Box, Tooltip, TooltipProps } from '@mui/material';
22

3-
export const DarkTooltip = ({ title, children, wrap }: TooltipProps & { wrap?: boolean }) => {
3+
export const DarkTooltip = ({
4+
title,
5+
children,
6+
wrap,
7+
enterTouchDelay,
8+
leaveTouchDelay,
9+
}: TooltipProps & { wrap?: boolean; enterTouchDelay?: number; leaveTouchDelay?: number }) => {
410
return (
511
<div>
612
<Tooltip
713
placement="top"
14+
enterTouchDelay={enterTouchDelay}
15+
leaveTouchDelay={leaveTouchDelay}
816
componentsProps={{
917
tooltip: {
1018
sx: {

src/components/transactions/Switch/BaseSwitchModalContent.tsx

Lines changed: 120 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import React, { useEffect, useMemo, useState } from 'react';
99
import { Link } from 'src/components/primitives/Link';
1010
import { Warning } from 'src/components/primitives/Warning';
1111
import { ConnectWalletButton } from 'src/components/WalletConnection/ConnectWalletButton';
12-
import { isSmartContractWallet } from 'src/helpers/provider';
12+
import { isSafeWallet, isSmartContractWallet } from 'src/helpers/provider';
1313
import { TokenInfoWithBalance, useTokensBalance } from 'src/hooks/generic/useTokensBalance';
1414
import { useMultiProviderSwitchRates } from 'src/hooks/switch/useMultiProviderSwitchRates';
1515
import { useSwitchProvider } from 'src/hooks/switch/useSwitchProvider';
@@ -55,6 +55,9 @@ export type SwitchDetailsParams = Parameters<
5555
>[0];
5656

5757
const valueLostPercentage = (destValueInUsd: number, srcValueInUsd: number) => {
58+
if (destValueInUsd === 0) return 1;
59+
if (srcValueInUsd === 0) return 0;
60+
5861
const receivingPercentage = destValueInUsd / srcValueInUsd;
5962
const valueLostPercentage = receivingPercentage ? 1 - receivingPercentage : 0;
6063
return valueLostPercentage;
@@ -158,8 +161,10 @@ export const BaseSwitchModalContent = ({
158161
return forcedChainId;
159162
return defaultNetwork.chainId;
160163
});
164+
const trackEvent = useRootStore((store) => store.trackEvent);
165+
const [showUSDTResetWarning, setShowUSDTResetWarning] = useState(false);
161166
const switchProvider = useSwitchProvider({ chainId: selectedChainId });
162-
const [slippage, setSlippage] = useState(switchProvider == 'cowprotocol' ? '2' : '0.10');
167+
const [slippage, setSlippage] = useState(switchProvider == 'cowprotocol' ? '0.5' : '0.10');
163168
const [showGasStation, setShowGasStation] = useState(switchProvider == 'paraswap');
164169
const [highPriceImpactConfirmed, setHighPriceImpactConfirmed] = useState(false);
165170
const selectedNetworkConfig = getNetworkConfig(selectedChainId);
@@ -173,13 +178,17 @@ export const BaseSwitchModalContent = ({
173178
);
174179

175180
const [userIsSmartContractWallet, setUserIsSmartContractWallet] = useState(false);
181+
const [userIsSafeWallet, setUserIsSafeWallet] = useState(false);
176182
useEffect(() => {
177183
try {
178184
if (user && connectedChainId) {
179185
getEthersProvider(wagmiConfig, { chainId: connectedChainId }).then((provider) => {
180-
isSmartContractWallet(user, provider).then((isSmartContractWallet) => {
181-
setUserIsSmartContractWallet(isSmartContractWallet);
182-
});
186+
Promise.all([isSmartContractWallet(user, provider), isSafeWallet(user, provider)]).then(
187+
([isSmartContract, isSafe]) => {
188+
setUserIsSmartContractWallet(isSmartContract);
189+
setUserIsSafeWallet(isSafe);
190+
}
191+
);
183192
});
184193
}
185194
} catch (error) {
@@ -326,6 +335,7 @@ export const BaseSwitchModalContent = ({
326335
isNativeToken(selectedInputToken?.address),
327336
switchProvider
328337
);
338+
329339
const safeSlippage =
330340
slippageValidation && slippageValidation.severity === ValidationSeverity.ERROR
331341
? 0
@@ -357,6 +367,52 @@ export const BaseSwitchModalContent = ({
357367
isTxSuccess: switchTxState.success,
358368
});
359369

370+
useEffect(() => {
371+
if (ratesError) {
372+
console.error('tracking error', ratesError);
373+
trackEvent('Swap Error', {
374+
'Error Message': ratesError.message,
375+
'Error Name': ratesError.name,
376+
'Error Stack': ratesError.stack,
377+
'Input Token': selectedInputToken.symbol,
378+
'Output Token': selectedOutputToken.symbol,
379+
'Input Amount': debounceInputAmount,
380+
'Output Amount': normalizeBN(
381+
switchRates?.provider === 'cowprotocol'
382+
? switchRates?.destSpot
383+
: switchRates?.destAmount || 0,
384+
switchRates?.destDecimals || 18
385+
).toString(),
386+
'Input Amount USD': switchRates?.srcUSD,
387+
'Output Amount USD': switchRates?.destUSD,
388+
Slippage: safeSlippage,
389+
});
390+
}
391+
}, [ratesError]);
392+
393+
useEffect(() => {
394+
if (txError && txError.actionBlocked) {
395+
console.error('tracking error', txError);
396+
trackEvent('Swap Tx Error', {
397+
'Error Message': txError.error?.toString(),
398+
'Error Raw': txError.rawError?.toString(),
399+
'Error Action': txError.txAction,
400+
'Input Token': selectedInputToken.symbol,
401+
'Output Token': selectedOutputToken.symbol,
402+
'Input Amount': debounceInputAmount,
403+
'Output Amount': normalizeBN(
404+
switchRates?.provider === 'cowprotocol'
405+
? switchRates?.destSpot
406+
: switchRates?.destAmount || 0,
407+
switchRates?.destDecimals || 18
408+
).toString(),
409+
'Input Amount USD': switchRates?.srcUSD,
410+
'Output Amount USD': switchRates?.destUSD,
411+
Slippage: safeSlippage,
412+
});
413+
}
414+
}, [txError]);
415+
360416
// Define default slippage for CoW
361417
useEffect(() => {
362418
if (switchProvider == 'cowprotocol' && isCowProtocolRates(switchRates)) {
@@ -445,13 +501,19 @@ export const BaseSwitchModalContent = ({
445501

446502
// Eth-Flow requires to leave some assets for gas
447503
const nativeDecimals = 18;
448-
const gasRequiredForEthFlow = parseUnits('0.01', nativeDecimals); // TODO: Ask for better value coming from the SDK
449-
const requiredAssetsLeftForGas = isNativeToken(selectedInputToken.address)
450-
? gasRequiredForEthFlow
451-
: undefined;
452-
const maxAmount = requiredAssetsLeftForGas
453-
? parseUnits(selectedInputToken.balance, nativeDecimals) - requiredAssetsLeftForGas
454-
: undefined;
504+
const gasRequiredForEthFlow =
505+
selectedChainId === 1
506+
? parseUnits('0.01', nativeDecimals)
507+
: parseUnits('0.0001', nativeDecimals); // TODO: Ask for better value coming from the SDK
508+
const requiredAssetsLeftForGas =
509+
isNativeToken(selectedInputToken.address) && !userIsSmartContractWallet
510+
? gasRequiredForEthFlow
511+
: undefined;
512+
const maxAmount = (() => {
513+
const balance = parseUnits(selectedInputToken.balance, nativeDecimals);
514+
if (!requiredAssetsLeftForGas) return balance;
515+
return balance > requiredAssetsLeftForGas ? balance - requiredAssetsLeftForGas : balance;
516+
})();
455517
const maxAmountFormatted = maxAmount
456518
? normalize(maxAmount.toString(), nativeDecimals).toString()
457519
: undefined;
@@ -494,11 +556,7 @@ export const BaseSwitchModalContent = ({
494556
return (
495557
<>
496558
{showTitle && (
497-
<TxModalTitle
498-
title={`Swap ${
499-
debounceInputAmount.length && selectedInputToken ? selectedInputToken.symbol : 'Assets'
500-
}`}
501-
/>
559+
<TxModalTitle title={`Swap ${selectedInputToken ? selectedInputToken.symbol : 'Assets'}`} />
502560
)}
503561
{showChangeNetworkWarning && isWrongNetwork.isWrongNetwork && !readOnlyModeAddress && (
504562
<ChangeNetworkWarning
@@ -536,6 +594,11 @@ export const BaseSwitchModalContent = ({
536594
slippageValidation={slippageValidation}
537595
slippage={slippage}
538596
setSlippage={setSlippage}
597+
suggestedSlippage={
598+
switchRates?.provider === 'cowprotocol'
599+
? switchRates?.suggestedSlippage.toString()
600+
: undefined
601+
}
539602
/>
540603
</Box>
541604
{!selectedInputToken || !selectedOutputToken ? (
@@ -559,16 +622,21 @@ export const BaseSwitchModalContent = ({
559622
(token) =>
560623
token.address !== selectedOutputToken.address &&
561624
Number(token.balance) !== 0 &&
625+
// Remove native tokens for non-Safe smart contract wallets
626+
!(userIsSmartContractWallet && !userIsSafeWallet && token.extensions?.isNative) &&
562627
// Avoid wrapping
563628
!(
564629
isNativeToken(selectedOutputToken.address) &&
565-
token.address ===
566-
WRAPPED_NATIVE_CURRENCIES[selectedChainId as SupportedChainId]?.address
630+
token.address.toLowerCase() ===
631+
WRAPPED_NATIVE_CURRENCIES[
632+
selectedChainId as SupportedChainId
633+
]?.address.toLowerCase()
567634
) &&
568635
!(
569-
selectedOutputToken.address ===
570-
WRAPPED_NATIVE_CURRENCIES[selectedChainId as SupportedChainId]?.address &&
571-
isNativeToken(token.address)
636+
selectedOutputToken.address.toLowerCase() ===
637+
WRAPPED_NATIVE_CURRENCIES[
638+
selectedChainId as SupportedChainId
639+
]?.address.toLowerCase() && isNativeToken(token.address)
572640
)
573641
)}
574642
value={inputAmount}
@@ -608,21 +676,29 @@ export const BaseSwitchModalContent = ({
608676
// Avoid wrapping
609677
!(
610678
isNativeToken(selectedInputToken.address) &&
611-
token.address ===
612-
WRAPPED_NATIVE_CURRENCIES[selectedChainId as SupportedChainId]?.address
679+
token.address.toLowerCase() ===
680+
WRAPPED_NATIVE_CURRENCIES[
681+
selectedChainId as SupportedChainId
682+
]?.address.toLowerCase()
613683
) &&
614684
!(
615-
selectedInputToken.address ===
616-
WRAPPED_NATIVE_CURRENCIES[selectedChainId as SupportedChainId]?.address &&
617-
isNativeToken(token.address)
685+
selectedInputToken.address.toLowerCase() ===
686+
WRAPPED_NATIVE_CURRENCIES[
687+
selectedChainId as SupportedChainId
688+
]?.address.toLowerCase() && isNativeToken(token.address)
618689
)
619690
)}
620-
value={
621-
switchRates
622-
? normalizeBN(switchRates.destAmount, switchRates.destDecimals).toString()
623-
: '0'
691+
value={normalizeBN(
692+
switchRates?.provider === 'cowprotocol'
693+
? switchRates?.destSpot
694+
: switchRates?.destAmount || 0,
695+
switchRates?.destDecimals || 18
696+
).toString()}
697+
usdValue={
698+
switchRates?.provider === 'cowprotocol'
699+
? switchRates?.destSpotInUsd
700+
: switchRates?.destUSD || '0'
624701
}
625-
usdValue={switchRates?.destUSD || '0'}
626702
loading={
627703
debounceInputAmount !== '0' &&
628704
debounceInputAmount !== '' &&
@@ -667,6 +743,17 @@ export const BaseSwitchModalContent = ({
667743
</Warning>
668744
)}
669745

746+
{showUSDTResetWarning && (
747+
<Warning severity="info" sx={{ mt: 5 }}>
748+
<Typography variant="caption">
749+
<Trans>
750+
USDT on Ethereum requires approval reset before a new approval. This will
751+
require an additional transaction.
752+
</Trans>
753+
</Typography>
754+
</Warning>
755+
)}
756+
670757
<SwitchErrors
671758
ratesError={ratesError}
672759
balance={selectedInputToken.balance}
@@ -733,8 +820,7 @@ export const BaseSwitchModalContent = ({
733820
inputAmount={debounceInputAmount}
734821
inputToken={selectedInputToken.address}
735822
outputToken={selectedOutputToken.address}
736-
inputName={selectedInputToken.name}
737-
outputName={selectedOutputToken.name}
823+
setShowUSDTResetWarning={setShowUSDTResetWarning}
738824
inputSymbol={selectedInputToken.symbol}
739825
outputSymbol={selectedOutputToken.symbol}
740826
slippage={safeSlippage.toString()}

0 commit comments

Comments
 (0)