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 app/positions/components/FromMarketsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion app/positions/components/RebalanceActionInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion app/positions/components/RebalanceCart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function RebalanceCart({
let apyPreview: ReturnType<typeof previewMarketState> | null = null;
if (toMarket) {
try {
apyPreview = previewMarketState(toMarket, action.amount);
apyPreview = previewMarketState(toMarket, action.amount, undefined);
} catch {
apyPreview = null;
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Borrow/AddCollateralAndBorrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ export function AddCollateralAndBorrow({

{/* Market Details Block - includes position overview and collapsible details */}
<div className="mb-5">
<MarketDetailsBlock market={market} mode="borrow" defaultCollapsed showRewards />
<MarketDetailsBlock market={market} mode="borrow" defaultCollapsed showRewards borrowDelta={borrowAmount} />
</div>

{isConnected && (
Expand Down
2 changes: 1 addition & 1 deletion src/components/Borrow/WithdrawCollateralAndRepay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export function WithdrawCollateralAndRepay({

{/* Market Details Block - includes position overview and collapsible details */}
<div className="mb-5">
<MarketDetailsBlock market={market} mode="borrow" defaultCollapsed />
<MarketDetailsBlock market={market} mode="borrow" defaultCollapsed borrowDelta={repayAssets ? -repayAssets : undefined} />
</div>

{isConnected && (
Expand Down
4 changes: 2 additions & 2 deletions src/components/Input/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { formatBalance } from '@/utils/balance';

type InputProps = {
decimals: number;
setValue: React.Dispatch<React.SetStateAction<bigint>>;
setValue: (value: bigint) => void;
max?: bigint;
setError?: React.Dispatch<React.SetStateAction<string | null>>;
setError?: ((error: string | null) => void) | React.Dispatch<React.SetStateAction<string | null>>;
exceedMaxErrMessage?: string;
allowExceedMax?: boolean; // whether to still "setValue" when the input exceeds max
onMaxClick?: () => void;
Expand Down
20 changes: 11 additions & 9 deletions src/components/SupplyModalContent.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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({
Expand Down Expand Up @@ -119,10 +123,8 @@ export function SupplyModalContent({
<Input
decimals={market.loanAsset.decimals}
max={useEth ? ethBalance ?? BigInt(0) : tokenBalance ?? BigInt(0)}
setValue={setSupplyAmount}
setError={(
error: string | null | ((prev: string | null) => string | null),
) => {
setValue={handleSupplyAmountChange}
setError={(error: string | null) => {
if (
typeof error === 'string' &&
!error.includes("You don't have any supplied assets")
Expand Down
10 changes: 9 additions & 1 deletion src/components/SupplyModalV2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function SupplyModalV2({
}: SupplyModalV2Props): JSX.Element {
const [mode, setMode] = useState<'supply' | 'withdraw'>(defaultMode);
const [supplyPreviewAmount, setSupplyPreviewAmount] = useState<bigint | undefined>();
const [withdrawPreviewAmount, setWithdrawPreviewAmount] = useState<bigint | undefined>();

const hasPosition = position && BigInt(position.state.supplyAssets) > 0n;

Expand Down Expand Up @@ -83,7 +84,13 @@ export function SupplyModalV2({
defaultCollapsed
mode="supply"
showRewards
loanAssetDelta={mode === 'supply' ? supplyPreviewAmount : undefined}
supplyDelta={
mode === 'supply'
? supplyPreviewAmount
: withdrawPreviewAmount
? -withdrawPreviewAmount
: undefined
}
/>
</div>

Expand All @@ -100,6 +107,7 @@ export function SupplyModalV2({
market={market}
onClose={onClose}
refetch={refetch ?? (() => {})}
onAmountChange={setWithdrawPreviewAmount}
/>
)}
</div>
Expand Down
13 changes: 12 additions & 1 deletion src/components/WithdrawModalContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,28 @@ type WithdrawModalContentProps = {
market?: Market;
onClose: () => void;
refetch: () => void;
onAmountChange?: (amount: bigint) => void;
};

export function WithdrawModalContent({
position,
market,
onClose,
refetch,
onAmountChange,
}: WithdrawModalContentProps): JSX.Element {
const toast = useStyledToast();
const [inputError, setInputError] = useState<string | null>(null);
const [withdrawAmount, setWithdrawAmount] = useState<bigint>(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
Expand Down Expand Up @@ -153,7 +164,7 @@ export function WithdrawModalContent({
)
: BigInt(0)
}
setValue={setWithdrawAmount}
setValue={handleWithdrawAmountChange}
setError={setInputError}
exceedMaxErrMessage="Insufficient Liquidity"
/>
Expand Down
23 changes: 16 additions & 7 deletions src/components/common/MarketDetailsBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ type MarketDetailsBlockProps = {
mode?: 'supply' | 'borrow';
showRewards?: boolean;
disableExpansion?: boolean;
loanAssetDelta?: bigint;
supplyDelta?: bigint;
borrowDelta?: bigint;
};

export function MarketDetailsBlock({
Expand All @@ -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);

Expand All @@ -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 = () => {
Expand Down
35 changes: 31 additions & 4 deletions src/utils/morpho.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down