diff --git a/src/features/market-detail/components/market-header.tsx b/src/features/market-detail/components/market-header.tsx index 215d6335..12930578 100644 --- a/src/features/market-detail/components/market-header.tsx +++ b/src/features/market-detail/components/market-header.tsx @@ -8,7 +8,7 @@ import { ChevronDownIcon } from '@radix-ui/react-icons'; import { GrStatusGood } from 'react-icons/gr'; import { IoWarningOutline, IoEllipsisVertical } from 'react-icons/io5'; import { MdError } from 'react-icons/md'; -import { BsArrowUpCircle, BsArrowDownLeftCircle } from 'react-icons/bs'; +import { BsArrowUpCircle, BsArrowDownLeftCircle, BsFillLightningFill } from 'react-icons/bs'; import { FiExternalLink } from 'react-icons/fi'; import { LuCopy } from 'react-icons/lu'; import { Button } from '@/components/ui/button'; @@ -121,6 +121,7 @@ type MarketHeaderProps = { allWarnings: WarningWithDetail[]; onSupplyClick: () => void; onBorrowClick: () => void; + accrueInterest: () => void; }; export function MarketHeader({ @@ -132,6 +133,7 @@ export function MarketHeader({ allWarnings, onSupplyClick, onBorrowClick, + accrueInterest, }: MarketHeaderProps) { const [isExpanded, setIsExpanded] = useState(false); const { short: rateLabel } = useRateLabel(); @@ -351,6 +353,12 @@ export function MarketHeader({ > Borrow + } + > + Accrue Interest + window.open(getMarketURL(marketId, network), '_blank')} startContent={} diff --git a/src/features/market-detail/market-view.tsx b/src/features/market-detail/market-view.tsx index a0a4123d..8e6964e1 100644 --- a/src/features/market-detail/market-view.tsx +++ b/src/features/market-detail/market-view.tsx @@ -4,8 +4,9 @@ import { useState, useCallback, useMemo } from 'react'; import { useParams } from 'next/navigation'; -import { parseUnits, formatUnits } from 'viem'; -import { useConnection } from 'wagmi'; +import { parseUnits, formatUnits, type Address, encodeFunctionData } from 'viem'; +import { useConnection, useSwitchChain } from 'wagmi'; +import morphoAbi from '@/abis/morpho'; import { BorrowModal } from '@/modals/borrow/borrow-modal'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Spinner } from '@/components/ui/spinner'; @@ -16,6 +17,7 @@ import { useOraclePrice } from '@/hooks/useOraclePrice'; import { useTransactionFilters } from '@/stores/useTransactionFilters'; import { useMarketDetailPreferences, type MarketDetailTab } from '@/stores/useMarketDetailPreferences'; import useUserPosition from '@/hooks/useUserPosition'; +import { useTransactionWithToast } from '@/hooks/useTransactionWithToast'; import type { SupportedNetworks } from '@/utils/networks'; import { BorrowersTable } from '@/features/market-detail/components/borrowers-table'; import { BorrowsTable } from '@/features/market-detail/components/borrows-table'; @@ -96,6 +98,22 @@ function MarketContent() { totalCount: suppliersTotalCount, } = useAllMarketSuppliers(market?.uniqueKey, network); + const { mutateAsync: switchChainAsync } = useSwitchChain(); + + // Transaction hook for accruing interest + const { sendTransaction } = useTransactionWithToast({ + toastId: 'accrue-interest', + pendingText: 'Accruing Interest', + successText: 'Interest Accrued', + errorText: 'Failed to accrue interest', + chainId: market?.morphoBlue.chain.id, + pendingDescription: 'Updating market interest rates...', + successDescription: 'Market interest rates have been updated', + onSuccess: () => { + void refetchMarket(); + }, + }); + // 6. All memoized values and callbacks // Helper to scale user input to token amount @@ -247,6 +265,30 @@ function MarketContent() { setShowBorrowModal(true); }; + const handleAccrueInterest = async () => { + await switchChainAsync({ chainId: market.morphoBlue.chain.id }); + const morphoAddress = market.morphoBlue.address as Address; + + sendTransaction({ + to: morphoAddress, + account: address, + data: encodeFunctionData({ + abi: morphoAbi, + functionName: 'accrueInterest', + 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), + }, + ], + }), + chainId: market.morphoBlue.chain.id, + }); + }; + return ( <>
@@ -261,6 +303,7 @@ function MarketContent() { allWarnings={allWarnings} onSupplyClick={handleSupplyClick} onBorrowClick={handleBorrowClick} + accrueInterest={handleAccrueInterest} /> {showBorrowModal && ( @@ -358,6 +401,25 @@ function MarketContent() { + {/* Tables */} +
+ setShowSupplierFiltersModal(true)} + /> +
+
+ setShowBorrowerFiltersModal(true)} + /> +
+ {/* Suppliers row: Pie + Concentration */}
- - {/* Tables */} -
- setShowSupplierFiltersModal(true)} - /> -
-
- setShowBorrowerFiltersModal(true)} - /> -
diff --git a/src/features/positions/components/supplied-morpho-blue-grouped-table.tsx b/src/features/positions/components/supplied-morpho-blue-grouped-table.tsx index 8aeff50d..9f402140 100644 --- a/src/features/positions/components/supplied-morpho-blue-grouped-table.tsx +++ b/src/features/positions/components/supplied-morpho-blue-grouped-table.tsx @@ -228,6 +228,7 @@ export function SuppliedMorphoBlueGroupedTable({ account }: SuppliedMorphoBlueGr className="cursor-pointer hover:bg-gray-50" onClick={() => toggleRow(rowKey)} > + {/* Chain image */}
+ + {/* Loan asset details */}
{formatReadable(groupedPosition.totalSupply)} @@ -251,11 +254,15 @@ export function SuppliedMorphoBlueGroupedTable({ account }: SuppliedMorphoBlueGr />
+ + {/* Current APR/APY */}
{formatReadable((isAprDisplay ? convertApyToApr(avgApy) : avgApy) * 100)}%
+ + {/* Accrued interest */}
{isEarningsLoading ? ( @@ -293,6 +300,8 @@ export function SuppliedMorphoBlueGroupedTable({ account }: SuppliedMorphoBlueGr )}
+ + {/* Collateral exposure */} + + {/* Risk indicators */} + + {/* Actions button */}