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
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ Monarch allow users to choose which Morpho vaults they trust, and use them as fi

**Registry**: `src/contexts/VaultRegistryContext.tsx`

**Hook**: `useAllMorphoVaults()` in `src/hooks/useAllMorphoVaults.ts`
**Hook**: `useAllMorphoVaultsQuery()` in `src/hooks/queries/useAllMorphoVaultsQuery.ts`
- Fetches all vaults from Morpho API
- Caches with TanStack Query

Expand Down
4 changes: 2 additions & 2 deletions src/contexts/VaultRegistryContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { createContext, useContext, useMemo, type ReactNode } from 'react';
import type { MorphoVault } from '@/data-sources/morpho-api/vaults';
import { useAllMorphoVaults } from '@/hooks/useAllMorphoVaults';
import { useAllMorphoVaultsQuery } from '@/hooks/queries/useAllMorphoVaultsQuery';
import type { Address } from 'viem';

type VaultRegistryContextType = {
Expand All @@ -15,7 +15,7 @@ type VaultRegistryContextType = {
const VaultRegistryContext = createContext<VaultRegistryContextType | undefined>(undefined);

export function VaultRegistryProvider({ children }: { children: ReactNode }) {
const { vaults, loading, error } = useAllMorphoVaults();
const { data: vaults = [], isLoading: loading, error } = useAllMorphoVaultsQuery();

const getVaultByAddress = useMemo(
() => (address: Address, chainId?: number) => {
Expand Down
2 changes: 1 addition & 1 deletion src/data-sources/morpho-api/transactions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { userTransactionsQuery } from '@/graphql/morpho-api-queries';
import type { TransactionFilters, TransactionResponse } from '@/hooks/useUserTransactions';
import type { TransactionFilters, TransactionResponse } from '@/hooks/queries/fetchUserTransactions';
import { SupportedNetworks } from '@/utils/networks';
import { morphoGraphqlFetcher } from './fetchers';

Expand Down
2 changes: 1 addition & 1 deletion src/data-sources/subgraph/transactions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { subgraphUserTransactionsQuery } from '@/graphql/morpho-subgraph-queries';
import type { TransactionFilters, TransactionResponse } from '@/hooks/useUserTransactions';
import type { TransactionFilters, TransactionResponse } from '@/hooks/queries/fetchUserTransactions';
import type { SupportedNetworks } from '@/utils/networks';
import { getSubgraphUrl } from '@/utils/subgraph-urls';
import { type UserTransaction, UserTxTypes } from '@/utils/types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button';
import { ExecuteTransactionButton } from '@/components/ui/ExecuteTransactionButton';
import { Modal, ModalBody, ModalHeader } from '@/components/common/Modal';
import { useProcessedMarkets } from '@/hooks/useProcessedMarkets';
import { useUserBalances } from '@/hooks/useUserBalances';
import { useUserBalancesQuery } from '@/hooks/queries/useUserBalancesQuery';
import { ALL_SUPPORTED_NETWORKS, isAgentAvailable, SupportedNetworks } from '@/utils/networks';
import { DeploymentProvider, useDeployment } from '@/features/autovault/components/deployment/deployment-context';
import { TokenSelection } from './token-selection';
Expand All @@ -22,7 +22,7 @@ function DeploymentModalContent({ isOpen, onOpenChange }: DeploymentModalContent
const { selectedTokenAndNetwork, createVault, isDeploying, deploymentPhase, deployedVaultAddress, navigateToVault } = useDeployment();

// Load balances and tokens at modal level
const { balances, loading: balancesLoading } = useUserBalances({
const { data: balances = [], isLoading: balancesLoading } = useUserBalancesQuery({
networkIds: VAULT_SUPPORTED_NETWORKS,
});
const { whitelistedMarkets, loading: marketsLoading } = useProcessedMarkets();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { type Address, formatUnits } from 'viem';
import { Spinner } from '@/components/ui/spinner';
import { useTokensQuery } from '@/hooks/queries/useTokensQuery';
import { TokenIcon } from '@/components/shared/token-icon';
import type { TokenBalance } from '@/hooks/useUserBalances';
import type { TokenBalance } from '@/hooks/queries/useUserBalancesQuery';
import { formatReadable } from '@/utils/balance';
import { getNetworkImg, SupportedNetworks } from '@/utils/networks';
import type { Market } from '@/utils/types';
Expand Down
85 changes: 34 additions & 51 deletions src/features/history/components/history-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { Modal, ModalHeader, ModalBody, ModalFooter } from '@/components/common/
import { MarketIdentity, MarketIdentityFocus, MarketIdentityMode } from '@/features/markets/components/market-identity';
import { RebalanceDetail } from './rebalance-detail';
import { useProcessedMarkets } from '@/hooks/useProcessedMarkets';
import useUserTransactions from '@/hooks/useUserTransactions';
import { useUserTransactionsQuery } from '@/hooks/queries/useUserTransactionsQuery';
import { useDisclosure } from '@/hooks/useDisclosure';
import { useHistoryPreferences } from '@/stores/useHistoryPreferences';
import { useStyledToast } from '@/hooks/useStyledToast';
Expand Down Expand Up @@ -80,11 +80,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His
const { allMarkets } = useProcessedMarkets();
const toast = useStyledToast();

const { loading, fetchTransactions } = useUserTransactions();
const [currentPage, setCurrentPage] = useState(1);
const [history, setHistory] = useState<UserTransaction[]>([]);
const [isInitialized, setIsInitialized] = useState(false);
const [totalPages, setTotalPages] = useState(0);

// Settings state from Zustand store
const { entriesPerPage: pageSize, setEntriesPerPage: setPageSize, isGroupedView, setIsGroupedView } = useHistoryPreferences();
Expand All @@ -94,6 +90,33 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His
// Temporary input state for settings modal
const [customPageSize, setCustomPageSize] = useState(pageSize.toString());

// Get filtered market IDs based on selected asset
const marketIdFilter = useMemo(() => {
if (!selectedAsset) return [];

return allMarkets
.filter((m) => m.loanAsset.symbol === selectedAsset.symbol && m.morphoBlue.chain.id === selectedAsset.chainId)
.map((m) => m.uniqueKey);
}, [selectedAsset, allMarkets]);

// Fetch transactions using React Query
const {
data,
isLoading: loading,
refetch,
} = useUserTransactionsQuery({
filters: {
userAddress: account ? [account] : [],
first: pageSize,
skip: (currentPage - 1) * pageSize,
marketUniqueKeys: marketIdFilter,
},
enabled: Boolean(account) && allMarkets.length > 0,
});

const history = data?.items ?? [];
const totalPages = data ? Math.ceil(data.pageInfo.countTotal / pageSize) : 0;

// Group transactions for display (especially useful for vault adapter mode)
const groupedHistory = useMemo(() => groupTransactionsByHash(history), [history]);

Expand Down Expand Up @@ -131,22 +154,12 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His

const handleManualRefresh = () => {
void (async () => {
if (!account || !fetchTransactions || allMarkets.length === 0) return;
if (!account || allMarkets.length === 0) return;

const result = await fetchTransactions({
userAddress: [account],
first: pageSize,
skip: (currentPage - 1) * pageSize,
marketUniqueKeys: marketIdFilter,
await refetch();
toast.info('Data updated', 'Transaction history updated', {
icon: <span>🔄</span>,
});

if (result) {
setHistory(result.items);
setTotalPages(Math.ceil(result.pageInfo.countTotal / pageSize));
toast.info('Data updated', 'Transaction history updated', {
icon: <span>🔄</span>,
});
}
})();
};

Expand Down Expand Up @@ -219,36 +232,6 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His
setCurrentPage(1);
}, [isGroupedView, pageSize]);

// Get filtered market IDs based on selected asset
const marketIdFilter = useMemo(() => {
if (!selectedAsset) return [];

return allMarkets
.filter((m) => m.loanAsset.symbol === selectedAsset.symbol && m.morphoBlue.chain.id === selectedAsset.chainId)
.map((m) => m.uniqueKey);
}, [selectedAsset, allMarkets]);

useEffect(() => {
const loadTransactions = async () => {
if (!account || !fetchTransactions || allMarkets.length === 0) return;

const result = await fetchTransactions({
userAddress: [account],
first: pageSize,
skip: (currentPage - 1) * pageSize,
marketUniqueKeys: marketIdFilter,
});

if (result) {
setHistory(result.items);
setTotalPages(Math.ceil(result.pageInfo.countTotal / pageSize));
}
setIsInitialized(true);
};

void loadTransactions();
}, [account, currentPage, fetchTransactions, marketIdFilter]);

useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
Expand Down Expand Up @@ -639,7 +622,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His
</TableRow>
</TableHeader>
<TableBody className="text-sm">
{!isInitialized || loading ? (
{loading ? (
renderSkeletonRows(8)
) : (isGroupedView ? groupedHistory : ungroupedHistory).length === 0 ? (
<TableRow>
Expand Down Expand Up @@ -997,7 +980,7 @@ export function HistoryTable({ account, positions, isVaultAdapter = false }: His
</TableBody>
</Table>

{isInitialized && !loading && totalPages > 1 && (
{!loading && totalPages > 1 && (
<TablePagination
currentPage={currentPage}
totalPages={totalPages}
Expand Down
39 changes: 16 additions & 23 deletions src/features/history/components/transaction-history-preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { useEffect, useState, useMemo } from 'react';
import { useMemo, useState } from 'react';
import Link from 'next/link';
import { formatUnits } from 'viem';
import { motion } from 'framer-motion';
Expand All @@ -10,7 +10,7 @@ import { TransactionIdentity } from '@/components/shared/transaction-identity';
import { TableContainerWithDescription } from '@/components/common/table-container-with-header';
import { MarketIdentity, MarketIdentityMode } from '@/features/markets/components/market-identity';
import { useProcessedMarkets } from '@/hooks/useProcessedMarkets';
import useUserTransactions from '@/hooks/useUserTransactions';
import { useUserTransactionsQuery } from '@/hooks/queries/useUserTransactionsQuery';
import { formatReadable } from '@/utils/balance';
import { groupTransactionsByHash, getWithdrawals, getSupplies } from '@/utils/transactionGrouping';
import { getTruncatedAssetName } from '@/utils/oracle';
Expand Down Expand Up @@ -54,32 +54,25 @@ export function TransactionHistoryPreview({
limit = 10,
emptyMessage,
}: TransactionHistoryPreviewProps) {
const { loading, fetchTransactions } = useUserTransactions();
const [history, setHistory] = useState<ReturnType<typeof groupTransactionsByHash>>([]);
const [isInitialized, setIsInitialized] = useState(false);
const [isViewAllHovered, setIsViewAllHovered] = useState(false);
const { allMarkets } = useProcessedMarkets();

useEffect(() => {
const loadTransactions = async () => {
if (!account || !fetchTransactions || allMarkets.length === 0) return;
const { data, isLoading: loading } = useUserTransactionsQuery({
filters: {
userAddress: account ? [account] : [],
first: limit,
skip: 0,
chainIds: chainId ? [chainId] : undefined,
},
enabled: Boolean(account) && allMarkets.length > 0,
});

const result = await fetchTransactions({
userAddress: [account],
first: limit,
skip: 0,
chainIds: chainId ? [chainId] : undefined,
});
const history = useMemo(() => {
if (!data) return [];
return groupTransactionsByHash(data.items);
}, [data]);

if (result) {
const grouped = groupTransactionsByHash(result.items);
setHistory(grouped);
}
setIsInitialized(true);
};

void loadTransactions();
}, [account, chainId, limit, fetchTransactions, allMarkets.length]);
const isInitialized = !loading;

const historyLink = useMemo(() => {
const params = new URLSearchParams();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner';
import { TokenIcon } from '@/components/shared/token-icon';
import { useProcessedMarkets } from '@/hooks/useProcessedMarkets';
import { useUserBalancesAllNetworks } from '@/hooks/useUserBalances';
import { useUserBalancesAllNetworksQuery } from '@/hooks/queries/useUserBalancesQuery';
import { formatReadable } from '@/utils/balance';
import { getNetworkImg } from '@/utils/networks';
import { findToken } from '@/utils/tokens';
Expand All @@ -27,7 +27,7 @@ function NetworkIcon({ networkId }: { networkId: number }) {
}

export function AssetSelection() {
const { balances, loading: balancesLoading } = useUserBalancesAllNetworks();
const { data: balances = [], isLoading: balancesLoading } = useUserBalancesAllNetworksQuery();
const { markets, loading: marketsLoading } = useProcessedMarkets();
const { setSelectedToken, setSelectedMarkets, goToNextStep } = useOnboarding();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createContext, useContext, useState, useMemo, useCallback } from 'react';
import { useUserBalancesAllNetworks } from '@/hooks/useUserBalances';
import { useUserBalancesAllNetworksQuery } from '@/hooks/queries/useUserBalancesQuery';
import type { Market } from '@/utils/types';
import type { TokenWithMarkets } from './types';

Expand Down Expand Up @@ -45,7 +45,7 @@ export function OnboardingProvider({ children }: { children: React.ReactNode })
const [currentStep, setStep] = useState<OnboardingStep>('asset-selection');

// Fetch user balances once for the entire onboarding flow
const { balances, loading: balancesLoading } = useUserBalancesAllNetworks();
const { data: balances = [], isLoading: balancesLoading } = useUserBalancesAllNetworksQuery();

const currentStepIndex = ONBOARDING_STEPS.findIndex((s) => s.id === currentStep);

Expand Down
8 changes: 6 additions & 2 deletions src/features/positions/positions-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { TooltipContent } from '@/components/shared/tooltip-content';
import { useProcessedMarkets } from '@/hooks/useProcessedMarkets';
import useUserPositionsSummaryData, { type EarningsPeriod } from '@/hooks/useUserPositionsSummaryData';
import { usePortfolioValue } from '@/hooks/usePortfolioValue';
import { useUserVaultsV2 } from '@/hooks/useUserVaultsV2';
import { useUserVaultsV2Query } from '@/hooks/queries/useUserVaultsV2Query';
import { SuppliedMorphoBlueGroupedTable } from './components/supplied-morpho-blue-grouped-table';
import { PortfolioValueBadge } from './components/portfolio-value-badge';
import { UserVaultsTable } from './components/user-vaults-table';
Expand All @@ -37,7 +37,11 @@ export default function Positions() {
} = useUserPositionsSummaryData(account, earningsPeriod);

// Fetch user's auto vaults
const { vaults, loading: isVaultsLoading, refetch: refetchVaults } = useUserVaultsV2(account);
const {
data: vaults = [],
isLoading: isVaultsLoading,
refetch: refetchVaults,
} = useUserVaultsV2Query({ userAddress: account as Address });

// Calculate portfolio value from positions and vaults
const { totalUsd, isLoading: isPricesLoading, error: pricesError } = usePortfolioValue(marketPositions, vaults);
Expand Down
4 changes: 2 additions & 2 deletions src/features/swap/components/BridgeSwapModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { motion } from 'framer-motion';
import { Modal, ModalBody, ModalFooter, ModalHeader } from '@/components/common/Modal';
import { Button } from '@/components/ui/button';
import { ExecuteTransactionButton } from '@/components/ui/ExecuteTransactionButton';
import { useUserBalances } from '@/hooks/useUserBalances';
import { useUserBalancesQuery } from '@/hooks/queries/useUserBalancesQuery';
import { useAllowance } from '@/hooks/useAllowance';
import { formatBalance } from '@/utils/balance';
import { useCowBridge } from '../hooks/useCowBridge';
Expand All @@ -28,7 +28,7 @@ export function BridgeSwapModal({ isOpen, onClose, targetToken }: BridgeSwapModa
const [slippage, _setSlippage] = useState<number>(DEFAULT_SLIPPAGE_PERCENT);

// Fetch user balances from CoW-supported chains
const { balances, loading: balancesLoading } = useUserBalances({
const { data: balances = [], isLoading: balancesLoading } = useUserBalancesQuery({
networkIds: COW_BRIDGE_CHAINS as unknown as number[],
});

Expand Down
Loading