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
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ export function OpenLongForm({
onOpenLong?.(e);
}}
>
Open Long
Buy Fixed
</button>
);
})()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { convertSharesToBase } from "src/hyperdrive/convertSharesToBase";
import { getDepositAssets } from "src/hyperdrive/getDepositAssets";
import { useAppConfigForConnectedChain } from "src/ui/appconfig/useAppConfigForConnectedChain";
import { PrimaryStat } from "src/ui/base/components/PrimaryStat";
import { Tooltip } from "src/ui/base/components/Tooltip/Tooltip";
import { formatBalance } from "src/ui/base/formatting/formatBalance";
import { formatDate } from "src/ui/base/formatting/formatDate";
import { useFixedRate } from "src/ui/hyperdrive/longs/hooks/useFixedRate";
Expand Down Expand Up @@ -153,7 +154,15 @@ export function OpenLongStats({
"text-base-content/80": !amountPaid,
})}
>
<img src={baseToken.iconUrl} className="mr-1 h-8 rounded-full" />
<Tooltip
tooltip={baseToken.symbol}
className="self-center font-normal"
>
<img
src={baseToken.iconUrl}
className="mr-1.5 size-7 rounded-full"
/>
</Tooltip>
{`${formatBalance({
balance: amountPaidInBase + yieldAtMaturity,
decimals: baseToken.decimals,
Expand All @@ -162,7 +171,6 @@ export function OpenLongStats({
</span>
)
}
valueUnit={`${baseToken.symbol}`}
subValue={
// Defillama fetches the token price via {chain}:{tokenAddress}. Since the token address differs on testnet, term length is displayed instead.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { fixed } from "@delvtech/fixed-point-wasm";
import { HyperdriveConfig } from "@delvtech/hyperdrive-appconfig";
import {
getYieldSource,
HyperdriveConfig,
} from "@delvtech/hyperdrive-appconfig";
import { adjustAmountByPercentage } from "@delvtech/hyperdrive-js";
import { InformationCircleIcon } from "@heroicons/react/24/outline";
import { MouseEvent, ReactElement, useState } from "react";
import classNames from "classnames";
import { MouseEvent, ReactElement } from "react";
import { MAX_UINT256 } from "src/base/constants";
import { formatRate } from "src/base/formatRate";
import { isTestnetChain } from "src/chains/isTestnetChain";
Expand Down Expand Up @@ -32,6 +36,7 @@ import { OpenShortPreview } from "src/ui/hyperdrive/shorts/OpenShortPreview/Open
import { useMaxShort } from "src/ui/hyperdrive/shorts/hooks/useMaxShort";
import { useOpenShort } from "src/ui/hyperdrive/shorts/hooks/useOpenShort";
import { usePreviewOpenShort } from "src/ui/hyperdrive/shorts/hooks/usePreviewOpenShort";
import { useShortRate } from "src/ui/hyperdrive/shorts/hooks/useShortRate";
import { PositionPicker } from "src/ui/markets/PositionPicker";
import { RewardsTooltip } from "src/ui/rewards/RewardsTooltip/RewardsTooltip";
import { useOpenShortRewards } from "src/ui/rewards/hooks/useOpenShortRewards";
Expand All @@ -48,8 +53,6 @@ import { useYieldSourceRate } from "src/ui/vaults/useYieldSourceRate";
import { formatUnits } from "viem";
import { useAccount, useChainId } from "wagmi";

(window as any).fixed = fixed;

interface OpenShortPositionFormProps {
hyperdrive: HyperdriveConfig;
onOpenShort?: (e: MouseEvent<HTMLButtonElement>) => void;
Expand All @@ -68,6 +71,12 @@ export function OpenShortForm({
chainId: hyperdrive.chainId,
});
const appConfig = useAppConfigForConnectedChain();
const yieldSource = getYieldSource({
hyperdriveChainId: hyperdrive.chainId,
hyperdriveAddress: hyperdrive.address,
appConfig,
});

const { baseToken, baseTokenDepositEnabled, tokenOptions } =
useTokenDepositOptions({
hyperdrive,
Expand Down Expand Up @@ -122,23 +131,13 @@ export function OpenShortForm({
tokenChainId: activeToken.chainId,
});

// TODO: Implement the two way input switch once getMaxShort is fixed on the sdk
const [activeInput, setActiveInput] = useState<"bonds" | "budget">("bonds");

const {
amount: amountOfBondsToShort,
amountAsBigInt: amountOfBondsToShortAsBigInt,
setAmount: setShortAmount,
} = useNumericInput({
decimals: hyperdrive.decimals,
});
const {
amount: amountToPay,
amountAsBigInt: amountToPayAsBigInt,
setAmount: setPaymentAmount,
} = useNumericInput({
decimals: hyperdrive.decimals,
});

const {
traderDeposit,
Expand Down Expand Up @@ -187,17 +186,23 @@ export function OpenShortForm({
budget: MAX_UINT256,
});

const { maxBondsOut: maxBondsOutFromPayment } = useMaxShort({
chainId: hyperdrive.chainId,
hyperdriveAddress: hyperdrive.address,
budget: amountToPayAsBigInt || 0n,
});

const hasEnoughLiquidity = getIsValidTradeSize({
tradeAmount: amountOfBondsToShortAsBigInt,
maxTradeSize: maxBondsOut,
});

const defaultBondAmount =
hyperdrive.decimals > 6 ? BigInt(1e15) : BigInt(1e6);
const { shortApr, shortRateStatus } = useShortRate({
chainId: hyperdrive.chainId,
// show the market short rate (aka bond amount of 1) if the user hasn't
// already entered a short size
bondAmount: amountOfBondsToShortAsBigInt || defaultBondAmount,
hyperdriveAddress: hyperdrive.address,
timestamp: BigInt(Math.floor(Date.now() / 1000)),
variableApy: vaultRate?.netVaultRate,
});

const {
setSlippage,
slippage,
Expand Down Expand Up @@ -232,23 +237,9 @@ export function OpenShortForm({
},
onExecuted: () => {
setShortAmount("");
setPaymentAmount("");
},
});

// TODO: Implement the two way input switch once getMaxShort is fixed on the sdk
// Max button is wired up to the user's balance, or the pool's max long.
// Whichever is smallest.
// let maxButtonValue = "0";
// if (activeTokenBalance && maxBondsOut) {
// maxButtonValue = formatUnits(
// activeTokenBalance.value > maxBondsOut
// ? maxBondsOut
// : activeTokenBalance?.value,
// activeToken.decimals,
// );
// }

const exposureMultiplier =
amountOfBondsToShortAsBigInt && traderDeposit
? fixed(amountOfBondsToShortAsBigInt, activeToken.decimals)
Expand Down Expand Up @@ -293,19 +284,10 @@ export function OpenShortForm({
},
});
setActiveToken(tokenAddress);

// TODO: Determine if there is a bug here.
setPaymentAmount("0");
}}
/>
}
value={
activeInput === "bonds"
? amountOfBondsToShort || ""
: maxBondsOutFromPayment
? formatUnits(maxBondsOutFromPayment, baseToken.decimals)
: ""
}
value={amountOfBondsToShort || ""}
settings={
<div className="mb-3 flex w-full items-center justify-between">
<PositionPicker hyperdrive={hyperdrive} />
Expand Down Expand Up @@ -342,7 +324,6 @@ export function OpenShortForm({
},
});
setShortAmount(newAmount);
setActiveInput("bonds");
}}
bottomLeftElement={
// Defillama fetches the token price via {chain}:{tokenAddress}.
Expand All @@ -366,6 +347,7 @@ export function OpenShortForm({
}
/>
<TokenInput
disabled
variant="lighter"
name={`${baseToken.symbol}-input`}
token={
Expand All @@ -374,23 +356,11 @@ export function OpenShortForm({
activeTokenAddress={activeToken.address}
onChange={(tokenAddress) => {
setActiveToken(tokenAddress);
setPaymentAmount("0");
}}
/>
}
inputLabel="You pay"
value={
activeInput === "budget"
? amountToPay || "0"
: formatUnits(traderDeposit || 0n, activeToken.decimals)
}
// This input is disabled until the getMaxShort is fixed on the sdk.
disabled
// maxValue={maxButtonValue}
// onChange={(newAmount) => {
// setActiveInput("budget");
// setPaymentAmount(newAmount);
// }}
value={formatUnits(traderDeposit || 0n, activeToken.decimals)}
bottomLeftElement={
// Defillama fetches the token price via {chain}:{tokenAddress}. Since the token address differs on testnet, price display is disabled there.
!isTestnetChain(hyperdrive.chainId) ? (
Expand Down Expand Up @@ -425,59 +395,85 @@ export function OpenShortForm({
</div>
}
primaryStats={
<div className="flex justify-between px-4 py-8">
<PrimaryStat
label="Variable APY"
value={
<span className="text-h3 font-bold">
{isNewPool ? (
"✨New✨"
) : rewards?.length ? (
<RewardsTooltip
hyperdriveAddress={hyperdrive.address}
position="openShort"
baseRate={vaultRate?.vaultRate}
netRate={vaultRate?.netVaultRate}
chainId={hyperdrive.chainId}
>
{formatRate({ rate: vaultRate?.netVaultRate ?? 0n })}⚡
</RewardsTooltip>
) : (
formatRate({ rate: vaultRate?.netVaultRate ?? 0n })
)}
</span>
}
valueContainerClassName="flex items-end"
unitClassName="text-h3 font-bold"
subValue={
<span className="gradient-text">
<span className="font-bold">{`${exposureMultiplier}x`}</span>{" "}
capital exposure
</span>
}
valueLoading={longPriceStatus === "loading"}
/>
<div className="daisy-divider daisy-divider-horizontal" />
<PrimaryStat
alignment="right"
label="Fixed APR Cost"
value={
<span className="text-h3 font-bold">
{formatRate({ rate: fixedRatePaid || fixedApr?.apr || 0n })}
</span>
}
unitClassName="mb-1 font-bold"
subValue={
<Tooltip
position="top"
tooltip="Short positions provide the fixed rate yield to Long positions. Opening a Short is a one-time cost."
className="gap-1 before:text-left"
>
What am I paying for?{" "}
<InformationCircleIcon className="size-4 text-neutral-content" />
</Tooltip>
}
/>
// TOOD: Make this it's own component, OpenShortStats
<div className="flex flex-col gap-4 py-8">
<div className="flex w-full justify-between px-4">
<PrimaryStat
label="Variable APY"
value={
<span className="text-h3 font-bold">
{isNewPool ? (
"✨New✨"
) : rewards?.length ? (
<RewardsTooltip
hyperdriveAddress={hyperdrive.address}
position="openShort"
baseRate={vaultRate?.vaultRate}
netRate={vaultRate?.netVaultRate}
chainId={hyperdrive.chainId}
>
{formatRate({ rate: shortApr?.apr ?? 0n })}⚡
</RewardsTooltip>
) : (
`${formatRate({ rate: shortApr?.apr ?? 0n })}`
)}
</span>
}
valueContainerClassName="flex items-end"
unitClassName="text-h3 font-bold"
subValue={
<span
className={classNames("gradient-text", {
invisible: !shortApr?.apr,
})}
>
<span className="font-bold">
≈ {`${exposureMultiplier}x`}
</span>{" "}
capital exposure
</span>
}
valueLoading={longPriceStatus === "loading"}
/>
<div className="daisy-divider daisy-divider-horizontal" />
<PrimaryStat
alignment="right"
label="Fixed APR Cost"
value={
<span className="text-h3 font-bold">
{formatRate({ rate: fixedRatePaid || fixedApr?.apr || 0n })}
</span>
}
unitClassName="mb-1 font-bold"
subValue={
<Tooltip
position="top"
tooltip="Short positions provide the fixed rate yield in exchange for the variable. Opening a Short is a one-time cost."
className="gap-1 before:text-left"
>
What am I paying for?{" "}
<InformationCircleIcon className="size-4 text-neutral-content" />
</Tooltip>
}
/>
</div>
{!!amountOfBondsToShortAsBigInt && hasEnoughLiquidity ? (
<div className="flex items-center rounded-md bg-base-200 py-3">
<p className="mx-4 text-sm leading-bodyText text-neutral-content">
Earn{" "}
<span className="font-bold text-white">
{formatRate({ rate: shortApr?.apr ?? 0n })}
</span>{" "}
if <span className="font-bold">{yieldSource.shortName}</span>{" "}
remains constant at{" "}
<span className="mx-0.5 font-bold text-white">
{vaultRate?.netVaultRate
? formatRate({ rate: vaultRate.netVaultRate })
: null}
</span>
</p>
</div>
) : null}
</div>
}
transactionPreview={
Expand Down
Loading
Loading