Skip to content

Commit 67d51c2

Browse files
committed
fix: usdt approval reset on supply and repay
1 parent d24506e commit 67d51c2

File tree

13 files changed

+189
-23
lines changed

13 files changed

+189
-23
lines changed

src/components/transactions/Repay/RepayActions.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export interface RepayActionProps extends BoxProps {
2929
repayWithATokens: boolean;
3030
blocked?: boolean;
3131
maxApproveNeeded: string;
32+
setShowUSDTResetWarning?: (showUSDTResetWarning: boolean) => void;
33+
chainId?: number;
3234
}
3335

3436
export const RepayActions = ({
@@ -41,6 +43,8 @@ export const RepayActions = ({
4143
repayWithATokens,
4244
blocked,
4345
maxApproveNeeded,
46+
setShowUSDTResetWarning,
47+
chainId,
4448
...props
4549
}: RepayActionProps) => {
4650
const [
@@ -112,7 +116,7 @@ export const RepayActions = ({
112116
setApprovalTxState({});
113117
}
114118

115-
const { approval } = useApprovalTx({
119+
const { approval, requiresApprovalReset } = useApprovalTx({
116120
usePermit,
117121
approvedAmount,
118122
requiresApproval,
@@ -122,6 +126,8 @@ export const RepayActions = ({
122126
signatureAmount: amountToRepay,
123127
onApprovalTxConfirmed: fetchApprovedAmount,
124128
onSignTxCompleted: (signedParams) => setSignatureParams(signedParams),
129+
chainId,
130+
setShowUSDTResetWarning,
125131
});
126132

127133
useEffect(() => {
@@ -244,6 +250,7 @@ export const RepayActions = ({
244250
actionText={<Trans>Repay {symbol}</Trans>}
245251
actionInProgressText={<Trans>Repaying {symbol}</Trans>}
246252
tryPermit={permitAvailable}
253+
requiresApprovalReset={requiresApprovalReset}
247254
/>
248255
);
249256
};

src/components/transactions/Repay/RepayModalContent.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Trans } from '@lingui/macro';
99
import Typography from '@mui/material/Typography';
1010
import { BigNumber } from 'bignumber.js';
1111
import React, { useEffect, useRef, useState } from 'react';
12+
import { Warning } from 'src/components/primitives/Warning';
1213
import {
1314
ExtendedFormattedUser,
1415
useAppDataContext,
@@ -67,6 +68,7 @@ export const RepayModalContent = ({
6768
const [repayMax, setRepayMax] = useState('');
6869
const [_amount, setAmount] = useState('');
6970
const amountRef = useRef<string>();
71+
const [showUSDTResetWarning, setShowUSDTResetWarning] = useState(false);
7072

7173
const networkConfig = getNetworkConfig(currentChainId);
7274

@@ -279,6 +281,17 @@ export const RepayModalContent = ({
279281

280282
{txError && <GasEstimationError txError={txError} />}
281283

284+
{showUSDTResetWarning && (
285+
<Warning severity="info" sx={{ mt: 5 }}>
286+
<Typography variant="caption">
287+
<Trans>
288+
USDT on Ethereum requires approval reset before a new approval. This will require an
289+
additional transaction.
290+
</Trans>
291+
</Typography>
292+
</Warning>
293+
)}
294+
282295
<RepayActions
283296
maxApproveNeeded={safeAmountToRepayAll.toString()}
284297
poolReserve={poolReserve}
@@ -289,6 +302,8 @@ export const RepayModalContent = ({
289302
isWrongNetwork={isWrongNetwork}
290303
symbol={modalSymbol}
291304
repayWithATokens={repayWithATokens}
305+
setShowUSDTResetWarning={setShowUSDTResetWarning}
306+
chainId={currentChainId}
292307
/>
293308
</>
294309
);

src/components/transactions/Supply/SupplyActions.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export interface SupplyActionProps extends BoxProps {
2727
blocked: boolean;
2828
decimals: number;
2929
isWrappedBaseAsset: boolean;
30+
setShowUSDTResetWarning?: (showUSDTResetWarning: boolean) => void;
31+
chainId?: number;
3032
}
3133

3234
export const SupplyActions = React.memo(
@@ -39,6 +41,8 @@ export const SupplyActions = React.memo(
3941
blocked,
4042
decimals,
4143
isWrappedBaseAsset,
44+
setShowUSDTResetWarning,
45+
chainId,
4246
...props
4347
}: SupplyActionProps) => {
4448
const [
@@ -101,7 +105,7 @@ export const SupplyActions = React.memo(
101105

102106
const usePermit = permitAvailable && walletApprovalMethodPreference === ApprovalMethod.PERMIT;
103107

104-
const { approval } = useApprovalTx({
108+
const { approval, requiresApprovalReset } = useApprovalTx({
105109
usePermit,
106110
approvedAmount,
107111
requiresApproval,
@@ -111,6 +115,8 @@ export const SupplyActions = React.memo(
111115
signatureAmount: amountToSupply,
112116
onApprovalTxConfirmed: fetchApprovedAmount,
113117
onSignTxCompleted: (signedParams) => setSignatureParams(signedParams),
118+
chainId,
119+
setShowUSDTResetWarning,
114120
});
115121

116122
useEffect(() => {
@@ -212,6 +218,7 @@ export const SupplyActions = React.memo(
212218
tryPermit={permitAvailable}
213219
sx={sx}
214220
{...props}
221+
requiresApprovalReset={requiresApprovalReset}
215222
/>
216223
);
217224
}

src/components/transactions/Supply/SupplyModalContent.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
import { useAssetCaps } from 'src/hooks/useAssetCaps';
2020
import { useModalContext } from 'src/hooks/useModal';
2121
import { useWrappedTokens, WrappedTokenConfig } from 'src/hooks/useWrappedTokens';
22+
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
2223
import { ERC20TokenType } from 'src/libs/web3-data-provider/Web3Provider';
2324
import { useRootStore } from 'src/store/root';
2425
import { GENERAL } from 'src/utils/events';
@@ -146,6 +147,7 @@ export const SupplyModalContent = React.memo(
146147
}: SupplyModalContentProps) => {
147148
const { marketReferencePriceInUsd } = useAppDataContext();
148149
const { mainTxState: supplyTxState, gasLimit, txError } = useModalContext();
150+
const { chainId } = useWeb3Context();
149151
const [minRemainingBaseTokenBalance, currentMarketData, currentNetworkConfig] = useRootStore(
150152
useShallow((state) => [
151153
state.poolComputed.minRemainingBaseTokenBalance,
@@ -156,6 +158,7 @@ export const SupplyModalContent = React.memo(
156158

157159
// states
158160
const [amount, setAmount] = useState('');
161+
const [showUSDTResetWarning, setShowUSDTResetWarning] = useState(false);
159162
const supplyUnWrapped = underlyingAsset.toLowerCase() === API_ETH_MOCK_ADDRESS.toLowerCase();
160163

161164
const walletBalance = supplyUnWrapped ? nativeBalance : tokenBalance;
@@ -201,6 +204,8 @@ export const SupplyModalContent = React.memo(
201204
blocked: false,
202205
decimals: poolReserve.decimals,
203206
isWrappedBaseAsset: poolReserve.isWrappedBaseAsset,
207+
setShowUSDTResetWarning,
208+
chainId,
204209
};
205210

206211
if (supplyTxState.success)
@@ -272,6 +277,17 @@ export const SupplyModalContent = React.memo(
272277

273278
{txError && <GasEstimationError txError={txError} />}
274279

280+
{showUSDTResetWarning && (
281+
<Warning severity="info" sx={{ mt: 5 }}>
282+
<Typography variant="caption">
283+
<Trans>
284+
USDT on Ethereum requires approval reset before a new approval. This will require an
285+
additional transaction.
286+
</Trans>
287+
</Typography>
288+
</Warning>
289+
)}
290+
275291
<SupplyActions {...supplyActionsProps} />
276292
</>
277293
);

src/components/transactions/Switch/SwitchActions.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,7 @@ export const SwitchActions = ({
153153
0
154154
);
155155

156-
if (
157-
needsUSDTApprovalReset(inputSymbol, chainId, BigInt(currentApproved), BigInt(amountToApprove))
158-
) {
156+
if (needsUSDTApprovalReset(inputSymbol, chainId, currentApproved, amountToApprove)) {
159157
setShowUSDTResetWarning(true);
160158
setRequiresApprovalReset(true);
161159
} else {

src/components/transactions/TxActionsWrapper.tsx

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ interface TxActionsWrapperProps extends BoxProps {
2222
preparingTransactions: boolean;
2323
requiresAmount?: boolean;
2424
requiresApproval: boolean;
25+
requiresApprovalReset?: boolean;
2526
symbol?: string;
2627
blocked?: boolean;
2728
fetchingData?: boolean;
@@ -47,6 +48,7 @@ export const TxActionsWrapper = ({
4748
preparingTransactions,
4849
requiresAmount,
4950
requiresApproval,
51+
requiresApprovalReset,
5052
sx,
5153
symbol,
5254
blocked,
@@ -95,7 +97,15 @@ export const TxActionsWrapper = ({
9597
)
9698
return null;
9799
if (approvalTxState?.loading)
98-
return { loading: true, disabled: true, content: <Trans>Approving {symbol}...</Trans> };
100+
return {
101+
loading: true,
102+
disabled: true,
103+
content: (
104+
<Trans>
105+
{requiresApprovalReset ? 'Resetting' : 'Approving'} {symbol}...
106+
</Trans>
107+
),
108+
};
99109
if (approvalTxState?.success)
100110
return {
101111
disabled: true,
@@ -116,7 +126,11 @@ export const TxActionsWrapper = ({
116126
iconSize={20}
117127
iconMargin={2}
118128
color="white"
119-
text={<Trans>Approve {symbol} to continue</Trans>}
129+
text={
130+
<Trans>
131+
{requiresApprovalReset ? 'Reset' : 'Approve'} {symbol} to continue
132+
</Trans>
133+
}
120134
/>
121135
),
122136
handleClick: handleApproval,

src/hooks/useApprovalTx.tsx

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import { ApproveType, MAX_UINT_AMOUNT, ProtocolAction } from '@aave/contract-helpers';
22
import { SignatureLike } from '@ethersproject/bytes';
3-
import { constants } from 'ethers';
3+
import { constants, ethers } from 'ethers';
44
import { parseUnits } from 'ethers/lib/utils';
5+
import { useEffect, useState } from 'react';
56
import { MOCK_SIGNED_HASH } from 'src/helpers/useTransactionHandler';
67
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
78
import { useRootStore } from 'src/store/root';
89
import { getErrorTextFromError, TxAction } from 'src/ui-config/errorMapping';
10+
import { isUSDTOnEthereum, needsUSDTApprovalReset } from 'src/utils/usdtHelpers';
911
import { useShallow } from 'zustand/shallow';
1012

1113
import { useModalContext } from './useModal';
@@ -28,6 +30,7 @@ export const useApprovalTx = ({
2830
onSignTxCompleted,
2931
chainId,
3032
amountToApprove,
33+
setShowUSDTResetWarning,
3134
}: {
3235
usePermit: boolean;
3336
approvedAmount: ApproveType | undefined;
@@ -40,6 +43,7 @@ export const useApprovalTx = ({
4043
onSignTxCompleted?: (signedParams: SignedParams) => void;
4144
chainId?: number;
4245
amountToApprove?: string;
46+
setShowUSDTResetWarning?: (showUSDTResetWarning: boolean) => void;
4347
}) => {
4448
const [generateApproval, generateSignatureRequest, estimateGasLimit, addTransaction] =
4549
useRootStore(
@@ -55,9 +59,109 @@ export const useApprovalTx = ({
5559

5660
const { approvalTxState, setApprovalTxState, setTxError } = useModalContext();
5761

62+
const [requiresApprovalReset, setRequiresApprovalReset] = useState(false);
63+
64+
// Warning for USDT on Ethereum approval reset
65+
useEffect(() => {
66+
if (
67+
!chainId ||
68+
!isUSDTOnEthereum(symbol, chainId) ||
69+
!setShowUSDTResetWarning ||
70+
!signatureAmount ||
71+
signatureAmount === '0'
72+
) {
73+
return;
74+
}
75+
76+
console.log('signatureAmount', signatureAmount);
77+
console.log('decimals', decimals);
78+
const amountToApprove = parseUnits(signatureAmount, decimals).toString();
79+
const currentApproved = parseUnits(
80+
approvedAmount?.amount?.toString() || '0',
81+
decimals
82+
).toString();
83+
84+
console.log('amountToApprove', amountToApprove);
85+
console.log('currentApproved', currentApproved);
86+
console.log(
87+
'needsUSDTApprovalReset',
88+
needsUSDTApprovalReset(symbol, chainId, currentApproved, amountToApprove)
89+
);
90+
91+
if (needsUSDTApprovalReset(symbol, chainId, currentApproved, amountToApprove)) {
92+
setShowUSDTResetWarning(true);
93+
setRequiresApprovalReset(true);
94+
} else {
95+
setShowUSDTResetWarning(false);
96+
setRequiresApprovalReset(false);
97+
}
98+
}, [symbol, chainId, approvedAmount?.amount, signatureAmount, setShowUSDTResetWarning, decimals]);
99+
58100
const approval = async () => {
59101
try {
60102
if (requiresApproval && approvedAmount) {
103+
// Handle USDT approval reset first
104+
if (requiresApprovalReset) {
105+
const resetData = {
106+
spender: approvedAmount.spender,
107+
user: approvedAmount.user,
108+
token: approvedAmount.token,
109+
amount: '0',
110+
};
111+
112+
try {
113+
if (usePermit) {
114+
const deadline = Math.floor(Date.now() / 1000 + 3600).toString();
115+
const signatureRequest = await generateSignatureRequest(
116+
{
117+
...resetData,
118+
deadline,
119+
},
120+
{ chainId }
121+
);
122+
setApprovalTxState({ ...approvalTxState, loading: true });
123+
await signTxData(signatureRequest);
124+
setApprovalTxState({
125+
loading: false,
126+
success: false,
127+
});
128+
} else {
129+
// Create direct ERC20 approval transaction for reset to 0
130+
const abi = new ethers.utils.Interface([
131+
'function approve(address spender, uint256 amount)',
132+
]);
133+
const encodedData = abi.encodeFunctionData('approve', [approvedAmount.spender, '0']);
134+
const resetTx = {
135+
data: encodedData,
136+
to: approvedAmount.token,
137+
from: approvedAmount.user,
138+
};
139+
const resetTxWithGasEstimation = await estimateGasLimit(resetTx, chainId);
140+
setApprovalTxState({ ...approvalTxState, loading: true });
141+
const resetResponse = await sendTx(resetTxWithGasEstimation);
142+
await resetResponse.wait(1);
143+
setApprovalTxState({
144+
loading: false,
145+
success: false,
146+
});
147+
}
148+
} catch (error) {
149+
const parsedError = getErrorTextFromError(error, TxAction.APPROVAL, false);
150+
console.error(parsedError);
151+
setTxError(parsedError);
152+
setApprovalTxState({
153+
txHash: undefined,
154+
loading: false,
155+
});
156+
}
157+
if (onApprovalTxConfirmed) {
158+
onApprovalTxConfirmed();
159+
}
160+
161+
return;
162+
}
163+
164+
// Normal approval logic
61165
if (usePermit) {
62166
setApprovalTxState({ ...approvalTxState, loading: true });
63167
const deadline = Math.floor(Date.now() / 1000 + 3600).toString();
@@ -119,5 +223,6 @@ export const useApprovalTx = ({
119223

120224
return {
121225
approval,
226+
requiresApprovalReset,
122227
};
123228
};

src/locales/el/messages.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/locales/en/messages.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)