diff --git a/.gitignore b/.gitignore index 03e1bb04..b120727b 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,5 @@ next-env.d.ts !.yarn/plugins !.yarn/sdks !.yarn/versions + +.cursor \ No newline at end of file diff --git a/app/positions/components/PositionsSummaryTable.tsx b/app/positions/components/PositionsSummaryTable.tsx index b97f0c27..a3fa7694 100644 --- a/app/positions/components/PositionsSummaryTable.tsx +++ b/app/positions/components/PositionsSummaryTable.tsx @@ -6,7 +6,7 @@ import Image from 'next/image'; import { BsQuestionCircle } from 'react-icons/bs'; import { IoRefreshOutline, IoChevronDownOutline } from 'react-icons/io5'; import { PiHandCoins } from 'react-icons/pi'; -import { PulseLoader} from 'react-spinners'; +import { PulseLoader } from 'react-spinners'; import { useAccount } from 'wagmi'; import { Button } from '@/components/common/Button'; import { TokenIcon } from '@/components/TokenIcon'; @@ -14,11 +14,11 @@ import { TooltipContent } from '@/components/TooltipContent'; import { useStyledToast } from '@/hooks/useStyledToast'; import { formatReadable, formatBalance } from '@/utils/balance'; import { getNetworkImg } from '@/utils/networks'; -import { - EarningsPeriod, +import { + EarningsPeriod, getGroupedEarnings, groupPositionsByLoanAsset, - processCollaterals + processCollaterals, } from '@/utils/positions'; import { MarketPosition, @@ -80,13 +80,15 @@ export function PositionsSummaryTable({ [EarningsPeriod.Month]: '30D', }; - const groupedPositions = useMemo(() => - groupPositionsByLoanAsset(marketPositions, rebalancerInfo), - [marketPositions, rebalancerInfo]); + const groupedPositions = useMemo( + () => groupPositionsByLoanAsset(marketPositions, rebalancerInfo), + [marketPositions, rebalancerInfo], + ); - const processedPositions = useMemo(() => - processCollaterals(groupedPositions), - [groupedPositions]); + const processedPositions = useMemo( + () => processCollaterals(groupedPositions), + [groupedPositions], + ); useEffect(() => { if (selectedGroupedPosition) { @@ -238,7 +240,9 @@ export function PositionsSummaryTable({ if (earnings === null) return '-'; return ( formatReadable( - Number(formatBalance(earnings, groupedPosition.loanAssetDecimals)), + Number( + formatBalance(earnings, groupedPosition.loanAssetDecimals), + ), ) + ' ' + groupedPosition.loanAsset diff --git a/app/positions/components/RebalanceModal.tsx b/app/positions/components/RebalanceModal.tsx index 1daf9945..118ccfdf 100644 --- a/app/positions/components/RebalanceModal.tsx +++ b/app/positions/components/RebalanceModal.tsx @@ -2,9 +2,9 @@ import React, { useState, useMemo, useCallback } from 'react'; import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter } from '@nextui-org/react'; import { GrRefresh } from 'react-icons/gr'; import { parseUnits, formatUnits } from 'viem'; -import { useAccount, useSwitchChain } from 'wagmi'; import { Button } from '@/components/common'; import { Spinner } from '@/components/common/Spinner'; +import { useMarketNetwork } from '@/hooks/useMarketNetwork'; import { useMarkets } from '@/hooks/useMarkets'; import { usePagination } from '@/hooks/usePagination'; import { useRebalance } from '@/hooks/useRebalance'; @@ -239,18 +239,18 @@ export function RebalanceModal({ resetSelections, ]); - const { chainId } = useAccount(); - const { switchChainAsync } = useSwitchChain(); - - const needSwitchChain = useMemo( - () => chainId !== groupedPosition.chainId, - [chainId, groupedPosition.chainId], - ); + // Use the market network hook for chain switching with direct chainId + const { needSwitchChain, switchToNetwork } = useMarketNetwork({ + targetChainId: groupedPosition.chainId, + }); const handleExecuteRebalance = useCallback(async () => { if (needSwitchChain) { try { - await switchChainAsync({ chainId: groupedPosition.chainId }); + // Call our switchToNetwork function + switchToNetwork(); + // Wait a bit for the network switch to complete + await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (error) { toast.error('Something went wrong', 'Failed to switch network. Please try again'); return; @@ -265,7 +265,7 @@ export function RebalanceModal({ } finally { setShowProcessModal(false); } - }, [executeRebalance, needSwitchChain, switchChainAsync, groupedPosition.chainId, toast]); + }, [executeRebalance, needSwitchChain, switchToNetwork, toast]); const handleManualRefresh = () => { refetch(() => { diff --git a/app/positions/components/onboarding/SetupPositions.tsx b/app/positions/components/onboarding/SetupPositions.tsx index 449d16f4..172b3654 100644 --- a/app/positions/components/onboarding/SetupPositions.tsx +++ b/app/positions/components/onboarding/SetupPositions.tsx @@ -3,21 +3,21 @@ import { Slider } from '@nextui-org/react'; import { LockClosedIcon, LockOpen1Icon } from '@radix-ui/react-icons'; import Image from 'next/image'; import { formatUnits, parseUnits } from 'viem'; -import { useChainId, useSwitchChain } from 'wagmi'; import { Button } from '@/components/common'; import { MarketInfoBlock } from '@/components/common/MarketInfoBlock'; import { SupplyProcessModal } from '@/components/SupplyProcessModal'; import { useLocalStorage } from '@/hooks/useLocalStorage'; +import { useMarketNetwork } from '@/hooks/useMarketNetwork'; import { useMultiMarketSupply } from '@/hooks/useMultiMarketSupply'; import { useStyledToast } from '@/hooks/useStyledToast'; import { useUserBalances } from '@/hooks/useUserBalances'; import { formatBalance } from '@/utils/balance'; +import { SupportedNetworks } from '@/utils/networks'; import { findToken } from '@/utils/tokens'; import { useOnboarding } from './OnboardingContext'; export function SetupPositions() { const toast = useStyledToast(); - const chainId = useChainId(); const { selectedToken, selectedMarkets, goToNextStep, goToPrevStep } = useOnboarding(); const { balances } = useUserBalances(); const [useEth] = useLocalStorage('useEth', false); @@ -29,12 +29,10 @@ export function SetupPositions() { const [error, setError] = useState(null); const [isSupplying, setIsSupplying] = useState(false); - const needSwitchChain = useMemo( - () => chainId !== selectedToken?.network, - [chainId, selectedToken], - ); - - const { switchChainAsync } = useSwitchChain(); + // Use our custom hook for network switching + const { needSwitchChain, switchToNetwork } = useMarketNetwork({ + targetChainId: selectedToken?.network ?? SupportedNetworks.Base, + }); // Compute token balance and decimals const tokenBalance = useMemo(() => { @@ -227,7 +225,9 @@ export function SetupPositions() { if (needSwitchChain && selectedToken) { try { - await switchChainAsync({ chainId: selectedToken.network }); + switchToNetwork(); + // Wait for network switch to complete + await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (switchError) { toast.error('Failed to switch network', 'Please try again'); return; diff --git a/app/rewards/components/RewardTable.tsx b/app/rewards/components/RewardTable.tsx index fefc771e..abe94714 100644 --- a/app/rewards/components/RewardTable.tsx +++ b/app/rewards/components/RewardTable.tsx @@ -1,21 +1,23 @@ 'use client'; -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { Table, TableHeader, TableBody, TableColumn, TableRow, TableCell } from '@nextui-org/table'; import Image from 'next/image'; import Link from 'next/link'; import { Address } from 'viem'; -import { useAccount, useSwitchChain } from 'wagmi'; +import { useAccount } from 'wagmi'; import { Button } from '@/components/common/Button'; import { TokenIcon } from '@/components/TokenIcon'; +import { useMarketNetwork } from '@/hooks/useMarketNetwork'; import { DistributionResponseType } from '@/hooks/useRewards'; import { useStyledToast } from '@/hooks/useStyledToast'; import { useTransactionWithToast } from '@/hooks/useTransactionWithToast'; import { formatBalance, formatSimple } from '@/utils/balance'; import { getAssetURL } from '@/utils/external'; -import { getNetworkImg } from '@/utils/networks'; +import { getNetworkImg, SupportedNetworks } from '@/utils/networks'; import { findToken } from '@/utils/tokens'; import { AggregatedRewardType } from '@/utils/types'; + type RewardTableProps = { account: string; rewards: AggregatedRewardType[]; @@ -30,8 +32,17 @@ export default function RewardTable({ showClaimed, }: RewardTableProps) { const { chainId } = useAccount(); - const { switchChainAsync } = useSwitchChain(); const toast = useStyledToast(); + const [targetChainId, setTargetChainId] = useState(chainId ?? SupportedNetworks.Mainnet); + + // Use our custom hook for network switching + const { switchToNetwork } = useMarketNetwork({ + targetChainId, + onNetworkSwitched: () => { + // Additional actions after network switch if needed + }, + }); + const { sendTransaction } = useTransactionWithToast({ toastId: 'claim', pendingText: 'Claiming Reward...', @@ -211,9 +222,11 @@ export default function RewardTable({ return; } if (chainId !== distribution.distributor.chain_id) { - await switchChainAsync({ - chainId: distribution.distributor.chain_id, - }); + // Set the target chain ID and switch + setTargetChainId(distribution.distributor.chain_id); + switchToNetwork(); + // Wait for network switch + await new Promise((resolve) => setTimeout(resolve, 1000)); } sendTransaction({ account: account as Address, diff --git a/src/components/Borrow/AddCollateralAndBorrow.tsx b/src/components/Borrow/AddCollateralAndBorrow.tsx index 933f6dd0..ddffa9e1 100644 --- a/src/components/Borrow/AddCollateralAndBorrow.tsx +++ b/src/components/Borrow/AddCollateralAndBorrow.tsx @@ -3,13 +3,14 @@ import { Switch } from '@nextui-org/react'; import { ReloadIcon } from '@radix-ui/react-icons'; import Image from 'next/image'; import { Address } from 'viem'; -import { useAccount, useSwitchChain } from 'wagmi'; +import { useAccount } from 'wagmi'; import { Button } from '@/components/common'; import { LTVWarning } from '@/components/common/LTVWarning'; import Input from '@/components/Input/Input'; import AccountConnect from '@/components/layout/header/AccountConnect'; import { useBorrowTransaction } from '@/hooks/useBorrowTransaction'; import { useLocalStorage } from '@/hooks/useLocalStorage'; +import { useMarketNetwork } from '@/hooks/useMarketNetwork'; import { useOraclePrice } from '@/hooks/useOraclePrice'; import { formatBalance, formatReadable } from '@/utils/balance'; import { ERC20Token } from '@/utils/tokens'; @@ -45,7 +46,7 @@ export function AddCollateralAndBorrow({ const [borrowInputError, setBorrowInputError] = useState(null); const [usePermit2Setting] = useLocalStorage('usePermit2', true); - const { isConnected, chainId } = useAccount(); + const { isConnected } = useAccount(); // Add a loading state for the refresh button const [isRefreshing, setIsRefreshing] = useState(false); @@ -57,7 +58,10 @@ export function AddCollateralAndBorrow({ const [currentLTV, setCurrentLTV] = useState(BigInt(0)); const [newLTV, setNewLTV] = useState(BigInt(0)); - const { switchChain } = useSwitchChain(); + // Use the market network hook for chain switching + const { needSwitchChain, switchToNetwork } = useMarketNetwork({ + targetChainId: market.morphoBlue.chain.id, + }); // Use the new hook for borrow transaction logic const { @@ -78,11 +82,6 @@ export function AddCollateralAndBorrow({ borrowAmount, }); - const needSwitchChain = useMemo( - () => chainId !== market.morphoBlue.chain.id, - [chainId, market.morphoBlue.chain.id], - ); - const { price: oraclePrice } = useOraclePrice({ oracle: market.oracleAddress as Address, chainId: market.morphoBlue.chain.id, @@ -391,11 +390,7 @@ export function AddCollateralAndBorrow({ ) : needSwitchChain ? ( - ) : (!permit2Authorized && !useEth) || (!usePermit2Setting && !isApproved) ? ( diff --git a/src/components/Borrow/WithdrawCollateralAndRepay.tsx b/src/components/Borrow/WithdrawCollateralAndRepay.tsx index 632b842d..8fa2c687 100644 --- a/src/components/Borrow/WithdrawCollateralAndRepay.tsx +++ b/src/components/Borrow/WithdrawCollateralAndRepay.tsx @@ -1,12 +1,13 @@ import React, { useMemo, useState, useEffect, useCallback } from 'react'; import { ReloadIcon } from '@radix-ui/react-icons'; import Image from 'next/image'; -import { useAccount, useSwitchChain } from 'wagmi'; +import { useAccount } from 'wagmi'; import { Button } from '@/components/common'; import { LTVWarning } from '@/components/common/LTVWarning'; import Input from '@/components/Input/Input'; import AccountConnect from '@/components/layout/header/AccountConnect'; import { RepayProcessModal } from '@/components/RepayProcessModal'; +import { useMarketNetwork } from '@/hooks/useMarketNetwork'; import { useOraclePrice } from '@/hooks/useOraclePrice'; import { useRepayTransaction } from '@/hooks/useRepayTransaction'; import { formatBalance, formatReadable } from '@/utils/balance'; @@ -44,7 +45,7 @@ export function WithdrawCollateralAndRepay({ const [withdrawInputError, setWithdrawInputError] = useState(null); const [repayInputError, setRepayInputError] = useState(null); - const { isConnected, chainId } = useAccount(); + const { isConnected } = useAccount(); // Add a loading state for the refresh button const [isRefreshing, setIsRefreshing] = useState(false); @@ -56,7 +57,10 @@ export function WithdrawCollateralAndRepay({ const [currentLTV, setCurrentLTV] = useState(BigInt(0)); const [newLTV, setNewLTV] = useState(BigInt(0)); - const { switchChain } = useSwitchChain(); + // Use the market network hook for chain switching + const { needSwitchChain, switchToNetwork } = useMarketNetwork({ + targetChainId: market.morphoBlue.chain.id, + }); // Use the repay transaction hook const { @@ -91,11 +95,6 @@ export function WithdrawCollateralAndRepay({ } }, [repayAssets, currentPosition]); - const needSwitchChain = useMemo( - () => chainId !== market.morphoBlue.chain.id, - [chainId, market.morphoBlue.chain.id], - ); - const { price: oraclePrice } = useOraclePrice({ oracle: market.oracleAddress as `0x${string}`, chainId: market.morphoBlue.chain.id, @@ -379,11 +378,7 @@ export function WithdrawCollateralAndRepay({ ) : needSwitchChain ? ( - ) : ( diff --git a/src/components/supplyModal.tsx b/src/components/supplyModal.tsx index 5a537fe8..cfe129e4 100644 --- a/src/components/supplyModal.tsx +++ b/src/components/supplyModal.tsx @@ -1,312 +1,58 @@ -import React, { useCallback, useMemo, useState } from 'react'; +import React from 'react'; import { Switch } from '@nextui-org/react'; import { Cross1Icon, ExternalLinkIcon } from '@radix-ui/react-icons'; import Image from 'next/image'; -import { Address, encodeFunctionData } from 'viem'; -import { useAccount, useBalance, useSwitchChain } from 'wagmi'; -import morphoBundlerAbi from '@/abis/bundlerV2'; +import { useAccount } from 'wagmi'; import Input from '@/components/Input/Input'; import AccountConnect from '@/components/layout/header/AccountConnect'; -import { useERC20Approval } from '@/hooks/useERC20Approval'; import { useLocalStorage } from '@/hooks/useLocalStorage'; -import { usePermit2 } from '@/hooks/usePermit2'; -import { useStyledToast } from '@/hooks/useStyledToast'; -import { useTransactionWithToast } from '@/hooks/useTransactionWithToast'; -import { useUserMarketsCache } from '@/hooks/useUserMarketsCache'; +import { useMarketNetwork } from '@/hooks/useMarketNetwork'; +import { useSupplyMarket } from '@/hooks/useSupplyMarket'; import { formatBalance, formatReadable } from '@/utils/balance'; import { getExplorerURL } from '@/utils/external'; -import { getBundlerV2, getIRMTitle, MONARCH_TX_IDENTIFIER } from '@/utils/morpho'; +import { getIRMTitle } from '@/utils/morpho'; import { findToken } from '@/utils/tokens'; import { Market } from '@/utils/types'; import { Button } from './common'; import { MarketInfoBlock } from './common/MarketInfoBlock'; import OracleVendorBadge from './OracleVendorBadge'; import { SupplyProcessModal } from './SupplyProcessModal'; + type SupplyModalProps = { market: Market; onClose: () => void; }; export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element { - // Add state for the supply amount and using ETH - const [supplyAmount, setSupplyAmount] = useState(BigInt(0)); - const [inputError, setInputError] = useState(null); - const [useEth, setUseEth] = useState(false); - const [showProcessModal, setShowProcessModal] = useState(false); - const [currentStep, setCurrentStep] = useState<'approve' | 'signing' | 'supplying'>('approve'); const [usePermit2Setting] = useLocalStorage('usePermit2', true); - - const { batchAddUserMarkets } = useUserMarketsCache(); - - const { address: account, isConnected, chainId } = useAccount(); - + const { isConnected } = useAccount(); const loanToken = findToken(market.loanAsset.address, market.morphoBlue.chain.id); - const { switchChain } = useSwitchChain(); - - const toast = useStyledToast(); - - const { data: tokenBalance } = useBalance({ - token: market.loanAsset.address as `0x${string}`, - address: account, - chainId: market.morphoBlue.chain.id, - }); - - const { data: ethBalance } = useBalance({ - address: account, - chainId: market.morphoBlue.chain.id, - }); - - // get allowance for morpho + // Use the hook to handle all supply logic const { - authorizePermit2, - permit2Authorized, - isLoading: isLoadingPermit2, - signForBundlers, - } = usePermit2({ - user: account as `0x${string}`, - spender: getBundlerV2(market.morphoBlue.chain.id), - token: market.loanAsset.address as `0x${string}`, - refetchInterval: 10000, - chainId: market.morphoBlue.chain.id, - tokenSymbol: market.loanAsset.symbol, - amount: supplyAmount, - }); - - const { isApproved, approve } = useERC20Approval({ - token: market.loanAsset.address as Address, - spender: getBundlerV2(market.morphoBlue.chain.id), - amount: supplyAmount, - tokenSymbol: market.loanAsset.symbol, - }); - - const needSwitchChain = useMemo( - () => chainId !== market.morphoBlue.chain.id, - [chainId, market.morphoBlue.chain.id], - ); - - const { isConfirming: supplyPending, sendTransactionAsync } = useTransactionWithToast({ - toastId: 'supply', - pendingText: `Supplying ${formatBalance(supplyAmount, market.loanAsset.decimals)} ${ - market.loanAsset.symbol - }`, - successText: `${market.loanAsset.symbol} Supplied`, - errorText: 'Failed to supply', - chainId, - pendingDescription: `Supplying to market ${market.uniqueKey.slice(2, 8)}...`, - successDescription: `Successfully supplied to market ${market.uniqueKey.slice(2, 8)}`, - }); - - const executeSupplyTransaction = useCallback(async () => { - try { - const txs: `0x${string}`[] = []; - if (useEth) { - txs.push( - encodeFunctionData({ - abi: morphoBundlerAbi, - functionName: 'wrapNative', - args: [supplyAmount], - }), - ); - } else if (usePermit2Setting) { - const { sigs, permitSingle } = await signForBundlers(); - console.log('Signed for bundlers:', { sigs, permitSingle }); - - const tx1 = encodeFunctionData({ - abi: morphoBundlerAbi, - functionName: 'approve2', - args: [permitSingle, sigs, false], - }); - - // transferFrom with permit2 - const tx2 = encodeFunctionData({ - abi: morphoBundlerAbi, - functionName: 'transferFrom2', - args: [market.loanAsset.address as Address, supplyAmount], - }); - - txs.push(tx1); - txs.push(tx2); - } else { - // For standard ERC20 flow, we only need to transfer the tokens - txs.push( - encodeFunctionData({ - abi: morphoBundlerAbi, - functionName: 'erc20TransferFrom', - args: [market.loanAsset.address as Address, supplyAmount], - }), - ); - } - - setCurrentStep('supplying'); - - const minShares = BigInt(1); - const morphoSupplyTx = encodeFunctionData({ - abi: morphoBundlerAbi, - functionName: 'morphoSupply', - args: [ - { - loanToken: market.loanAsset.address as Address, - collateralToken: market.collateralAsset.address as Address, - oracle: market.oracleAddress as Address, - irm: market.irmAddress as Address, - lltv: BigInt(market.lltv), - }, - supplyAmount, - BigInt(0), - minShares, - account as `0x${string}`, - '0x', // callback - ], - }); - - txs.push(morphoSupplyTx); - - // add timeout here to prevent rabby reverting - await new Promise((resolve) => setTimeout(resolve, 800)); - - await sendTransactionAsync({ - account, - to: getBundlerV2(market.morphoBlue.chain.id), - data: (encodeFunctionData({ - abi: morphoBundlerAbi, - functionName: 'multicall', - args: [txs], - }) + MONARCH_TX_IDENTIFIER) as `0x${string}`, - value: useEth ? supplyAmount : 0n, - }); - - batchAddUserMarkets([ - { - marketUniqueKey: market.uniqueKey, - chainId: market.morphoBlue.chain.id, - }, - ]); - - // come back to main supply page - setShowProcessModal(false); - } catch (error: unknown) { - setShowProcessModal(false); - toast.error('Supply Failed', 'Supply to market failed or cancelled'); - } - }, [ - account, - market, supplyAmount, - sendTransactionAsync, + setSupplyAmount, + inputError, + setInputError, useEth, - signForBundlers, - usePermit2Setting, - toast, - ]); - - const approveAndSupply = useCallback(async () => { - if (!account) { - toast.info('No account connected', 'Please connect your wallet to continue.'); - return; - } - - try { - setShowProcessModal(true); - setCurrentStep('approve'); - - if (useEth) { - setCurrentStep('supplying'); - await executeSupplyTransaction(); - return; - } - - if (usePermit2Setting) { - // Permit2 flow - try { - await authorizePermit2(); - setCurrentStep('signing'); - - // Small delay to prevent UI glitches - await new Promise((resolve) => setTimeout(resolve, 500)); - - await executeSupplyTransaction(); - } catch (error: unknown) { - console.error('Error in Permit2 flow:', error); - if (error instanceof Error) { - if (error.message.includes('User rejected')) { - toast.error('Transaction rejected', 'Transaction rejected by user'); - } else { - toast.error('Error', 'Failed to process Permit2 transaction'); - } - } else { - toast.error('Error', 'An unexpected error occurred'); - } - throw error; - } - return; - } - - // Standard ERC20 flow - if (!isApproved) { - try { - await approve(); - setCurrentStep('supplying'); - - // Small delay to prevent UI glitches - await new Promise((resolve) => setTimeout(resolve, 1000)); - } catch (error: unknown) { - console.error('Error in approval:', error); - if (error instanceof Error) { - if (error.message.includes('User rejected')) { - toast.error('Transaction rejected', 'Approval rejected by user'); - } else { - toast.error('Transaction Error', 'Failed to approve token'); - } - } else { - toast.error('Transaction Error', 'An unexpected error occurred during approval'); - } - throw error; - } - } else { - setCurrentStep('supplying'); - } - - await executeSupplyTransaction(); - } catch (error: unknown) { - console.error('Error in approveAndSupply:', error); - setShowProcessModal(false); - } - }, [ - account, - authorizePermit2, - executeSupplyTransaction, - useEth, - usePermit2Setting, + setUseEth, + showProcessModal, + setShowProcessModal, + currentStep, + tokenBalance, + ethBalance, isApproved, - approve, - toast, - ]); - - const signAndSupply = useCallback(async () => { - if (!account) { - toast.info('No account connected', 'Please connect your wallet to continue.'); - return; - } - - try { - setShowProcessModal(true); - setCurrentStep('signing'); - await executeSupplyTransaction(); - } catch (error: unknown) { - console.error('Error in signAndSupply:', error); - setShowProcessModal(false); - if (error instanceof Error) { - if (error.message.includes('User rejected')) { - toast.error('Transaction rejected', 'Transaction rejected by user'); - } else { - toast.error('Transaction Error', 'Failed to process transaction'); - } - } else { - toast.error('Transaction Error', 'An unexpected error occurred'); - } - } - }, [account, executeSupplyTransaction, toast]); + permit2Authorized, + isLoadingPermit2, + supplyPending, + approveAndSupply, + signAndSupply, + } = useSupplyMarket(market); + + // Use the market network hook to handle network switching + const { needSwitchChain, switchToNetwork } = useMarketNetwork({ + targetChainId: market.morphoBlue.chain.id, + }); return (

{useEth - ? formatBalance(ethBalance?.value ? ethBalance.value : '0', 18) - : formatBalance( - tokenBalance?.value ? tokenBalance.value : '0', - market.loanAsset.decimals, - )} + ? formatBalance(ethBalance ?? BigInt(0), 18) + : formatBalance(tokenBalance ?? BigInt(0), market.loanAsset.decimals)}

{useEth ? 'ETH' : market.loanAsset.symbol}{' '} @@ -470,15 +213,7 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element

{needSwitchChain ? ( - ) : (!permit2Authorized && !useEth) || (!usePermit2Setting && !isApproved) ? ( diff --git a/src/components/withdrawModal.tsx b/src/components/withdrawModal.tsx index ed8b5daf..e47c7f19 100644 --- a/src/components/withdrawModal.tsx +++ b/src/components/withdrawModal.tsx @@ -1,14 +1,15 @@ // Import the necessary hooks -import { useCallback, useMemo, useState } from 'react'; +import { useCallback, useState } from 'react'; import { Cross1Icon } from '@radix-ui/react-icons'; import Image from 'next/image'; import { Address, encodeFunctionData } from 'viem'; -import { useAccount, useSwitchChain } from 'wagmi'; +import { useAccount } from 'wagmi'; import morphoAbi from '@/abis/morpho'; import { MarketInfoBlock } from '@/components/common/MarketInfoBlock'; import Input from '@/components/Input/Input'; import AccountConnect from '@/components/layout/header/AccountConnect'; +import { useMarketNetwork } from '@/hooks/useMarketNetwork'; import { useStyledToast } from '@/hooks/useStyledToast'; import { useTransactionWithToast } from '@/hooks/useTransactionWithToast'; import { formatBalance, formatReadable, min } from '@/utils/balance'; @@ -35,12 +36,10 @@ export function WithdrawModal({ position, onClose, refetch }: ModalProps): JSX.E position.market.morphoBlue.chain.id, ); - const needSwitchChain = useMemo( - () => chainId !== position.market.morphoBlue.chain.id, - [chainId, position.market.morphoBlue.chain.id], - ); - - const { switchChain } = useSwitchChain(); + // Use the market network hook for chain switching + const { needSwitchChain, switchToNetwork } = useMarketNetwork({ + targetChainId: position.market.morphoBlue.chain.id, + }); const { isConfirming, sendTransaction } = useTransactionWithToast({ toastId: 'withdraw', @@ -196,7 +195,7 @@ export function WithdrawModal({ position, onClose, refetch }: ModalProps): JSX.E {needSwitchChain ? (