Skip to content

Commit cd6efe0

Browse files
committed
feat: refactored withdraw and repay flows with sdk v3
1 parent a0d2d38 commit cd6efe0

File tree

23 files changed

+1518
-14
lines changed

23 files changed

+1518
-14
lines changed

pages/_app.page.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ const FaucetModal = dynamic(() =>
7373
const RepayModal = dynamic(() =>
7474
import('src/components/transactions/Repay/RepayModal').then((module) => module.RepayModal)
7575
);
76+
const RepayModalSDK = dynamic(() =>
77+
import('src/components/transactions/Repay/RepayModalSDK').then((module) => module.RepayModalSDK)
78+
);
7679
const SupplyModal = dynamic(() =>
7780
import('src/components/transactions/Supply/SupplyModal').then((module) => module.SupplyModal)
7881
);
@@ -86,6 +89,11 @@ const WithdrawModal = dynamic(() =>
8689
(module) => module.WithdrawModal
8790
)
8891
);
92+
const WithdrawModalSDK = dynamic(() =>
93+
import('src/components/transactions/Withdraw/WithdrawModalSDK').then(
94+
(module) => module.WithdrawModalSDK
95+
)
96+
);
8997
const StakingMigrateModal = dynamic(() =>
9098
import('src/components/transactions/StakingMigrate/StakingMigrateModal').then(
9199
(module) => module.StakingMigrateModal
@@ -180,9 +188,11 @@ export default function MyApp(props: MyAppProps) {
180188
<SupplyModal />
181189
<SupplyModalSDK />
182190
<WithdrawModal />
191+
<WithdrawModalSDK />
183192
<BorrowModal />
184193
<BorrowModalSDK />
185194
<RepayModal />
195+
<RepayModalSDK />
186196
<CollateralChangeModal />
187197
<ClaimRewardsModal />
188198
<EmodeModal />

src/components/transactions/FlowCommons/ModalWrapperSDK.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { API_ETH_MOCK_ADDRESS } from '@aave/contract-helpers';
2-
import { MarketUserState } from '@aave/graphql/import';
2+
import {
3+
MarketUserReserveBorrowPosition,
4+
MarketUserReserveSupplyPosition,
5+
MarketUserState,
6+
} from '@aave/graphql/import';
37
import React from 'react';
48
import { ReactElement } from 'react-markdown/lib/react-markdown';
59
import { ReserveWithId, useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
@@ -21,6 +25,8 @@ export interface ModalWrapperSDKProps {
2125
poolReserve: ReserveWithId;
2226
reserveUserState?: ReserveWithId['userState'];
2327
marketUserState?: MarketUserState | null;
28+
userSupplies?: MarketUserReserveSupplyPosition[];
29+
userBorrows?: MarketUserReserveBorrowPosition[];
2430
symbol: string;
2531
tokenBalance: string;
2632
nativeBalance: string;
@@ -49,14 +55,22 @@ export const ModalWrapperSDK: React.FC<{
4955
const currentMarketData = useRootStore((store) => store.currentMarketData);
5056
const currentNetworkConfig = useRootStore((store) => store.currentNetworkConfig);
5157
const { walletBalances } = useWalletBalances(currentMarketData);
52-
const { supplyReserves, borrowReserves, market: sdkMarket } = useAppDataContext();
58+
const {
59+
supplyReserves,
60+
borrowReserves,
61+
market: sdkMarket,
62+
userSupplies,
63+
userBorrows,
64+
} = useAppDataContext();
5365
const { txError, mainTxState } = useModalContext();
5466

5567
const { isWrongNetwork, requiredChainId } = useIsWrongNetwork(_requiredChainId);
5668

5769
if (txError && txError.blocking) {
5870
return <TxErrorView txError={txError} />;
5971
}
72+
if (!underlyingAsset) return null;
73+
6074
const addr = underlyingAsset.toLowerCase();
6175
const poolReserveSDK =
6276
supplyReserves.find((reserve) =>
@@ -112,6 +126,8 @@ export const ModalWrapperSDK: React.FC<{
112126
underlyingAsset,
113127
reserveUserState: reserveUserState,
114128
marketUserState: marketUserState,
129+
userSupplies: userSupplies,
130+
userBorrows: userBorrows,
115131
})}
116132
</AssetCapsProviderSDK>
117133
);
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
import {
2+
bigDecimal,
3+
chainId as sdkChainId,
4+
evmAddress,
5+
RepayRequest,
6+
signatureFrom,
7+
} from '@aave/client';
8+
import { repay } from '@aave/client/actions';
9+
import { sendWith } from '@aave/client/viem';
10+
import {
11+
API_ETH_MOCK_ADDRESS,
12+
gasLimitRecommendations,
13+
ProtocolAction,
14+
} from '@aave/contract-helpers';
15+
import { valueToBigNumber } from '@aave/math-utils';
16+
import { Trans } from '@lingui/macro';
17+
import { BoxProps } from '@mui/material';
18+
import { useQueryClient } from '@tanstack/react-query';
19+
import { client } from 'pages/_app.page';
20+
import { useEffect, useState } from 'react';
21+
import { ReserveWithId } from 'src/hooks/app-data-provider/useAppDataProvider';
22+
import { SignedParams, useApprovalTx } from 'src/hooks/useApprovalTx';
23+
import { usePoolApprovedAmount } from 'src/hooks/useApprovedAmount';
24+
import { useModalContext } from 'src/hooks/useModal';
25+
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
26+
import { useRootStore } from 'src/store/root';
27+
import { ApprovalMethod } from 'src/store/walletSlice';
28+
import { getErrorTextFromError, TxAction } from 'src/ui-config/errorMapping';
29+
import { queryKeysFactory } from 'src/ui-config/queries';
30+
import { wagmiConfig } from 'src/ui-config/wagmiConfig';
31+
import { getWalletClient } from 'wagmi/actions';
32+
import { useShallow } from 'zustand/shallow';
33+
34+
import { TxActionsWrapper } from '../TxActionsWrapper';
35+
import { APPROVAL_GAS_LIMIT, checkRequiresApproval } from '../utils';
36+
37+
export interface RepayActionPropsSDK extends BoxProps {
38+
amountToRepay: string;
39+
poolReserve: ReserveWithId;
40+
isWrongNetwork: boolean;
41+
poolAddress: string;
42+
symbol: string;
43+
repayWithATokens: boolean;
44+
blocked?: boolean;
45+
maxApproveNeeded: string;
46+
setShowUSDTResetWarning?: (showUSDTResetWarning: boolean) => void;
47+
chainId?: number;
48+
maxAmountToRepay: string;
49+
}
50+
51+
export const RepayActionsSDK = ({
52+
amountToRepay,
53+
poolReserve,
54+
poolAddress,
55+
isWrongNetwork,
56+
sx,
57+
symbol,
58+
repayWithATokens,
59+
blocked,
60+
maxApproveNeeded,
61+
setShowUSDTResetWarning,
62+
chainId,
63+
maxAmountToRepay,
64+
...props
65+
}: RepayActionPropsSDK) => {
66+
const [tryPermit, walletApprovalMethodPreference, addTransaction, currentMarketData] =
67+
useRootStore(
68+
useShallow((state) => [
69+
state.tryPermit,
70+
state.walletApprovalMethodPreference,
71+
state.addTransaction,
72+
state.currentMarketData,
73+
])
74+
);
75+
const {
76+
approvalTxState,
77+
mainTxState,
78+
loadingTxns,
79+
setMainTxState,
80+
setTxError,
81+
setGasLimit,
82+
setLoadingTxns,
83+
setApprovalTxState,
84+
} = useModalContext();
85+
const { currentAccount, chainId: userChainId } = useWeb3Context();
86+
const queryClient = useQueryClient();
87+
const [signatureParams, setSignatureParams] = useState<SignedParams | undefined>();
88+
89+
const isNativeRepay = poolAddress.toLowerCase() === API_ETH_MOCK_ADDRESS.toLowerCase();
90+
const permitAvailable = tryPermit({
91+
reserveAddress: poolAddress,
92+
isWrappedBaseAsset: !!poolReserve.acceptsNative,
93+
});
94+
const usePermit = permitAvailable && walletApprovalMethodPreference === ApprovalMethod.PERMIT;
95+
96+
const {
97+
data: approvedAmount,
98+
refetch: fetchApprovedAmount,
99+
isRefetching: fetchingApprovedAmount,
100+
isFetchedAfterMount,
101+
} = usePoolApprovedAmount(currentMarketData, poolAddress);
102+
103+
useEffect(() => {
104+
setLoadingTxns(fetchingApprovedAmount);
105+
}, [fetchingApprovedAmount, setLoadingTxns]);
106+
107+
useEffect(() => {
108+
if (!isFetchedAfterMount && !repayWithATokens && !isNativeRepay) {
109+
fetchApprovedAmount();
110+
}
111+
}, [fetchApprovedAmount, isFetchedAfterMount, repayWithATokens, isNativeRepay]);
112+
113+
const requiresApproval =
114+
!repayWithATokens &&
115+
!isNativeRepay &&
116+
Number(amountToRepay) !== 0 &&
117+
checkRequiresApproval({
118+
approvedAmount: approvedAmount?.amount || '0',
119+
amount: Number(amountToRepay) === -1 ? maxApproveNeeded : amountToRepay,
120+
signedAmount: signatureParams ? signatureParams.amount : '0',
121+
});
122+
123+
if (requiresApproval && approvalTxState?.success) {
124+
setApprovalTxState({});
125+
}
126+
127+
const { approval, requiresApprovalReset } = useApprovalTx({
128+
usePermit,
129+
approvedAmount,
130+
requiresApproval,
131+
assetAddress: poolAddress,
132+
symbol,
133+
decimals: poolReserve.underlyingToken.decimals,
134+
signatureAmount: amountToRepay === '-1' ? maxApproveNeeded : amountToRepay,
135+
onApprovalTxConfirmed: fetchApprovedAmount,
136+
onSignTxCompleted: (signedParams) => setSignatureParams(signedParams),
137+
chainId,
138+
setShowUSDTResetWarning,
139+
});
140+
141+
useEffect(() => {
142+
let repayGasLimit = Number(gasLimitRecommendations[ProtocolAction.repay].recommended);
143+
if (requiresApproval && !approvalTxState.success) {
144+
repayGasLimit += Number(APPROVAL_GAS_LIMIT);
145+
}
146+
setGasLimit(repayGasLimit.toString());
147+
}, [approvalTxState.success, requiresApproval, setGasLimit]);
148+
149+
const handleAction = async () => {
150+
if (!currentAccount || !amountToRepay || Number(amountToRepay) === 0) return;
151+
152+
try {
153+
setLoadingTxns(true);
154+
setMainTxState({ ...mainTxState, loading: true });
155+
setTxError(undefined);
156+
157+
const walletClient = await getWalletClient(wagmiConfig, {
158+
chainId: chainId ?? currentMarketData.chainId ?? userChainId,
159+
});
160+
161+
if (!walletClient) {
162+
throw new Error('Wallet client not available');
163+
}
164+
const normalized = amountToRepay === '-1' ? maxAmountToRepay : amountToRepay;
165+
const amountInput: RepayRequest['amount'] = isNativeRepay
166+
? { native: bigDecimal(normalized) }
167+
: {
168+
erc20: {
169+
currency: evmAddress(poolAddress),
170+
value: { exact: bigDecimal(normalized) },
171+
permitSig:
172+
usePermit && signatureParams
173+
? {
174+
value: signatureFrom(signatureParams.signature as string),
175+
deadline: Number(signatureParams.deadline),
176+
}
177+
: null,
178+
},
179+
};
180+
181+
const result = await repay(client, {
182+
market: evmAddress(currentMarketData.addresses.LENDING_POOL),
183+
amount: amountInput,
184+
sender: evmAddress(currentAccount),
185+
chainId: sdkChainId(chainId ?? currentMarketData.chainId),
186+
})
187+
.andThen(sendWith(walletClient))
188+
.andThen(client.waitForTransaction);
189+
190+
if (result.isErr()) {
191+
const parsedError = getErrorTextFromError(
192+
result.error as Error,
193+
TxAction.MAIN_ACTION,
194+
false
195+
);
196+
setTxError(parsedError);
197+
setMainTxState({ txHash: undefined, loading: false });
198+
return;
199+
}
200+
201+
const txHash = String(result.value);
202+
setMainTxState({
203+
txHash,
204+
loading: false,
205+
success: true,
206+
});
207+
208+
addTransaction(txHash, {
209+
action: ProtocolAction.repay,
210+
txState: 'success',
211+
asset: poolAddress,
212+
amount: normalized,
213+
assetName: symbol,
214+
amountUsd: valueToBigNumber(normalized)
215+
.multipliedBy(poolReserve.usdExchangeRate)
216+
.toString(),
217+
});
218+
219+
queryClient.invalidateQueries({ queryKey: queryKeysFactory.pool });
220+
queryClient.invalidateQueries({ queryKey: queryKeysFactory.gho });
221+
} catch (error) {
222+
const parsedError = getErrorTextFromError(error as Error, TxAction.MAIN_ACTION, false);
223+
setTxError(parsedError);
224+
setMainTxState({
225+
txHash: undefined,
226+
loading: false,
227+
});
228+
} finally {
229+
setLoadingTxns(false);
230+
}
231+
};
232+
233+
return (
234+
<TxActionsWrapper
235+
blocked={blocked}
236+
preparingTransactions={loadingTxns}
237+
symbol={poolReserve.underlyingToken.symbol}
238+
mainTxState={mainTxState}
239+
approvalTxState={approvalTxState}
240+
requiresAmount
241+
amount={amountToRepay || '0'}
242+
requiresApproval={requiresApproval}
243+
isWrongNetwork={isWrongNetwork}
244+
sx={sx}
245+
{...props}
246+
handleAction={handleAction}
247+
handleApproval={requiresApproval ? approval : undefined}
248+
actionText={<Trans>Repay {symbol}</Trans>}
249+
actionInProgressText={<Trans>Repaying {symbol}</Trans>}
250+
tryPermit={permitAvailable}
251+
requiresApprovalReset={requiresApprovalReset}
252+
/>
253+
);
254+
};

0 commit comments

Comments
 (0)