Skip to content

Commit 1bad771

Browse files
committed
feat: refactor borrow flow; functionality complete, pending polish and code cleanup
1 parent 958a5e2 commit 1bad771

File tree

11 files changed

+627
-34
lines changed

11 files changed

+627
-34
lines changed

pages/_app.page.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ const BridgeModal = dynamic(() =>
5454
const BorrowModal = dynamic(() =>
5555
import('src/components/transactions/Borrow/BorrowModal').then((module) => module.BorrowModal)
5656
);
57+
const BorrowModalSDK = dynamic(() =>
58+
import('src/components/transactions/Borrow/BorrowModalSDK').then(
59+
(module) => module.BorrowModalSDK
60+
)
61+
);
5762
const ClaimRewardsModal = dynamic(() =>
5863
import('src/components/transactions/ClaimRewards/ClaimRewardsModal').then(
5964
(module) => module.ClaimRewardsModal
@@ -176,6 +181,7 @@ export default function MyApp(props: MyAppProps) {
176181
<SupplyModalSDK />
177182
<WithdrawModal />
178183
<BorrowModal />
184+
<BorrowModalSDK />
179185
<RepayModal />
180186
<CollateralChangeModal />
181187
<ClaimRewardsModal />
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
import { bigDecimal, ChainId, evmAddress } from '@aave/client';
2+
import { approveBorrowCreditDelegation, borrow } from '@aave/client/actions';
3+
import { sendWith } from '@aave/client/viem';
4+
import {
5+
API_ETH_MOCK_ADDRESS,
6+
gasLimitRecommendations,
7+
ProtocolAction,
8+
} from '@aave/contract-helpers';
9+
import { valueToBigNumber } from '@aave/math-utils';
10+
import { Trans } from '@lingui/macro';
11+
import { BoxProps } from '@mui/material';
12+
import { useQueryClient } from '@tanstack/react-query';
13+
import { client } from 'pages/_app.page';
14+
import React, { useEffect, useState } from 'react';
15+
import { ReserveWithId, useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
16+
import { useModalContext } from 'src/hooks/useModal';
17+
import { useWeb3Context } from 'src/libs/hooks/useWeb3Context';
18+
import { useRootStore } from 'src/store/root';
19+
import { getErrorTextFromError, TxAction } from 'src/ui-config/errorMapping';
20+
import { queryKeysFactory } from 'src/ui-config/queries';
21+
import { wagmiConfig } from 'src/ui-config/wagmiConfig';
22+
import { getWalletClient } from 'wagmi/actions';
23+
import { useShallow } from 'zustand/shallow';
24+
25+
import { TxActionsWrapper } from '../TxActionsWrapper';
26+
import { APPROVE_DELEGATION_GAS_LIMIT } from '../utils';
27+
28+
export interface BorrowActionsPropsSDK extends BoxProps {
29+
poolReserve: ReserveWithId;
30+
amountToBorrow: string;
31+
poolAddress: string;
32+
isWrongNetwork: boolean;
33+
symbol: string;
34+
borrowNative: boolean;
35+
blocked: boolean;
36+
}
37+
38+
export const BorrowActionsSDK = React.memo(
39+
({
40+
symbol,
41+
poolReserve,
42+
amountToBorrow,
43+
poolAddress,
44+
isWrongNetwork,
45+
borrowNative,
46+
blocked,
47+
sx,
48+
}: BorrowActionsPropsSDK) => {
49+
const [currentMarketData, addTransaction] = useRootStore(
50+
useShallow((state) => [state.currentMarketData, state.addTransaction])
51+
);
52+
const {
53+
approvalTxState,
54+
mainTxState,
55+
loadingTxns,
56+
setMainTxState,
57+
setTxError,
58+
setGasLimit,
59+
setLoadingTxns,
60+
setApprovalTxState,
61+
} = useModalContext();
62+
63+
const queryClient = useQueryClient();
64+
const { borrowReserves } = useAppDataContext();
65+
const { currentAccount, chainId: userChainId } = useWeb3Context();
66+
const [requiresApproval, setRequiresApproval] = useState(
67+
borrowNative && poolAddress === API_ETH_MOCK_ADDRESS
68+
);
69+
console.log('BorrowActionsSDK render requiresApproval:', requiresApproval);
70+
console.log('PoolAddress', poolAddress);
71+
useEffect(() => {
72+
setRequiresApproval(
73+
borrowNative && poolAddress === API_ETH_MOCK_ADDRESS && !approvalTxState.success
74+
);
75+
}, [borrowNative, poolAddress, approvalTxState.success]);
76+
77+
const handleApproval = async () => {
78+
if (!currentAccount) return;
79+
try {
80+
setApprovalTxState({ ...approvalTxState, loading: true });
81+
const walletClient = await getWalletClient(wagmiConfig, {
82+
chainId: currentMarketData.chainId ?? userChainId,
83+
});
84+
if (!walletClient) {
85+
throw new Error('Wallet client not available');
86+
}
87+
88+
const approvalResult = await approveBorrowCreditDelegation(client, {
89+
market: evmAddress(currentMarketData.addresses.LENDING_POOL),
90+
underlyingToken: evmAddress(poolReserve.underlyingToken.address),
91+
amount: bigDecimal(amountToBorrow),
92+
user: evmAddress(currentAccount),
93+
delegatee: evmAddress(currentMarketData.addresses.WETH_GATEWAY ?? currentAccount),
94+
chainId: (currentMarketData.chainId ?? userChainId) as ChainId,
95+
}).andThen(sendWith(walletClient));
96+
97+
if (approvalResult.isErr()) {
98+
const parsedError = getErrorTextFromError(
99+
approvalResult.error as Error,
100+
TxAction.APPROVAL,
101+
false
102+
);
103+
setTxError(parsedError);
104+
setApprovalTxState({ txHash: undefined, loading: false });
105+
return;
106+
}
107+
108+
const txHash = String(approvalResult.value);
109+
setApprovalTxState({ txHash, loading: false, success: true });
110+
setRequiresApproval(false);
111+
setTxError(undefined);
112+
} catch (error) {
113+
const parsedError = getErrorTextFromError(error as Error, TxAction.APPROVAL, false);
114+
setTxError(parsedError);
115+
setApprovalTxState({ txHash: undefined, loading: false });
116+
}
117+
};
118+
119+
useEffect(() => {
120+
let borrowGasLimit = Number(gasLimitRecommendations[ProtocolAction.borrow].recommended);
121+
if (requiresApproval && !approvalTxState.success) {
122+
borrowGasLimit += Number(APPROVE_DELEGATION_GAS_LIMIT);
123+
}
124+
setGasLimit(borrowGasLimit.toString());
125+
}, [requiresApproval, approvalTxState.success, setGasLimit]);
126+
127+
const handleAction = async () => {
128+
if (!amountToBorrow || Number(amountToBorrow) === 0) return;
129+
if (requiresApproval && !approvalTxState.success) {
130+
setTxError(
131+
getErrorTextFromError(
132+
new Error('Approval required before borrowing'),
133+
TxAction.APPROVAL,
134+
false
135+
)
136+
);
137+
return;
138+
}
139+
try {
140+
setLoadingTxns(true);
141+
setMainTxState({ ...mainTxState, loading: true });
142+
setTxError(undefined);
143+
144+
const walletClient = await getWalletClient(wagmiConfig, {
145+
chainId: currentMarketData.chainId ?? userChainId,
146+
});
147+
148+
if (!walletClient) {
149+
throw new Error('Wallet client not available');
150+
}
151+
const amountInput = borrowNative
152+
? { native: bigDecimal(amountToBorrow) }
153+
: {
154+
erc20: {
155+
currency: evmAddress(poolAddress),
156+
value: bigDecimal(amountToBorrow),
157+
},
158+
};
159+
160+
const result = await borrow(client, {
161+
market: evmAddress(currentMarketData.addresses.LENDING_POOL),
162+
amount: amountInput,
163+
sender: evmAddress(currentAccount),
164+
chainId: (currentMarketData.chainId ?? userChainId) as ChainId,
165+
})
166+
.andThen(sendWith(walletClient))
167+
.andThen(client.waitForTransaction);
168+
if (result.isErr()) {
169+
const parsedError = getErrorTextFromError(
170+
result.error as Error,
171+
TxAction.MAIN_ACTION,
172+
false
173+
);
174+
setTxError(parsedError);
175+
setMainTxState({ txHash: undefined, loading: false });
176+
return;
177+
}
178+
179+
const txHash = String(result.value);
180+
setMainTxState({
181+
txHash,
182+
loading: false,
183+
success: true,
184+
});
185+
186+
addTransaction(txHash, {
187+
action: ProtocolAction.borrow,
188+
txState: 'success',
189+
asset: poolAddress,
190+
amount: amountToBorrow,
191+
assetName: symbol,
192+
amountUsd: (() => {
193+
const reserve = borrowReserves.find(
194+
(r) => r.underlyingToken.address.toLowerCase() === poolAddress.toLowerCase()
195+
);
196+
return reserve
197+
? valueToBigNumber(amountToBorrow).multipliedBy(reserve.usdExchangeRate).toString()
198+
: undefined;
199+
})(),
200+
});
201+
202+
queryClient.invalidateQueries({ queryKey: queryKeysFactory.pool });
203+
queryClient.invalidateQueries({ queryKey: queryKeysFactory.gho });
204+
} catch (error) {
205+
const parsedError = getErrorTextFromError(error as Error, TxAction.MAIN_ACTION, false);
206+
setTxError(parsedError);
207+
setMainTxState({
208+
txHash: undefined,
209+
loading: false,
210+
});
211+
} finally {
212+
setLoadingTxns(false);
213+
}
214+
};
215+
216+
return (
217+
<TxActionsWrapper
218+
blocked={blocked}
219+
mainTxState={mainTxState}
220+
approvalTxState={approvalTxState}
221+
requiresAmount={true}
222+
amount={amountToBorrow}
223+
isWrongNetwork={isWrongNetwork}
224+
handleAction={handleAction}
225+
actionText={<Trans>Borrow {symbol}</Trans>}
226+
actionInProgressText={<Trans>Borrowing {symbol}</Trans>}
227+
handleApproval={requiresApproval ? handleApproval : undefined}
228+
requiresApproval={requiresApproval}
229+
preparingTransactions={loadingTxns}
230+
sx={sx}
231+
/>
232+
);
233+
}
234+
);

0 commit comments

Comments
 (0)