diff --git a/app/positions/components/FromMarketsTable.tsx b/app/positions/components/FromMarketsTable.tsx index 3b9e1335..ade637de 100644 --- a/app/positions/components/FromMarketsTable.tsx +++ b/app/positions/components/FromMarketsTable.tsx @@ -34,7 +34,7 @@ export function FromMarketsTable({ try { const deltaBigInt = BigInt(Math.floor(position.pendingDelta)); - return previewMarketState(position.market, deltaBigInt); + return previewMarketState(position.market, deltaBigInt, undefined); } catch { return null; } diff --git a/app/positions/components/RebalanceActionInput.tsx b/app/positions/components/RebalanceActionInput.tsx index 8ba862ce..8e1a0805 100644 --- a/app/positions/components/RebalanceActionInput.tsx +++ b/app/positions/components/RebalanceActionInput.tsx @@ -50,7 +50,7 @@ export function RebalanceActionInput({ } try { const amountBigInt = parseUnits(amount, groupedPosition.loanAssetDecimals); - return previewMarketState(selectedToMarket, amountBigInt); + return previewMarketState(selectedToMarket, amountBigInt, undefined); } catch { return null; } diff --git a/app/positions/components/RebalanceCart.tsx b/app/positions/components/RebalanceCart.tsx index 8659c37e..b72c1c47 100644 --- a/app/positions/components/RebalanceCart.tsx +++ b/app/positions/components/RebalanceCart.tsx @@ -42,7 +42,7 @@ export function RebalanceCart({ let apyPreview: ReturnType | null = null; if (toMarket) { try { - apyPreview = previewMarketState(toMarket, action.amount); + apyPreview = previewMarketState(toMarket, action.amount, undefined); } catch { apyPreview = null; } diff --git a/src/components/Borrow/AddCollateralAndBorrow.tsx b/src/components/Borrow/AddCollateralAndBorrow.tsx index 9564bcb2..229b6e00 100644 --- a/src/components/Borrow/AddCollateralAndBorrow.tsx +++ b/src/components/Borrow/AddCollateralAndBorrow.tsx @@ -247,7 +247,7 @@ export function AddCollateralAndBorrow({ {/* Market Details Block - includes position overview and collapsible details */}
- +
{isConnected && ( diff --git a/src/components/Borrow/WithdrawCollateralAndRepay.tsx b/src/components/Borrow/WithdrawCollateralAndRepay.tsx index 753430d2..18ca4c4e 100644 --- a/src/components/Borrow/WithdrawCollateralAndRepay.tsx +++ b/src/components/Borrow/WithdrawCollateralAndRepay.tsx @@ -253,7 +253,7 @@ export function WithdrawCollateralAndRepay({ {/* Market Details Block - includes position overview and collapsible details */}
- +
{isConnected && ( diff --git a/src/components/Input/Input.tsx b/src/components/Input/Input.tsx index c4d2a6df..146ebbf7 100644 --- a/src/components/Input/Input.tsx +++ b/src/components/Input/Input.tsx @@ -5,9 +5,9 @@ import { formatBalance } from '@/utils/balance'; type InputProps = { decimals: number; - setValue: React.Dispatch>; + setValue: (value: bigint) => void; max?: bigint; - setError?: React.Dispatch>; + setError?: ((error: string | null) => void) | React.Dispatch>; exceedMaxErrMessage?: string; allowExceedMax?: boolean; // whether to still "setValue" when the input exceeds max onMaxClick?: () => void; diff --git a/src/components/SupplyModalContent.tsx b/src/components/SupplyModalContent.tsx index 28bacb31..8c9bc4d2 100644 --- a/src/components/SupplyModalContent.tsx +++ b/src/components/SupplyModalContent.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback } from 'react'; import { Switch } from '@heroui/react'; import { useAccount } from 'wagmi'; import Input from '@/components/Input/Input'; @@ -55,10 +55,14 @@ export function SupplyModalContent({ signAndSupply, } = useSupplyMarket(market, onSuccess); - // Notify parent component when supply amount changes - useEffect(() => { - onAmountChange?.(supplyAmount); - }, [supplyAmount, onAmountChange]); + // Handle supply amount change + const handleSupplyAmountChange = useCallback( + (amount: bigint) => { + setSupplyAmount(amount); + onAmountChange?.(amount); + }, + [setSupplyAmount, onAmountChange], + ); // Use the market network hook to handle network switching const { needSwitchChain, switchToNetwork } = useMarketNetwork({ @@ -119,10 +123,8 @@ export function SupplyModalContent({ string | null), - ) => { + setValue={handleSupplyAmountChange} + setError={(error: string | null) => { if ( typeof error === 'string' && !error.includes("You don't have any supplied assets") diff --git a/src/components/SupplyModalV2.tsx b/src/components/SupplyModalV2.tsx index 85d4c62c..cd1fd2ee 100644 --- a/src/components/SupplyModalV2.tsx +++ b/src/components/SupplyModalV2.tsx @@ -26,6 +26,7 @@ export function SupplyModalV2({ }: SupplyModalV2Props): JSX.Element { const [mode, setMode] = useState<'supply' | 'withdraw'>(defaultMode); const [supplyPreviewAmount, setSupplyPreviewAmount] = useState(); + const [withdrawPreviewAmount, setWithdrawPreviewAmount] = useState(); const hasPosition = position && BigInt(position.state.supplyAssets) > 0n; @@ -83,7 +84,13 @@ export function SupplyModalV2({ defaultCollapsed mode="supply" showRewards - loanAssetDelta={mode === 'supply' ? supplyPreviewAmount : undefined} + supplyDelta={ + mode === 'supply' + ? supplyPreviewAmount + : withdrawPreviewAmount + ? -withdrawPreviewAmount + : undefined + } /> @@ -100,6 +107,7 @@ export function SupplyModalV2({ market={market} onClose={onClose} refetch={refetch ?? (() => {})} + onAmountChange={setWithdrawPreviewAmount} /> )} diff --git a/src/components/WithdrawModalContent.tsx b/src/components/WithdrawModalContent.tsx index 0eaaab0e..246a65c1 100644 --- a/src/components/WithdrawModalContent.tsx +++ b/src/components/WithdrawModalContent.tsx @@ -19,6 +19,7 @@ type WithdrawModalContentProps = { market?: Market; onClose: () => void; refetch: () => void; + onAmountChange?: (amount: bigint) => void; }; export function WithdrawModalContent({ @@ -26,10 +27,20 @@ export function WithdrawModalContent({ market, onClose, refetch, + onAmountChange, }: WithdrawModalContentProps): JSX.Element { const toast = useStyledToast(); const [inputError, setInputError] = useState(null); const [withdrawAmount, setWithdrawAmount] = useState(BigInt(0)); + + // Notify parent component when withdraw amount changes + const handleWithdrawAmountChange = useCallback( + (amount: bigint) => { + setWithdrawAmount(amount); + onAmountChange?.(amount); + }, + [onAmountChange], + ); const { address: account, isConnected, chainId } = useAccount(); // Use market from either position or direct prop @@ -153,7 +164,7 @@ export function WithdrawModalContent({ ) : BigInt(0) } - setValue={setWithdrawAmount} + setValue={handleWithdrawAmountChange} setError={setInputError} exceedMaxErrMessage="Insufficient Liquidity" /> diff --git a/src/components/common/MarketDetailsBlock.tsx b/src/components/common/MarketDetailsBlock.tsx index 1fc8a86f..e44808ba 100644 --- a/src/components/common/MarketDetailsBlock.tsx +++ b/src/components/common/MarketDetailsBlock.tsx @@ -17,7 +17,8 @@ type MarketDetailsBlockProps = { mode?: 'supply' | 'borrow'; showRewards?: boolean; disableExpansion?: boolean; - loanAssetDelta?: bigint; + supplyDelta?: bigint; + borrowDelta?: bigint; }; export function MarketDetailsBlock({ @@ -27,7 +28,8 @@ export function MarketDetailsBlock({ mode = 'supply', showRewards = false, disableExpansion = false, - loanAssetDelta, + supplyDelta, + borrowDelta, }: MarketDetailsBlockProps): JSX.Element { const [isExpanded, setIsExpanded] = useState(!defaultCollapsed && !disableExpansion); @@ -38,13 +40,20 @@ export function MarketDetailsBlock({ whitelisted: market.whitelisted && !market.isMonarchWhitelisted }); - // Calculate preview state when loanAssetDelta is provided + // Calculate preview state when supplyDelta or borrowDelta is provided const previewState = useMemo(() => { - if (!loanAssetDelta || loanAssetDelta <= 0n || mode !== 'supply') { - return null; + // For supply mode: show preview if supplyDelta is non-zero + if (mode === 'supply' && supplyDelta && supplyDelta !== 0n) { + return previewMarketState(market, supplyDelta, undefined); } - return previewMarketState(market, loanAssetDelta); - }, [market, loanAssetDelta, mode]); + + // For borrow mode: show preview if borrowDelta is non-zero + if (mode === 'borrow' && borrowDelta && borrowDelta !== 0n) { + return previewMarketState(market, undefined, borrowDelta); + } + + return null; + }, [market, supplyDelta, borrowDelta, mode]); // Helper to format APY based on mode const getAPY = () => { diff --git a/src/utils/morpho.ts b/src/utils/morpho.ts index cbeab461..ff7294be 100644 --- a/src/utils/morpho.ts +++ b/src/utils/morpho.ts @@ -257,13 +257,14 @@ type MarketStatePreview = { }; /** - * Simulates a supply operation and returns the full market state preview. + * Simulates market state changes based on supply and borrow deltas. * * @param market - The market configuration and state - * @param supplyAmount - The amount to simulate supplying (in asset units, positive for supply) + * @param supplyDelta - Supply delta (positive: supply(), negative: withdraw()) + * @param borrowDelta - Borrow delta (positive: borrow(), negative: repay()) * @returns The estimated market state after the action, or null if simulation fails */ -export function previewMarketState(market: Market, supplyAmount: bigint): MarketStatePreview | null { +export function previewMarketState(market: Market, supplyDelta?: bigint, borrowDelta?: bigint): MarketStatePreview | null { try { const params = new BlueMarketParams({ loanToken: market.loanAsset.address as Address, @@ -284,7 +285,33 @@ export function previewMarketState(market: Market, supplyAmount: bigint): Market fee: BigInt(Math.floor(market.state.fee * 1e18)), }); - const { market: updated } = blueMarket.supply(supplyAmount, 0n); + let updated = blueMarket; + + // Apply supply delta + if (supplyDelta && supplyDelta !== 0n) { + if (supplyDelta > 0n) { + // Positive delta: supply + const result = updated.supply(supplyDelta, 0n); + updated = result.market; + } else { + // Negative delta: withdraw (pass positive amount) + const result = updated.withdraw(-supplyDelta, 0n); + updated = result.market; + } + } + + // Apply borrow delta + if (borrowDelta && borrowDelta !== 0n) { + if (borrowDelta > 0n) { + // Positive delta: borrow + const result = updated.borrow(borrowDelta, 0n); + updated = result.market; + } else { + // Negative delta: repay (pass positive amount) + const result = updated.repay(-borrowDelta, 0n); + updated = result.market; + } + } return { supplyApy: updated.supplyApy,