diff --git a/src/views/Lending/Cooler/hooks/useGetWalletFundsRequired.tsx b/src/views/Lending/Cooler/hooks/useGetWalletFundsRequired.tsx new file mode 100644 index 000000000..b1c61b4cc --- /dev/null +++ b/src/views/Lending/Cooler/hooks/useGetWalletFundsRequired.tsx @@ -0,0 +1,45 @@ +import { COOLER_CONSOLIDATION_CONTRACT } from "src/constants/contracts"; +import { DecimalBigNumber } from "src/helpers/DecimalBigNumber/DecimalBigNumber"; +import { useTestableNetworks } from "src/hooks/useTestableNetworks"; +import { CoolerConsolidation__factory } from "src/typechain"; +import { useProvider, useQuery } from "wagmi"; + +export const useGetWalletFundsRequired = ({ + clearingHouseAddress, + coolerAddress, + loanIds, +}: { + clearingHouseAddress: string; + coolerAddress: string; + loanIds: number[]; +}) => { + const provider = useProvider(); + const networks = useTestableNetworks(); + + const { data, isFetched, isLoading } = useQuery( + ["useGetWalletFundsRequired", clearingHouseAddress, coolerAddress], + async () => { + try { + const contractAddress = COOLER_CONSOLIDATION_CONTRACT.addresses[networks.MAINNET]; + const contract = CoolerConsolidation__factory.connect(contractAddress, provider); + const requiredDebtInWallet = await contract.fundsRequired(clearingHouseAddress, coolerAddress, loanIds); + const requiredCollateralInWallet = await contract.collateralRequired( + clearingHouseAddress, + coolerAddress, + loanIds, + ); + return { + totalDebtNeededInWallet: new DecimalBigNumber(requiredDebtInWallet.interest, 18), + totalCollateralNeededInWallet: new DecimalBigNumber(requiredCollateralInWallet.additionalCollateral, 18), + }; + } catch { + return { + totalDebtNeededInWallet: new DecimalBigNumber("0", 18), + totalCollateralNeededInWallet: new DecimalBigNumber("0", 18), + }; + } + }, + { enabled: !!coolerAddress }, + ); + return { data, isFetched, isLoading }; +}; diff --git a/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx b/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx index c9c11d9f4..a58f2b277 100644 --- a/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx +++ b/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx @@ -1,4 +1,14 @@ -import { Box, FormControl, MenuItem, Select, SelectChangeEvent, SvgIcon, Typography } from "@mui/material"; +import { + Box, + Divider, + FormControl, + MenuItem, + Select, + SelectChangeEvent, + SvgIcon, + Tooltip, + Typography, +} from "@mui/material"; import { InfoNotification, Modal, PrimaryButton } from "@olympusdao/component-library"; import { BigNumber } from "ethers"; import { formatEther } from "ethers/lib/utils.js"; @@ -17,6 +27,7 @@ import { useCreateCooler } from "src/views/Lending/Cooler/hooks/useCreateCooler" import { useGetClearingHouse } from "src/views/Lending/Cooler/hooks/useGetClearingHouse"; import { useGetConsolidationAllowances } from "src/views/Lending/Cooler/hooks/useGetConsolidationAllowances"; import { useGetCoolerLoans } from "src/views/Lending/Cooler/hooks/useGetCoolerLoans"; +import { useGetWalletFundsRequired } from "src/views/Lending/Cooler/hooks/useGetWalletFundsRequired"; export const ConsolidateLoans = ({ v3CoolerAddress, @@ -107,22 +118,35 @@ export const ConsolidateLoans = ({ loanIds, }); + const { data: walletFundsRequired } = useGetWalletFundsRequired({ + clearingHouseAddress: clearingHouseAddresses.v3.clearingHouseAddress, + coolerAddress: coolerAddress || "", + loanIds, + }); + const maturityDate = new Date(); maturityDate.setDate(maturityDate.getDate() + Number(duration || 0)); const { data: debtBalance } = useBalance({ [networks.MAINNET]: debtAddress || "" })[networks.MAINNET]; - const [insufficientCollateral, setInsufficientCollateral] = useState(); + const { data: gOhmBalance } = useBalance(GOHM_ADDRESSES)[networks.MAINNET]; + const [insufficientBalances, setInsufficientBalances] = useState< + | { + debt: boolean; + gohm: boolean; + } + | undefined + >(); + useEffect(() => { - if (!debtBalance) { - setInsufficientCollateral(undefined); + if (!debtBalance || !gOhmBalance || !walletFundsRequired) { + setInsufficientBalances(undefined); return; } - if (Number(debtBalance) < parseFloat(formatEther(totals.interest))) { - setInsufficientCollateral(true); - } else { - setInsufficientCollateral(false); - } - }, [debtBalance, totals.interest]); + setInsufficientBalances({ + debt: Number(debtBalance) < Number(walletFundsRequired.totalDebtNeededInWallet.toString()), + gohm: Number(gOhmBalance) < Number(walletFundsRequired.totalCollateralNeededInWallet.toString()), + }); + }, [debtBalance, gOhmBalance, walletFundsRequired]); const handleVersionChange = (event: SelectChangeEvent) => { setSelectedVersion(event.target.value as "v1" | "v2" | "v3"); @@ -188,16 +212,106 @@ export const ConsolidateLoans = ({ <> All existing open loans for this Cooler and Clearinghouse will be repaid and consolidated into a new - loan with a {duration} day duration. You must hold enough {clearingHouseAddresses.v3.debtAssetName} in - your wallet to cover the interest owed at consolidation. + loan with a {duration} day duration. You must hold enough {clearingHouseAddresses.v3.debtAssetName} and + gOHM in your wallet to cover the interest owed at consolidation. + {walletFundsRequired && ( + <> + Wallet Balances Required + + + {clearingHouseAddresses.v3.debtAssetName} Balance + + + + + {Math.ceil(Number(walletFundsRequired?.totalDebtNeededInWallet.toString()) * 100) / 100} + + + + + + gOHM Balance + + + + {Math.ceil(Number(walletFundsRequired?.totalCollateralNeededInWallet.toString()) * 100000) / + 100000} + + + + + + )} + + + + + Required Allowances + + + + {clearingHouseAddresses.v3.debtAssetName} + + + + + {Math.ceil(Number(allowances?.totalDebtWithFee.toString()) * 100) / 100} + + + + + + gOHM + + + + {Math.ceil(Number(allowances?.consolidatedLoanCollateral.toString()) * 100000) / 100000} + + + + + + + + + Consolidated Loan Details + Loans to Consolidate @@ -210,7 +324,7 @@ export const ConsolidateLoans = ({ justifyContent="space-between" alignItems="center" mb={"9px"} - mt={"21px"} + mt={"9px"} > New Principal Amount @@ -226,7 +340,7 @@ export const ConsolidateLoans = ({ justifyContent="space-between" alignItems="center" mb={"9px"} - mt={"21px"} + mt={"9px"} > Interest Owed At Consolidation @@ -242,7 +356,7 @@ export const ConsolidateLoans = ({ justifyContent="space-between" alignItems="center" mb={"9px"} - mt={"21px"} + mt={"9px"} > New Maturity Date @@ -261,7 +375,7 @@ export const ConsolidateLoans = ({ )} - {selectedVersion && !insufficientCollateral ? ( + {selectedVersion && !insufficientBalances?.debt && !insufficientBalances?.gohm ? ( {needsCoolerCreation ? ( ) : ( - {insufficientCollateral + {insufficientBalances?.debt ? `Insufficient ${clearingHouseAddresses.v3.debtAssetName} Balance` - : "Select loans to consolidate"} + : insufficientBalances?.gohm + ? "Insufficient gOHM Balance" + : "Select loans to consolidate"} )} diff --git a/src/views/Lending/Cooler/positions/Positions.tsx b/src/views/Lending/Cooler/positions/Positions.tsx index 1d741ebf5..593c08071 100644 --- a/src/views/Lending/Cooler/positions/Positions.tsx +++ b/src/views/Lending/Cooler/positions/Positions.tsx @@ -87,7 +87,11 @@ export const CoolerPositions = () => { ...(loansV2 || []).map(loan => ({ ...loan, version: "v2" })), ...(loansV3 || []).map(loan => ({ ...loan, version: "v3" })), ]; - return allLoans; + return allLoans.sort((a, b) => { + const expiryA = Number(a.expiry?.toString() || 0); + const expiryB = Number(b.expiry?.toString() || 0); + return expiryA - expiryB; + }); }; const getActiveClearingHouse = () => { @@ -132,6 +136,8 @@ export const CoolerPositions = () => { clearingHouses.v2 && clearingHouses.v3); + const allLoansLoaded = isFetchedLoansV1 && isFetchedLoansV2 && isFetchedLoansV3; + return (
@@ -160,7 +166,7 @@ export const CoolerPositions = () => { )} - {allLoans.length === 0 && isFetchedLoansV1 && isFetchedLoansV2 && isFetchedLoansV3 && address && ( + {allLoans.length === 0 && allLoansLoaded && address && ( You currently have no Cooler loans @@ -183,13 +189,13 @@ export const CoolerPositions = () => { )} - {address && (!isFetchedLoansV1 || !isFetchedLoansV2 || !isFetchedLoansV3) && ( + {address && !allLoansLoaded && ( Loading your positions... )} - {allLoans.length > 0 && ( + {allLoans.length > 0 && allLoansLoaded && ( <>