From efe588539e9bf254007125b8f3948839f44b07a4 Mon Sep 17 00:00:00 2001 From: brightiron Date: Tue, 14 Jan 2025 21:23:39 -0600 Subject: [PATCH 1/2] support eoa migration --- .../Cooler/hooks/useConsolidateCooler.tsx | 35 +++++--- .../Cooler/positions/ConsolidateLoan.tsx | 89 ++++++++++++++++++- 2 files changed, 111 insertions(+), 13 deletions(-) diff --git a/src/views/Lending/Cooler/hooks/useConsolidateCooler.tsx b/src/views/Lending/Cooler/hooks/useConsolidateCooler.tsx index a7b1c7136..1aa5dfb07 100644 --- a/src/views/Lending/Cooler/hooks/useConsolidateCooler.tsx +++ b/src/views/Lending/Cooler/hooks/useConsolidateCooler.tsx @@ -20,26 +20,41 @@ export const useConsolidateCooler = () => { fromClearingHouseAddress, toClearingHouseAddress, loanIds, + newOwner, }: { fromCoolerAddress: string; toCoolerAddress: string; fromClearingHouseAddress: string; toClearingHouseAddress: string; loanIds: number[]; + newOwner?: boolean; }) => { if (!signer) throw new Error(`Please connect a wallet`); const contractAddress = COOLER_CONSOLIDATION_CONTRACT.addresses[networks.MAINNET]; const contract = CoolerConsolidation__factory.connect(contractAddress, signer); - const cooler = await contract.consolidate( - fromClearingHouseAddress, - toClearingHouseAddress, - fromCoolerAddress, - toCoolerAddress, - loanIds, - { - gasLimit: loanIds.length <= 15 ? loanIds.length * 2000000 : 30000000, - }, - ); + + const cooler = newOwner + ? await contract.consolidateWithNewOwner( + fromClearingHouseAddress, + toClearingHouseAddress, + fromCoolerAddress, + toCoolerAddress, + loanIds, + { + gasLimit: loanIds.length <= 15 ? loanIds.length * 2000000 : 30000000, + }, + ) + : await contract.consolidate( + fromClearingHouseAddress, + toClearingHouseAddress, + fromCoolerAddress, + toCoolerAddress, + loanIds, + { + gasLimit: loanIds.length <= 15 ? loanIds.length * 2000000 : 30000000, + }, + ); + const receipt = await cooler.wait(); return receipt; }, diff --git a/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx b/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx index a58f2b277..4caa15cee 100644 --- a/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx +++ b/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx @@ -1,4 +1,9 @@ +import { isAddress } from "@ethersproject/address"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { + Accordion, + AccordionDetails, + AccordionSummary, Box, Divider, FormControl, @@ -9,7 +14,7 @@ import { Tooltip, Typography, } from "@mui/material"; -import { InfoNotification, Modal, PrimaryButton } from "@olympusdao/component-library"; +import { InfoNotification, Input, Modal, PrimaryButton } from "@olympusdao/component-library"; import { BigNumber } from "ethers"; import { formatEther } from "ethers/lib/utils.js"; import { useEffect, useState } from "react"; @@ -26,6 +31,7 @@ import { useConsolidateCooler } from "src/views/Lending/Cooler/hooks/useConsolid 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 { useGetCoolerForWallet } from "src/views/Lending/Cooler/hooks/useGetCoolerForWallet"; import { useGetCoolerLoans } from "src/views/Lending/Cooler/hooks/useGetCoolerLoans"; import { useGetWalletFundsRequired } from "src/views/Lending/Cooler/hooks/useGetWalletFundsRequired"; @@ -57,6 +63,8 @@ export const ConsolidateLoans = ({ const createCooler = useCreateCooler(); const networks = useTestableNetworks(); const [open, setOpen] = useState(false); + const [newOwnerAddress, setNewOwnerAddress] = useState(""); + const [isValidAddress, setIsValidAddress] = useState(true); // Determine which versions are available for consolidation const hasV1Loans = v1Loans && v1Loans.length > 0; @@ -152,20 +160,29 @@ export const ConsolidateLoans = ({ setSelectedVersion(event.target.value as "v1" | "v2" | "v3"); }; + const handleAddressChange = (event: React.ChangeEvent) => { + const address = event.target.value; + setNewOwnerAddress(address); + setIsValidAddress(address === "" || isAddress(address)); + }; + const handleConsolidate = () => { if (!coolerAddress || !v3CoolerAddress) return; + coolerMutation.mutate( { fromCoolerAddress: coolerAddress, - toCoolerAddress: v3CoolerAddress, + toCoolerAddress: newOwnerAddress && isValidAddress ? newOwnerCoolerAddress || "" : v3CoolerAddress, fromClearingHouseAddress: selectedClearingHouse.clearingHouseAddress, toClearingHouseAddress: clearingHouseAddresses.v3.clearingHouseAddress, loanIds, + newOwner: Boolean(newOwnerAddress && isValidAddress && newOwnerCoolerAddress), }, { onSuccess: () => { setOpen(false); setSelectedVersion(""); + setNewOwnerAddress(""); }, }, ); @@ -173,6 +190,22 @@ export const ConsolidateLoans = ({ const needsCoolerCreation = !v3CoolerAddress; + // Add this hook alongside other hooks + const { data: newOwnerCoolerAddress, isFetched: isFetchedNewOwnerCoolerAddress } = useGetCoolerForWallet({ + walletAddress: isValidAddress ? newOwnerAddress : undefined, + factoryAddress: clearingHouseAddresses.v3?.factory, + collateralAddress: clearingHouseAddresses.v3?.collateralAddress, + debtAddress: clearingHouseAddresses.v3?.debtAddress, + clearingHouseVersion: "clearingHouseV3", + }); + + const getButtonText = () => { + if (newOwnerAddress && isValidAddress && newOwnerCoolerAddress) { + return `Transfer & Consolidate Loans`; + } + return `Consolidate Loans`; + }; + if (!showConsolidateButton) return null; return ( @@ -373,6 +406,56 @@ export const ConsolidateLoans = ({ + {selectedVersion && ( + <> + + + + + } + sx={{ + padding: 0, + "& .MuiAccordionSummary-content": { + margin: 0, + }, + }} + > + Transfer Ownership (Optional) + + + + + + + + + )} )} {selectedVersion && !insufficientBalances?.debt && !insufficientBalances?.gohm ? ( @@ -417,7 +500,7 @@ export const ConsolidateLoans = ({ disabled={coolerMutation.isLoading || !selectedVersion} fullWidth > - Consolidate Loans + {getButtonText()} From e6a9c565180c4483b77cded76064f0373787ec0b Mon Sep 17 00:00:00 2001 From: brightiron Date: Fri, 17 Jan 2025 19:47:03 -0600 Subject: [PATCH 2/2] additional validation --- .../Cooler/positions/ConsolidateLoan.tsx | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx b/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx index 4caa15cee..4c0a1d0d7 100644 --- a/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx +++ b/src/views/Lending/Cooler/positions/ConsolidateLoan.tsx @@ -1,4 +1,4 @@ -import { isAddress } from "@ethersproject/address"; +import { getAddress } from "@ethersproject/address"; import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; import { Accordion, @@ -34,6 +34,7 @@ import { useGetConsolidationAllowances } from "src/views/Lending/Cooler/hooks/us import { useGetCoolerForWallet } from "src/views/Lending/Cooler/hooks/useGetCoolerForWallet"; import { useGetCoolerLoans } from "src/views/Lending/Cooler/hooks/useGetCoolerLoans"; import { useGetWalletFundsRequired } from "src/views/Lending/Cooler/hooks/useGetWalletFundsRequired"; +import { useAccount } from "wagmi"; export const ConsolidateLoans = ({ v3CoolerAddress, @@ -63,6 +64,7 @@ export const ConsolidateLoans = ({ const createCooler = useCreateCooler(); const networks = useTestableNetworks(); const [open, setOpen] = useState(false); + const { address } = useAccount(); const [newOwnerAddress, setNewOwnerAddress] = useState(""); const [isValidAddress, setIsValidAddress] = useState(true); @@ -161,9 +163,19 @@ export const ConsolidateLoans = ({ }; const handleAddressChange = (event: React.ChangeEvent) => { - const address = event.target.value; - setNewOwnerAddress(address); - setIsValidAddress(address === "" || isAddress(address)); + const inputAddress = event.target.value; + setNewOwnerAddress(inputAddress); + + try { + const checksummedAddress = getAddress(inputAddress.toLowerCase()); + console.log("checksummedAddress", checksummedAddress); + // First check if empty or valid address, then check if it's not the current owner + const isValid = inputAddress === "" || checksummedAddress; + const isNotCurrentOwner = !address || !inputAddress || inputAddress.toLowerCase() !== address.toLowerCase(); + setIsValidAddress(isValid && isNotCurrentOwner); + } catch { + setIsValidAddress(false); + } }; const handleConsolidate = () => { @@ -190,7 +202,6 @@ export const ConsolidateLoans = ({ const needsCoolerCreation = !v3CoolerAddress; - // Add this hook alongside other hooks const { data: newOwnerCoolerAddress, isFetched: isFetchedNewOwnerCoolerAddress } = useGetCoolerForWallet({ walletAddress: isValidAddress ? newOwnerAddress : undefined, factoryAddress: clearingHouseAddresses.v3?.factory, @@ -444,11 +455,13 @@ export const ConsolidateLoans = ({ Boolean(newOwnerAddress && !newOwnerCoolerAddress && isFetchedNewOwnerCoolerAddress) } helperText={ - !isValidAddress - ? "Invalid Ethereum address" - : newOwnerAddress && !newOwnerCoolerAddress && isFetchedNewOwnerCoolerAddress - ? "This EOA must generate a V3 Cooler before you can consolidate to it" - : "" + !isValidAddress && newOwnerAddress.toLowerCase() === address?.toLowerCase() + ? "New owner cannot be the current owner" + : !isValidAddress + ? "Invalid Ethereum address" + : newOwnerAddress && !newOwnerCoolerAddress && isFetchedNewOwnerCoolerAddress + ? "This EOA must generate a V3 Cooler before you can consolidate to it" + : "" } /> @@ -497,7 +510,12 @@ export const ConsolidateLoans = ({ {getButtonText()}