diff --git a/app/api/positions/historical/route.ts b/app/api/positions/historical/route.ts index d2eac8ac..d0a607d7 100644 --- a/app/api/positions/historical/route.ts +++ b/app/api/positions/historical/route.ts @@ -52,7 +52,13 @@ async function getPositionAtBlock( blockNumber: number, chainId: number, ) { - console.log(`Get user position ${marketId.slice(0, 6)} at blockNumber ${blockNumber}`); + const isNow = blockNumber === 0; + + if (!isNow) { + console.log(`Get user position ${marketId.slice(0, 6)} at blockNumber ${blockNumber}`); + } else { + console.log(`Get user position ${marketId.slice(0, 6)} at current block`); + } const client = chainId === SupportedNetworks.Mainnet ? mainnetClient : baseClient; if (!client) throw new Error(`Unsupported chain ID: ${chainId}`); @@ -64,7 +70,7 @@ async function getPositionAtBlock( abi: morphoABI, functionName: 'position', args: [marketId as `0x${string}`, userAddress as Address], - blockNumber: BigInt(blockNumber), + blockNumber: isNow ? undefined : BigInt(blockNumber), })) as readonly bigint[]; // Convert array to position object @@ -111,23 +117,6 @@ async function getPositionAtBlock( market.totalBorrowShares, ); - // console.log(`Successfully retrieved position data:`, { - // marketId, - // userAddress, - // blockNumber, - // supplyShares: position.supplyShares.toString(), - // supplyAssets: supplyAssets.toString(), - // borrowShares: position.borrowShares.toString(), - // borrowAssets: borrowAssets.toString(), - // collateral: position.collateral.toString(), - // market: { - // totalSupplyAssets: market.totalSupplyAssets.toString(), - // totalSupplyShares: market.totalSupplyShares.toString(), - // totalBorrowAssets: market.totalBorrowAssets.toString(), - // totalBorrowShares: market.totalBorrowShares.toString(), - // }, - // }); - return { supplyShares: position.supplyShares.toString(), supplyAssets: supplyAssets.toString(), @@ -161,7 +150,7 @@ export async function GET(request: NextRequest) { // chainId, // }); - if (!blockNumber || !marketId || !userAddress) { + if (!marketId || !userAddress || (!blockNumber && blockNumber !== 0)) { console.error('Missing required parameters:', { blockNumber: !!blockNumber, marketId: !!marketId, diff --git a/src/abis/morpho-snippet.ts b/src/abis/morpho-snippet.ts new file mode 100644 index 00000000..dd67740d --- /dev/null +++ b/src/abis/morpho-snippet.ts @@ -0,0 +1,194 @@ +export const abi = [ + { + inputs: [{ internalType: 'address', name: 'morphoAddress', type: 'address' }], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'loanToken', type: 'address' }, + { internalType: 'address', name: 'collateralToken', type: 'address' }, + { internalType: 'address', name: 'oracle', type: 'address' }, + { internalType: 'address', name: 'irm', type: 'address' }, + { internalType: 'uint256', name: 'lltv', type: 'uint256' }, + ], + internalType: 'struct MarketParams', + name: 'marketParams', + type: 'tuple', + }, + { + components: [ + { internalType: 'uint128', name: 'totalSupplyAssets', type: 'uint128' }, + { internalType: 'uint128', name: 'totalSupplyShares', type: 'uint128' }, + { internalType: 'uint128', name: 'totalBorrowAssets', type: 'uint128' }, + { internalType: 'uint128', name: 'totalBorrowShares', type: 'uint128' }, + { internalType: 'uint128', name: 'lastUpdate', type: 'uint128' }, + { internalType: 'uint128', name: 'fee', type: 'uint128' }, + ], + internalType: 'struct Market', + name: 'market', + type: 'tuple', + }, + ], + name: 'borrowAPY', + outputs: [{ internalType: 'uint256', name: 'borrowApy', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'loanToken', type: 'address' }, + { internalType: 'address', name: 'collateralToken', type: 'address' }, + { internalType: 'address', name: 'oracle', type: 'address' }, + { internalType: 'address', name: 'irm', type: 'address' }, + { internalType: 'uint256', name: 'lltv', type: 'uint256' }, + ], + internalType: 'struct MarketParams', + name: 'marketParams', + type: 'tuple', + }, + { internalType: 'address', name: 'user', type: 'address' }, + ], + name: 'borrowAssetsUser', + outputs: [{ internalType: 'uint256', name: 'totalBorrowAssets', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'Id', name: 'marketId', type: 'bytes32' }, + { internalType: 'address', name: 'user', type: 'address' }, + ], + name: 'collateralAssetsUser', + outputs: [{ internalType: 'uint256', name: 'totalCollateralAssets', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'loanToken', type: 'address' }, + { internalType: 'address', name: 'collateralToken', type: 'address' }, + { internalType: 'address', name: 'oracle', type: 'address' }, + { internalType: 'address', name: 'irm', type: 'address' }, + { internalType: 'uint256', name: 'lltv', type: 'uint256' }, + ], + internalType: 'struct MarketParams', + name: 'marketParams', + type: 'tuple', + }, + ], + name: 'marketTotalBorrow', + outputs: [{ internalType: 'uint256', name: 'totalBorrowAssets', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'loanToken', type: 'address' }, + { internalType: 'address', name: 'collateralToken', type: 'address' }, + { internalType: 'address', name: 'oracle', type: 'address' }, + { internalType: 'address', name: 'irm', type: 'address' }, + { internalType: 'uint256', name: 'lltv', type: 'uint256' }, + ], + internalType: 'struct MarketParams', + name: 'marketParams', + type: 'tuple', + }, + ], + name: 'marketTotalSupply', + outputs: [{ internalType: 'uint256', name: 'totalSupplyAssets', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'morpho', + outputs: [{ internalType: 'contract IMorpho', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'loanToken', type: 'address' }, + { internalType: 'address', name: 'collateralToken', type: 'address' }, + { internalType: 'address', name: 'oracle', type: 'address' }, + { internalType: 'address', name: 'irm', type: 'address' }, + { internalType: 'uint256', name: 'lltv', type: 'uint256' }, + ], + internalType: 'struct MarketParams', + name: 'marketParams', + type: 'tuple', + }, + { + components: [ + { internalType: 'uint128', name: 'totalSupplyAssets', type: 'uint128' }, + { internalType: 'uint128', name: 'totalSupplyShares', type: 'uint128' }, + { internalType: 'uint128', name: 'totalBorrowAssets', type: 'uint128' }, + { internalType: 'uint128', name: 'totalBorrowShares', type: 'uint128' }, + { internalType: 'uint128', name: 'lastUpdate', type: 'uint128' }, + { internalType: 'uint128', name: 'fee', type: 'uint128' }, + ], + internalType: 'struct Market', + name: 'market', + type: 'tuple', + }, + ], + name: 'supplyAPY', + outputs: [{ internalType: 'uint256', name: 'supplyApy', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'loanToken', type: 'address' }, + { internalType: 'address', name: 'collateralToken', type: 'address' }, + { internalType: 'address', name: 'oracle', type: 'address' }, + { internalType: 'address', name: 'irm', type: 'address' }, + { internalType: 'uint256', name: 'lltv', type: 'uint256' }, + ], + internalType: 'struct MarketParams', + name: 'marketParams', + type: 'tuple', + }, + { internalType: 'address', name: 'user', type: 'address' }, + ], + name: 'supplyAssetsUser', + outputs: [{ internalType: 'uint256', name: 'totalSupplyAssets', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'address', name: 'loanToken', type: 'address' }, + { internalType: 'address', name: 'collateralToken', type: 'address' }, + { internalType: 'address', name: 'oracle', type: 'address' }, + { internalType: 'address', name: 'irm', type: 'address' }, + { internalType: 'uint256', name: 'lltv', type: 'uint256' }, + ], + internalType: 'struct MarketParams', + name: 'marketParams', + type: 'tuple', + }, + { internalType: 'Id', name: 'id', type: 'bytes32' }, + { internalType: 'address', name: 'user', type: 'address' }, + ], + name: 'userHealthFactor', + outputs: [{ internalType: 'uint256', name: 'healthFactor', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +]; diff --git a/src/components/supplyModal.tsx b/src/components/supplyModal.tsx index 088dbef8..5a537fe8 100644 --- a/src/components/supplyModal.tsx +++ b/src/components/supplyModal.tsx @@ -12,6 +12,7 @@ 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 { formatBalance, formatReadable } from '@/utils/balance'; import { getExplorerURL } from '@/utils/external'; import { getBundlerV2, getIRMTitle, MONARCH_TX_IDENTIFIER } from '@/utils/morpho'; @@ -35,6 +36,8 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element const [currentStep, setCurrentStep] = useState<'approve' | 'signing' | 'supplying'>('approve'); const [usePermit2Setting] = useLocalStorage('usePermit2', true); + const { batchAddUserMarkets } = useUserMarketsCache(); + const { address: account, isConnected, chainId } = useAccount(); const loanToken = findToken(market.loanAsset.address, market.morphoBlue.chain.id); @@ -173,6 +176,13 @@ export function SupplyModal({ market, onClose }: SupplyModalProps): JSX.Element value: useEth ? supplyAmount : 0n, }); + batchAddUserMarkets([ + { + marketUniqueKey: market.uniqueKey, + chainId: market.morphoBlue.chain.id, + }, + ]); + // come back to main supply page setShowProcessModal(false); } catch (error: unknown) { diff --git a/src/graphql/queries.ts b/src/graphql/queries.ts index 3c7cc629..fa9f9dc7 100644 --- a/src/graphql/queries.ts +++ b/src/graphql/queries.ts @@ -159,10 +159,8 @@ export const userPositionsQuery = ` state { supplyShares supplyAssets - supplyAssetsUsd borrowShares borrowAssets - borrowAssetsUsd collateral collateralUsd } @@ -182,16 +180,13 @@ export const userPositionForMarketQuery = ` state { supplyShares supplyAssets - supplyAssetsUsd borrowShares borrowAssets - borrowAssetsUsd collateral collateralUsd } market { - ...MarketFields - } + ...MarketFields } } } diff --git a/src/hooks/useBorrowTransaction.ts b/src/hooks/useBorrowTransaction.ts index 04d7d5f3..48df64b7 100644 --- a/src/hooks/useBorrowTransaction.ts +++ b/src/hooks/useBorrowTransaction.ts @@ -10,6 +10,7 @@ import { useLocalStorage } from './useLocalStorage'; import { usePermit2 } from './usePermit2'; import { useStyledToast } from './useStyledToast'; import { useTransactionWithToast } from './useTransactionWithToast'; +import { useUserMarketsCache } from './useUserMarketsCache'; type UseBorrowTransactionProps = { market: Market; @@ -27,6 +28,8 @@ export function useBorrowTransaction({ const [usePermit2Setting] = useLocalStorage('usePermit2', true); const [useEth, setUseEth] = useState(false); + const { batchAddUserMarkets } = useUserMarketsCache(); + const { address: account, chainId } = useAccount(); const toast = useStyledToast(); @@ -167,6 +170,13 @@ export function useBorrowTransaction({ value: useEth ? collateralAmount : 0n, }); + batchAddUserMarkets([ + { + marketUniqueKey: market.uniqueKey, + chainId: market.morphoBlue.chain.id, + }, + ]); + // come back to main borrow page setShowProcessModal(false); } catch (error: unknown) { diff --git a/src/hooks/useMultiMarketSupply.ts b/src/hooks/useMultiMarketSupply.ts index 85090089..93d79727 100644 --- a/src/hooks/useMultiMarketSupply.ts +++ b/src/hooks/useMultiMarketSupply.ts @@ -11,7 +11,7 @@ import { SupportedNetworks } from '@/utils/networks'; import { Market } from '@/utils/types'; import { useERC20Approval } from './useERC20Approval'; import { useStyledToast } from './useStyledToast'; - +import { useUserMarketsCache } from './useUserMarketsCache'; export type MarketSupply = { market: Market; amount: bigint; @@ -33,6 +33,8 @@ export function useMultiMarketSupply( const tokenSymbol = loanAsset?.symbol; const totalAmount = supplies.reduce((sum, supply) => sum + supply.amount, 0n); + const { batchAddUserMarkets } = useUserMarketsCache(); + const { authorizePermit2, permit2Authorized, @@ -160,6 +162,13 @@ export function useMultiMarketSupply( value: useEth ? totalAmount : 0n, }); + batchAddUserMarkets( + supplies.map((supply) => ({ + marketUniqueKey: supply.market.uniqueKey, + chainId: supply.market.morphoBlue.chain.id, + })), + ); + return true; } catch (error: unknown) { console.error('Error in executeSupplyTransaction:', error); diff --git a/src/hooks/usePositionSnapshot.ts b/src/hooks/usePositionSnapshot.ts index 2480b994..5b8949b5 100644 --- a/src/hooks/usePositionSnapshot.ts +++ b/src/hooks/usePositionSnapshot.ts @@ -6,6 +6,7 @@ export type PositionSnapshot = { supplyShares: string; borrowAssets: string; borrowShares: string; + collateral: string; }; type PositionResponse = { @@ -14,6 +15,7 @@ type PositionResponse = { supplyShares: string; borrowAssets: string; borrowShares: string; + collateral: string; } | null; }; @@ -50,11 +52,10 @@ export function usePositionSnapshot() { supplyShares: '0', borrowAssets: '0', borrowShares: '0', + collateral: '0', }; } - console.log('Position snapshot response:', positionData); - return { ...positionData.position, }; diff --git a/src/hooks/useRebalance.ts b/src/hooks/useRebalance.ts index c9677bd5..acb64019 100644 --- a/src/hooks/useRebalance.ts +++ b/src/hooks/useRebalance.ts @@ -8,6 +8,7 @@ import { getBundlerV2, MONARCH_TX_IDENTIFIER, MORPHO } from '@/utils/morpho'; import { GroupedPosition, RebalanceAction } from '@/utils/types'; import { usePermit2 } from './usePermit2'; import { useStyledToast } from './useStyledToast'; +import { useUserMarketsCache } from './useUserMarketsCache'; export const useRebalance = (groupedPosition: GroupedPosition, onRebalance?: () => void) => { const [rebalanceActions, setRebalanceActions] = useState([]); @@ -51,6 +52,9 @@ export const useRebalance = (groupedPosition: GroupedPosition, onRebalance?: () amount: totalAmount, }); + // add newly used markets to the cache + const { batchAddUserMarkets } = useUserMarketsCache(); + const addRebalanceAction = useCallback((action: RebalanceAction) => { setRebalanceActions((prev) => [...prev, action]); }, []); @@ -75,6 +79,8 @@ export const useRebalance = (groupedPosition: GroupedPosition, onRebalance?: () setIsConfirming(true); const transactions: `0x${string}`[] = []; + const allMarketKeys: string[] = []; + try { // Step 1: Authorize Permit2 if needed setCurrentStep('approve'); @@ -193,6 +199,9 @@ export const useRebalance = (groupedPosition: GroupedPosition, onRebalance?: () groupedWithdraws[withdrawKey].push(action); groupedSupplies[supplyKey].push(action); + + if (!allMarketKeys.includes(withdrawKey)) allMarketKeys.push(withdrawKey); + if (!allMarketKeys.includes(supplyKey)) allMarketKeys.push(supplyKey); }); // Generate batched withdraw transactions @@ -284,6 +293,14 @@ export const useRebalance = (groupedPosition: GroupedPosition, onRebalance?: () chainId: groupedPosition.chainId, }); + // add newly used markets to the cache + batchAddUserMarkets( + allMarketKeys.map((key) => ({ + marketUniqueKey: key, + chainId: groupedPosition.chainId, + })), + ); + setRebalanceActions([]); } catch (error) { console.error('Error during rebalance:', error); diff --git a/src/hooks/useUserMarketsCache.ts b/src/hooks/useUserMarketsCache.ts new file mode 100644 index 00000000..b1178034 --- /dev/null +++ b/src/hooks/useUserMarketsCache.ts @@ -0,0 +1,98 @@ +import { useCallback } from 'react'; +import storage from 'local-storage-fallback'; +import { useAccount } from 'wagmi'; +import { CacheMarketPositionKeys } from '../utils/storageKeys'; + +type MarketIdentifier = { + marketUniqueKey: string; + chainId: number; +}; + +type UserMarketsCache = Record; + +export function useUserMarketsCache() { + const { address } = useAccount(); + const userAddress = address?.toLowerCase() ?? ''; + + // Load cache from localStorage + const loadCache = useCallback((): UserMarketsCache => { + try { + const cached = storage.getItem(CacheMarketPositionKeys); + if (cached) { + return JSON.parse(cached) as UserMarketsCache; + } + } catch (error) { + console.error('Failed to load markets cache:', error); + } + return {}; + }, []); + + // Save cache to localStorage + const saveCache = useCallback((cache: UserMarketsCache) => { + try { + storage.setItem(CacheMarketPositionKeys, JSON.stringify(cache)); + } catch (error) { + console.error('Failed to save markets cache:', error); + } + }, []); + + // Add markets to the user's known list + const addUserMarkets = useCallback( + (markets: MarketIdentifier[]) => { + if (!userAddress) return; + + const cache = loadCache(); + const userMarkets = cache[userAddress] ?? []; + + const updatedMarkets = [...userMarkets]; + let hasChanges = false; + + markets.forEach((market) => { + // Check if market already exists + const exists = updatedMarkets.some( + (m) => m.marketUniqueKey === market.marketUniqueKey && m.chainId === market.chainId, + ); + + if (!exists) { + updatedMarkets.push(market); + hasChanges = true; + } + }); + + if (hasChanges) { + cache[userAddress] = updatedMarkets; + saveCache(cache); + } + }, + [userAddress, loadCache, saveCache], + ); + + // Get markets for the current user + const getUserMarkets = useCallback((): MarketIdentifier[] => { + if (!userAddress) return []; + + const cache = loadCache(); + return cache[userAddress] ?? []; + }, [userAddress, loadCache]); + + // Update cache with markets from API response + const batchAddUserMarkets = useCallback( + (apiMarkets: { marketUniqueKey: string; chainId: number }[]) => { + if (!userAddress || !apiMarkets.length) return; + + addUserMarkets( + apiMarkets.map((market) => ({ + marketUniqueKey: market.marketUniqueKey, + chainId: market.chainId, + })), + ); + }, + [userAddress, addUserMarkets], + ); + + return { + addUserMarkets, + getUserMarkets, + batchAddUserMarkets, + }; +} diff --git a/src/hooks/useUserPosition.ts b/src/hooks/useUserPosition.ts index 4dc721e7..f4e4e69e 100644 --- a/src/hooks/useUserPosition.ts +++ b/src/hooks/useUserPosition.ts @@ -1,8 +1,10 @@ import { useState, useEffect, useCallback } from 'react'; +import { Address } from 'viem'; import { userPositionForMarketQuery } from '@/graphql/queries'; import { SupportedNetworks } from '@/utils/networks'; import { MarketPosition } from '@/utils/types'; import { URLS } from '@/utils/urls'; +import { usePositionSnapshot } from './usePositionSnapshot'; const useUserPositions = ( user: string | undefined, @@ -14,6 +16,8 @@ const useUserPositions = ( const [position, setPosition] = useState(null); const [positionsError, setPositionsError] = useState(null); + const { fetchPositionSnapshot } = usePositionSnapshot(); + const fetchData = useCallback( async (isRefetch = false, onSuccess?: () => void) => { if (!user) { @@ -50,7 +54,23 @@ const useUserPositions = ( const data = (await res.json()) as { data: { marketPosition: MarketPosition } }; - setPosition(data.data.marketPosition); + // Read on-chain data + const currentSnapshot = await fetchPositionSnapshot(marketKey, user as Address, chainId, 0); + + console.log('currentSnapshot', currentSnapshot); + + if (currentSnapshot) { + setPosition({ + market: data.data.marketPosition.market, + state: { + ...currentSnapshot, + collateral: data.data.marketPosition.state.collateral, + }, + }); + } else { + setPosition(data.data.marketPosition); + } + onSuccess?.(); } catch (err) { console.error('Error fetching positions:', err); diff --git a/src/hooks/useUserPositions.ts b/src/hooks/useUserPositions.ts index 40c56a8d..79abeaae 100644 --- a/src/hooks/useUserPositions.ts +++ b/src/hooks/useUserPositions.ts @@ -1,11 +1,15 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { useState, useEffect, useCallback } from 'react'; +import { Address } from 'viem'; import { userPositionsQuery } from '@/graphql/queries'; import { SupportedNetworks } from '@/utils/networks'; import { MarketPosition } from '@/utils/types'; import { URLS } from '@/utils/urls'; import { getMarketWarningsWithDetail } from '@/utils/warnings'; +import { useUserMarketsCache } from '../hooks/useUserMarketsCache'; +import { useMarkets } from './useMarkets'; +import { usePositionSnapshot } from './usePositionSnapshot'; const useUserPositions = (user: string | undefined, showEmpty = false) => { const [loading, setLoading] = useState(true); @@ -13,6 +17,11 @@ const useUserPositions = (user: string | undefined, showEmpty = false) => { const [data, setData] = useState([]); const [positionsError, setPositionsError] = useState(null); + const { markets } = useMarkets(); + + const { fetchPositionSnapshot } = usePositionSnapshot(); + const { getUserMarkets, batchAddUserMarkets } = useUserMarketsCache(); + const fetchData = useCallback( async (isRefetch = false, onSuccess?: () => void) => { if (!user) { @@ -64,6 +73,8 @@ const useUserPositions = (user: string | undefined, showEmpty = false) => { const result1 = await responseMainnet.json(); const result2 = await responseBase.json(); + const unknownUsedMarkets = getUserMarkets(); + const marketPositions: MarketPosition[] = []; // Collect positions @@ -75,15 +86,59 @@ const useUserPositions = (user: string | undefined, showEmpty = false) => { } } - // Process positions and calculate earnings + for (const market of unknownUsedMarkets) { + // check if they're already in the marketPositions array + if ( + marketPositions.find( + (position) => + position.market.uniqueKey.toLowerCase() === market.marketUniqueKey.toLowerCase() && + position.market.morphoBlue.chain.id === market.chainId, + ) + ) { + continue; + } + + // skip markets we can't find + const marketWithDetails = markets.find((m) => m.uniqueKey === market.marketUniqueKey); + if (!marketWithDetails) { + continue; + } + + const currentSnapshot = await fetchPositionSnapshot( + market.marketUniqueKey, + user as Address, + market.chainId, + 0, + ); + + if (currentSnapshot) { + marketPositions.push({ + market: marketWithDetails, + state: currentSnapshot, + }); + } + } + const enhancedPositions = await Promise.all( marketPositions .filter( - (position: MarketPosition) => showEmpty || position.state.supplyShares.toString() !== '0', + (position: MarketPosition) => + showEmpty || position.state.supplyShares.toString() !== '0', ) .map(async (position: MarketPosition) => { + // fetch real market position to be accurate + const currentSnapshot = await fetchPositionSnapshot( + position.market.uniqueKey, + user as Address, + position.market.morphoBlue.chain.id, + 0, + ); + + const accuratePositionState = currentSnapshot ? currentSnapshot : position.state; + + // Process positions and calculate earnings return { - ...position, + state: accuratePositionState, market: { ...position.market, warningsWithDetail: getMarketWarningsWithDetail(position.market), @@ -93,6 +148,14 @@ const useUserPositions = (user: string | undefined, showEmpty = false) => { ); setData(enhancedPositions); + + batchAddUserMarkets( + marketPositions.map((position) => ({ + marketUniqueKey: position.market.uniqueKey, + chainId: position.market.morphoBlue.chain.id, + })), + ); + onSuccess?.(); } catch (err) { console.error('Error fetching positions:', err); @@ -102,7 +165,7 @@ const useUserPositions = (user: string | undefined, showEmpty = false) => { setIsRefetching(false); } }, - [user, showEmpty], + [user, showEmpty, markets], ); useEffect(() => { diff --git a/src/utils/storageKeys.ts b/src/utils/storageKeys.ts index ced1bb00..81164e7c 100644 --- a/src/utils/storageKeys.ts +++ b/src/utils/storageKeys.ts @@ -8,4 +8,5 @@ export const MarketsShowUnknownKey = 'monarch_marketsShowUnknown'; export const MarketsShowUnknownOracleKey = 'monarch_marketsShowUnknownOracle'; export const ThemeKey = 'theme'; -export const ShowBGImage = 'monarch_show_bg_image'; + +export const CacheMarketPositionKeys = 'monarch_cache_market_unique_keys'; diff --git a/src/utils/types.ts b/src/utils/types.ts index 6d583bb9..8935b121 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -4,13 +4,10 @@ export type MarketPosition = { state: { supplyShares: string; supplyAssets: string; - supplyAssetsUsd: number; borrowShares: string; borrowAssets: string; - borrowAssetsUsd: number; collateral: string; - collateralUsd: number; - } + }; market: Market; // Now using the full Market type };