Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions src/components/providers/ClientProviders.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import type { ReactNode } from 'react';
import { GlobalModalProvider } from '@/contexts/GlobalModalContext';
import { LiquidationsProvider } from '@/contexts/LiquidationsContext';
import { MerklCampaignsProvider } from '@/contexts/MerklCampaignsContext';
import { OracleDataProvider } from '@/contexts/OracleDataContext';
import { OnboardingProvider } from '@/features/positions/components/onboarding/onboarding-context';
Expand All @@ -17,11 +16,9 @@ export function ClientProviders({ children }: ClientProvidersProps) {
<GlobalModalProvider>
<TokenProvider>
<OracleDataProvider>
<LiquidationsProvider>
<MerklCampaignsProvider>
<OnboardingProvider>{children}</OnboardingProvider>
</MerklCampaignsProvider>
</LiquidationsProvider>
<MerklCampaignsProvider>
<OnboardingProvider>{children}</OnboardingProvider>
</MerklCampaignsProvider>
</OracleDataProvider>
</TokenProvider>
</GlobalModalProvider>
Expand Down
41 changes: 0 additions & 41 deletions src/contexts/LiquidationsContext.tsx

This file was deleted.

8 changes: 4 additions & 4 deletions src/features/markets/components/market-indicators.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Tooltip } from '@/components/ui/tooltip';
import { FaShieldAlt, FaStar, FaUser } from 'react-icons/fa';
import { FiAlertCircle } from 'react-icons/fi';
import { TooltipContent } from '@/components/shared/tooltip-content';
import { useLiquidationsContext } from '@/contexts/LiquidationsContext';
import { useLiquidationsQuery } from '@/hooks/queries/useLiquidationsQuery';
import { computeMarketWarnings } from '@/hooks/useMarketWarnings';
import type { Market } from '@/utils/types';
import { RewardsIndicator } from '@/features/markets/components/rewards-indicator';
Expand All @@ -17,9 +17,9 @@ type MarketIndicatorsProps = {
};

export function MarketIndicators({ market, showRisk = false, isStared = false, hasUserPosition = false }: MarketIndicatorsProps) {
// Check liquidation protection status on-demand (like Merkl rewards pattern)
const { isProtectedByLiquidationBots } = useLiquidationsContext();
const hasLiquidationProtection = isProtectedByLiquidationBots(market.uniqueKey);
// Check liquidation protection status using React Query
const { data: liquidatedMarkets } = useLiquidationsQuery();
const hasLiquidationProtection = liquidatedMarkets?.has(market.uniqueKey) ?? false;

// Compute risk warnings if needed
const warnings = showRisk ? computeMarketWarnings(market, true) : [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
import { useState, useEffect, useCallback } from 'react';
import { useQuery } from '@tanstack/react-query';
import { supportsMorphoApi } from '@/config/dataSources';
import { fetchMorphoApiLiquidatedMarketKeys } from '@/data-sources/morpho-api/liquidations';
import { fetchSubgraphLiquidatedMarketKeys } from '@/data-sources/subgraph/liquidations';
import { ALL_SUPPORTED_NETWORKS } from '@/utils/networks';

const useLiquidations = () => {
const [loading, setLoading] = useState(true);
const [isRefetching, setIsRefetching] = useState(false);
const [liquidatedMarketKeys, setLiquidatedMarketKeys] = useState<Set<string>>(new Set());
const [error, setError] = useState<unknown | null>(null);
/**
* Fetches liquidated market IDs from all supported networks using React Query.
*
* Data fetching strategy:
* - Tries Morpho API first (if supported)
* - Falls back to Subgraph if API fails
* - Combines liquidated market keys from all networks
*
* Cache behavior:
* - staleTime: 10 minutes (data considered fresh)
* - Auto-refetch: Every 10 minutes in background
* - Refetch on window focus: enabled
*
* @example
* ```tsx
* const { data, isLoading, refetch } = useLiquidationsQuery();
* const isProtected = data?.has(marketId) ?? false;
* ```
*/
export const useLiquidationsQuery = () => {
return useQuery({
queryKey: ['liquidations'],
queryFn: async () => {
const combinedLiquidatedKeys = new Set<string>();
const fetchErrors: unknown[] = [];

const fetchLiquidations = useCallback(async (isRefetch = false) => {
if (isRefetch) {
setIsRefetching(true);
} else {
setLoading(true);
}
setError(null); // Reset error

// Define the networks to check for liquidations

const combinedLiquidatedKeys = new Set<string>();
const fetchErrors: unknown[] = [];

try {
await Promise.all(
ALL_SUPPORTED_NETWORKS.map(async (network) => {
try {
Expand All @@ -37,7 +43,6 @@ const useLiquidations = () => {
networkLiquidatedKeys = await fetchMorphoApiLiquidatedMarketKeys(network);
} catch (morphoError) {
console.error('Failed to fetch liquidated markets via Morpho API:', morphoError);
// Continue to Subgraph fallback
networkLiquidatedKeys = new Set();
trySubgraph = true;
}
Expand All @@ -53,7 +58,7 @@ const useLiquidations = () => {
networkLiquidatedKeys = await fetchSubgraphLiquidatedMarketKeys(network);
} catch (subgraphError) {
console.error('Failed to fetch liquidated markets via Subgraph:', subgraphError);
throw subgraphError; // Throw to be caught by outer catch
throw subgraphError;
}
}

Expand All @@ -66,37 +71,15 @@ const useLiquidations = () => {
}),
);

setLiquidatedMarketKeys(combinedLiquidatedKeys);

// If any network fetch failed, log but still return what we got
if (fetchErrors.length > 0) {
setError(fetchErrors[0]);
console.warn(`Failed to fetch liquidations from ${fetchErrors.length} network(s)`, fetchErrors[0]);
}
} catch (err) {
console.error('Error fetching liquidated markets:', err);
setError(err);
} finally {
if (isRefetch) {
setIsRefetching(false);
} else {
setLoading(false);
}
}
}, []);

useEffect(() => {
fetchLiquidations().catch((err) => {
console.error('Error in fetchLiquidations effect:', err);
// Explicitly catch and handle - prevents React error boundary from triggering
});
}, [fetchLiquidations]);

return {
loading,
isRefetching,
liquidatedMarketKeys,
error,
refetch: async () => fetchLiquidations(true),
};
return combinedLiquidatedKeys;
},
staleTime: 10 * 60 * 1000, // Data is fresh for 10 minutes
refetchInterval: 10 * 60 * 1000, // Auto-refetch every 10 minutes
refetchOnWindowFocus: true, // Refetch when user returns to tab
});
};

export default useLiquidations;
17 changes: 13 additions & 4 deletions src/hooks/useTransactionWithToast.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import { toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { useSendTransaction, useWaitForTransactionReceipt } from 'wagmi';
Expand Down Expand Up @@ -36,6 +36,15 @@ export function useTransactionWithToast({
hash,
});

// Use a ref to store the latest onSuccess callback without it being in the dependency array
// This prevents infinite loops when the callback is recreated on every render
const onSuccessRef = useRef(onSuccess);

// Update the ref whenever onSuccess changes
useEffect(() => {
onSuccessRef.current = onSuccess;
}, [onSuccess]);

const onClick = useCallback(() => {
if (hash) {
// if chainId is not supported, use 1
Expand Down Expand Up @@ -77,8 +86,8 @@ export function useTransactionWithToast({
onClick,
closeButton: true,
});
if (onSuccess) {
onSuccess();
if (onSuccessRef.current) {
onSuccessRef.current();
}
}
if (isError || txError) {
Expand All @@ -96,7 +105,7 @@ export function useTransactionWithToast({
closeButton: true,
});
}
}, [hash, isConfirmed, isError, txError, successText, successDescription, errorText, toastId, onClick, onSuccess]);
}, [hash, isConfirmed, isError, txError, successText, successDescription, errorText, toastId, onClick]);

return { sendTransactionAsync, sendTransaction, isConfirming, isConfirmed };
}
3 changes: 1 addition & 2 deletions src/modals/wrap-process-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { FaCheckCircle, FaCircle } from 'react-icons/fa';
import { LuArrowRightLeft } from 'react-icons/lu';
import { Modal, ModalBody, ModalHeader } from '@/components/common/Modal';
import type { WrapStep } from '@/hooks/useWrapLegacyMorpho';
import { formatBalance } from '@/utils/balance';

type WrapProcessModalProps = {
amount: bigint;
Expand Down Expand Up @@ -38,7 +37,7 @@ export function WrapProcessModal({ amount, currentStep, onOpenChange }: WrapProc
backdrop="blur"
>
<ModalHeader
title={`Wrapping ${formatBalance(amount, 18)} MORPHO`}
title="Wrapping MORPHO"
Comment thread
antoncoding marked this conversation as resolved.
description="Track each step to move legacy MORPHO into the new token"
mainIcon={<LuArrowRightLeft className="h-5 w-5" />}
/>
Expand Down