Skip to content

Commit

Permalink
Add Short APR and Short ROI toggle (#1136)
Browse files Browse the repository at this point in the history
* Upgrade hyperdrive-viem to latest hyperdrive-js-core

* Rename useCurrentFixedRate -> useFixedRate, useImpliedRate -> useShortRate

* Implement short roi/apr, cleanup hooks

* Remove unused code

* Use block wells to prevent layout shift

* Reuse type
  • Loading branch information
DannyDelott authored Jun 5, 2024
1 parent 5e23ea1 commit d377536
Show file tree
Hide file tree
Showing 13 changed files with 100 additions and 123 deletions.
17 changes: 17 additions & 0 deletions apps/hyperdrive-trading/src/base/queryStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FetchStatus, QueryStatus } from "@tanstack/react-query";
export type QueryStatusWithIdle = QueryStatus | "idle";

export function getStatus(
status: QueryStatus,
fetchStatus: FetchStatus,
): QueryStatus | "idle" {
let queryStatus: QueryStatusWithIdle = status;

// In cases where a query is disabled, it will still going into a loading
// status. However, the fetch status will be idle.
if (fetchStatus === "idle" && status === "loading") {
queryStatus = "idle";
}

return queryStatus;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export function FeatureFlagPicker(): ReactElement {
>
<li className="daisy-menu-title">Feature flags</li>
{/* Place your feature flag menu items here, eg: */}
<FeatureFlagMenuItem flagName="roi-apr">
ROI and APR
</FeatureFlagMenuItem>
{/* <FeatureFlagMenuItem flagName="name-here">
Menu Item Name here
</FeatureFlagMenuItem> */}
</ul>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useAppConfig } from "src/ui/appconfig/useAppConfig";
import { LabelValue } from "src/ui/base/components/LabelValue";
import { formatBalance } from "src/ui/base/formatting/formatBalance";
import { formatDate } from "src/ui/base/formatting/formatDate";
import { useCurrentFixedRate } from "src/ui/hyperdrive/hooks/useCurrentFixedRate";
import { useFixedRate } from "src/ui/hyperdrive/longs/hooks/useFixedRate";

interface OpenLongPreviewProps {
hyperdrive: HyperdriveConfig;
Expand Down Expand Up @@ -48,7 +48,7 @@ export function OpenLongPreview({
yieldSourceTokenAddress: hyperdrive.sharesToken,
tokens: appConfig.tokens,
});
const { fixedApr } = useCurrentFixedRate(hyperdrive.address);
const { fixedApr } = useFixedRate(hyperdrive.address);
const termLengthMS = Number(hyperdrive.poolConfig.positionDuration * 1000n);
const numDays = convertMillisecondsToDays(termLengthMS);
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@ import { useQuery } from "@tanstack/react-query";

import { formatRate } from "src/base/formatRate";
import { makeQueryKey } from "src/base/makeQueryKey";
import { QueryStatusWithIdle, getStatus } from "src/base/queryStatus";
import { useAppConfig } from "src/ui/appconfig/useAppConfig";
import { useReadHyperdrive } from "src/ui/hyperdrive/hooks/useReadHyperdrive";
import { Address } from "viem";

export function useCurrentFixedRate(hyperdriveAddress: Address): {
export function useFixedRate(hyperdriveAddress: Address): {
fixedApr: { apr: bigint; formatted: string } | undefined;
fixedRoi: { roi: bigint; formatted: string } | undefined;
fixedAprStatus: "loading" | "error" | "success";
fixedRateStatus: QueryStatusWithIdle;
} {
const { hyperdrives } = useAppConfig();
const readHyperdrive = useReadHyperdrive(hyperdriveAddress);
const hyperdrive = findHyperdriveConfig({ hyperdrives, hyperdriveAddress });
const readHyperdrive = useReadHyperdrive(hyperdriveAddress);

const queryEnabled = !!readHyperdrive;
const { data, status } = useQuery({
const { data, status, fetchStatus } = useQuery({
queryKey: makeQueryKey("fixedApr", { address: hyperdriveAddress }),
queryFn: queryEnabled
? async () => {
Expand All @@ -45,6 +46,6 @@ export function useCurrentFixedRate(hyperdriveAddress: Address): {
return {
fixedApr: data?.fixedApr,
fixedRoi: data?.fixedRoi,
fixedAprStatus: status,
fixedRateStatus: getStatus(status, fetchStatus),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useAppConfig } from "src/ui/appconfig/useAppConfig";
import { LabelValue } from "src/ui/base/components/LabelValue";
import { formatBalance } from "src/ui/base/formatting/formatBalance";
import { formatDate } from "src/ui/base/formatting/formatDate";
import { useCurrentFixedRate } from "src/ui/hyperdrive/hooks/useCurrentFixedRate";
import { useFixedRate } from "src/ui/hyperdrive/longs/hooks/useFixedRate";
interface OpenShortPreviewProps {
hyperdrive: HyperdriveConfig;
tokenIn: TokenConfig<any>;
Expand All @@ -38,7 +38,7 @@ export function OpenShortPreview({
baseTokenAddress: hyperdrive.baseToken,
tokens: appConfig.tokens,
});
const { fixedApr } = useCurrentFixedRate(hyperdrive.address);
const { fixedApr } = useFixedRate(hyperdrive.address);
const termLengthMS = Number(hyperdrive.poolConfig.positionDuration * 1000n);
return (
<div className="flex flex-col gap-3">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { FetchStatus, useQuery } from "@tanstack/react-query";
import { getHprFromApr } from "@delvtech/hyperdrive-viem";
import { findHyperdriveConfig } from "@hyperdrive/appconfig";
import { useQuery } from "@tanstack/react-query";
import { formatRate } from "src/base/formatRate";
import { makeQueryKey } from "src/base/makeQueryKey";
import { getStatus } from "src/base/queryStatus";
import { useAppConfig } from "src/ui/appconfig/useAppConfig";
import { useReadHyperdrive } from "src/ui/hyperdrive/hooks/useReadHyperdrive";
import { Address } from "viem";
interface UseImpliedRateOptions {
Expand All @@ -12,34 +17,36 @@ interface UseImpliedRateOptions {
/**
* Returns the list of shorts that the account currently has open.
*/
export function useImpliedRate({
export function useShortRate({
bondAmount,
timestamp,
variableApy,
hyperdriveAddress,
}: UseImpliedRateOptions): {
impliedRate: bigint | undefined;
impliedRateStatus: "error" | "success" | "loading";
impliedRateFetchStatus: FetchStatus;
shortApr: { apr: bigint; formatted: string } | undefined;
shortRoi: { roi: bigint; formatted: string } | undefined;
shortRateStatus: "loading" | "error" | "success" | "idle";
} {
const { hyperdrives } = useAppConfig();
const readHyperdrive = useReadHyperdrive(hyperdriveAddress);
const queryEnabled =
!!hyperdriveAddress &&
!!readHyperdrive &&
bondAmount !== undefined &&
timestamp !== undefined &&
variableApy !== undefined;

const {
data: impliedRate,
status: impliedRateStatus,
data,
status,
// If the query is disabled and does not have cached data, then the query
// will start in the status === 'loading' and fetchStatus === 'idle' state.
// This is needed to avoid showing infinite skeletons when the query is
// disabled and not fetching.
// See: https://tanstack.com/query/v4/docs/framework/react/guides/disabling-queries#:~:text=When%20enabled%20is%20false%3A,not%20automatically%20fetch%20on%20mount.
fetchStatus: impliedRateFetchStatus,
fetchStatus,
} = useQuery({
queryKey: makeQueryKey("impliedRate", {
queryKey: makeQueryKey("shortRate", {
hyperdriveAddress,
bondAmount: bondAmount?.toString(),
timestamp: timestamp?.toString(),
Expand All @@ -48,19 +55,33 @@ export function useImpliedRate({
enabled: queryEnabled,
queryFn: queryEnabled
? async () => {
const result = await readHyperdrive.getImpliedRate({
const hyperdrive = findHyperdriveConfig({
hyperdrives,
hyperdriveAddress,
});
const shortApr = await readHyperdrive.getImpliedRate({
bondAmount,
timestamp,
variableApy,
});
return result;
const shortRoi = getHprFromApr(
shortApr,
hyperdrive.poolConfig.positionDuration,
);
return {
shortApr: {
apr: shortApr,
formatted: formatRate(shortApr),
},
shortRoi: { roi: shortRoi, formatted: formatRate(shortRoi) },
};
}
: undefined,
});

return {
impliedRate,
impliedRateStatus,
impliedRateFetchStatus,
shortApr: data?.shortApr,
shortRoi: data?.shortRoi,
shortRateStatus: getStatus(status, fetchStatus),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ export function MarketDetailsBody({

{/* Stats section */}
<div className="flex flex-wrap gap-16">
<YieldStats hyperdrive={hyperdrive} />
<LiquidityStats hyperdrive={hyperdrive} />
<div className="flex-1">
<YieldStats hyperdrive={hyperdrive} />
</div>
<div className="flex-1">
<LiquidityStats hyperdrive={hyperdrive} />
</div>
</div>

{marketState?.isPaused && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ import Skeleton from "react-loading-skeleton";
import { useLocalStorage } from "react-use";
import { MultiStat, MultiStatProps } from "src/ui/base/components/MultiStat";
import { useIsTailwindSmallScreen } from "src/ui/base/mediaBreakpoints";
import { useCurrentFixedRate } from "src/ui/hyperdrive/hooks/useCurrentFixedRate";
import { useFixedRate } from "src/ui/hyperdrive/longs/hooks/useFixedRate";

export function FixedRateStat({
hyperdrive,
}: {
hyperdrive: HyperdriveConfig;
}): ReactElement {
const isTailwindSmallScreen = useIsTailwindSmallScreen();
const { fixedApr, fixedRoi, fixedAprStatus } = useCurrentFixedRate(
hyperdrive.address,
);
const {
fixedApr,
fixedRoi,
fixedRateStatus: fixedAprStatus,
} = useFixedRate(hyperdrive.address);
const [rateType, setRateType] = useLocalStorage<"fixedApr" | "fixedRoi">(
"yield-stats-long-rate-type",
"fixedApr",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ export function LiquidityStats({
});

return (
<Well transparent>
<Well transparent block>
<div className="space-y-8">
<h5 className="flex text-neutral-content">Liquidity</h5>
<div className="flex gap-6 lg:gap-16">
<div className="flex gap-6 lg:gap-8">
<Stat
label={`Total (${baseToken.symbol})`}
value={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import classNames from "classnames";
import { ReactElement } from "react";
import Skeleton from "react-loading-skeleton";
import { useLocalStorage } from "react-use";
import { formatRate } from "src/base/formatRate";
import { parseUnits } from "src/base/parseUnits";
import { MultiStat, MultiStatProps } from "src/ui/base/components/MultiStat";
import { useImpliedRate } from "src/ui/hyperdrive/shorts/hooks/useImpliedRate";
import { useShortRate } from "src/ui/hyperdrive/shorts/hooks/useShortRate";
import { useYieldSourceRate } from "src/ui/vaults/useYieldSourceRate";

export function ShortRateStat({
Expand All @@ -22,19 +21,14 @@ export function ShortRateStat({
hyperdriveAddress: hyperdrive.address,
});

const { impliedRate, impliedRateStatus, impliedRateFetchStatus } =
useImpliedRate({
bondAmount: parseUnits("1", 18),
hyperdriveAddress: hyperdrive.address,
variableApy: vaultRate?.vaultRate ? vaultRate.vaultRate : undefined,
timestamp: BigInt(Math.floor(Date.now() / 1000)),
});
const isLoadingShortRoi =
impliedRateStatus === "loading" &&
impliedRateFetchStatus === "fetching" &&
impliedRate === undefined;

const formattedRate = impliedRate ? `${formatRate(impliedRate)}%` : "-";
const { shortApr, shortRoi, shortRateStatus } = useShortRate({
bondAmount: parseUnits("1", 18),
hyperdriveAddress: hyperdrive.address,
variableApy: vaultRate?.vaultRate ? vaultRate.vaultRate : undefined,
timestamp: BigInt(Math.floor(Date.now() / 1000)),
});
const isLoadingShortRate =
shortRateStatus === "loading" && shortApr === undefined;

return (
<MultiStat
Expand All @@ -49,11 +43,11 @@ export function ShortRateStat({
description:
"Annualized return on shorts assuming the current variable rate stays the same for 1 year.",
tooltipPosition: "bottom",
value: isLoadingShortRoi ? (
value: isLoadingShortRate ? (
<Skeleton className="w-20" />
) : (
<span className={classNames("flex items-center gap-1.5")}>
{formattedRate}
{shortApr?.formatted ? `${shortApr.formatted}%` : "-"}
</span>
),
},
Expand All @@ -63,11 +57,11 @@ export function ShortRateStat({
description:
"Holding period return on shorts assuming the current variable rate stays the same until maturity.",
tooltipPosition: "bottom",
value: isLoadingShortRoi ? (
value: isLoadingShortRate ? (
<Skeleton className="w-20" />
) : (
<span className={classNames("flex items-center gap-1.5")}>
{formattedRate}
{shortRoi?.formatted ? `${shortRoi.formatted}%` : "-"}
</span>
),
},
Expand Down
Loading

0 comments on commit d377536

Please sign in to comment.