Skip to content

Polish open and close position modals #1188

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 20, 2024
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
3 changes: 2 additions & 1 deletion apps/hyperdrive-trading/src/ui/base/components/Stat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export function Stat({
"flex w-full items-center whitespace-pre-wrap ease-in-out",
{
"flex-col": !horizontal,
"flex-row-reverse gap-2": horizontal,
"flex-row-reverse": horizontal,
"gap-2": horizontal && label,
"gap-0.5": size === "xsmall",
},
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ export function MaturesOnCell({
const isTermComplete = maturity < (currentBlock?.timestamp || 0n);
const maturityDateMS = maturity * 1000n;

const remainingTime = getRemainingTimeLabel(Number(maturity));
const remainingTime = getRemainingTimeLabel({
maturitySeconds: Number(maturity),
});

return (
<div className="daisy-stat flex flex-row p-0 xl:flex-col">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import { calculateTimeLeft } from "src/base/calculateTimeLeft";

export function getRemainingTimeLabel(maturitySeconds: number): string {
/**
* @param options.maturitySeconds The timestamp in seconds when time runs out
* @param options.condensed Show condensed labels when showing both hours and minutes
* @returns string
*/
export function getRemainingTimeLabel({
maturitySeconds,
condensed = false,
}: {
maturitySeconds: number;
condensed?: boolean;
}): string {
const nowSeconds = Date.now() / 1000;
const isTermComplete = maturitySeconds < nowSeconds;
const { days, hours, minutes } = calculateTimeLeft(
Expand All @@ -16,6 +27,11 @@ export function getRemainingTimeLabel(maturitySeconds: number): string {
return `${days} days left`;
}

// Condensed only needed when showing both hours and minutes.
if (hours > 0 && condensed) {
return `${hours}h ${minutes}m left`;
}
Comment on lines +30 to +33
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: A small doc block over the option could help

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh good call


if (hours > 0) {
return `${hours} hours, ${minutes} minutes left`;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Long } from "@delvtech/hyperdrive-viem";
import { XMarkIcon } from "@heroicons/react/24/solid";
import { CheckIcon, XMarkIcon } from "@heroicons/react/24/solid";
import {
EmptyExtensions,
HyperdriveConfig,
Expand Down Expand Up @@ -54,14 +54,26 @@ export function CloseLongModalButton({
<div className="mt-5 flex w-full flex-wrap justify-between gap-4">
<div
className={classNames("daisy-badge daisy-badge-lg", {
"text-success": isMature,
"daisy-badge-neutral text-success": isMature,
})}
>
<Stat
horizontal
size="small"
label={isMature ? "" : "Term:"}
value={getRemainingTimeLabel(Number(long.maturity))}
label={isMature ? undefined : "Term:"}
value={
<span
className={classNames("flex items-center", {
"font-normal": isMature,
})}
>
{isMature ? <CheckIcon className="mr-2 h-4" /> : undefined}
{getRemainingTimeLabel({
maturitySeconds: Number(long.maturity),
condensed: true,
})}
</span>
}
/>
</div>
<div className="daisy-badge daisy-badge-lg">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,18 +242,8 @@ export function OpenLongForm({
spotRateAfterOpen={spotRateAfterOpen}
curveFee={curveFee}
activeToken={activeToken}
long={{
bondAmount: bondsReceived || 0n,
assetId: 0n,
baseAmountPaid: depositAmountAsBigInt || 0n,
maturity: BigInt(
Math.round(
(Date.now() +
Number(hyperdrive.poolConfig.positionDuration * 1000n)) /
1000,
),
),
}}
amountPaid={depositAmountAsBigInt || 0n}
bondAmount={bondsReceived || 0n}
openLongPreviewStatus={openLongPreviewStatus}
asBase={activeToken.address === baseToken.address}
vaultSharePrice={poolInfo?.vaultSharePrice}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Long, calculateAprFromPrice } from "@delvtech/hyperdrive-viem";
import { calculateAprFromPrice } from "@delvtech/hyperdrive-viem";
import { ArrowRightIcon } from "@heroicons/react/16/solid";
import {
HyperdriveConfig,
Expand All @@ -20,7 +20,8 @@ import { useFixedRate } from "src/ui/hyperdrive/longs/hooks/useFixedRate";

interface OpenLongPreviewProps {
hyperdrive: HyperdriveConfig;
long: Long;
bondAmount: bigint;
amountPaid: bigint;
openLongPreviewStatus: "error" | "idle" | "loading" | "success";
spotRateAfterOpen: bigint | undefined;
activeToken: TokenConfig<any>;
Expand All @@ -31,8 +32,9 @@ interface OpenLongPreviewProps {

export function OpenLongPreview({
hyperdrive,
long,
openLongPreviewStatus,
amountPaid,
bondAmount,
spotRateAfterOpen,
activeToken,
curveFee,
Expand All @@ -51,14 +53,23 @@ export function OpenLongPreview({
const { fixedApr } = useFixedRate(hyperdrive.address);

const isBaseAmount = asBase || sharesToken.extensions.isSharesPeggedToBase;
const amountPaidInBase = isBaseAmount
? amountPaid
: convertSharesToBase({
sharesAmount: amountPaid,
vaultSharePrice: vaultSharePrice,
decimals: baseToken.decimals,
});
const yieldAtMaturity = bondAmount - amountPaidInBase;
Comment on lines 55 to +62
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this replacing the prepareAmountIn? It seems easy enough to do inline. Although, refactoring or extending this logic will be harder if it's ever needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is different. prepareAmountIn will convert a shares input amount (steth) into what the contracts take: (lidoShares). In this case, we simply want to the know the value of the shares input in base.


return (
<div className="flex flex-col gap-3.5 px-2">
<div className="flex flex-col gap-3">
<LabelValue
label="You spend"
value={
<span>{`${formatBalance({
balance: long.baseAmountPaid,
balance: amountPaid,
decimals: baseToken.decimals,
places: baseToken.places,
})} ${activeToken.symbol}`}</span>
Expand All @@ -71,7 +82,7 @@ export function OpenLongPreview({
<Skeleton width={100} />
) : (
<span className="font-bold">{`${formatBalance({
balance: long.bondAmount,
balance: bondAmount,
decimals: baseToken.decimals,
places: baseToken.places,
})} hy${baseToken.symbol}`}</span>
Expand Down Expand Up @@ -109,21 +120,13 @@ export function OpenLongPreview({
className="gradient-text daisy-tooltip daisy-tooltip-top daisy-tooltip-left cursor-help border-b border-dashed border-current before:border"
data-tip="Your net fixed rate after pool fees and slippage are applied."
>
{long.bondAmount > 0
{bondAmount > 0
? `${formatRate(
calculateAprFromPrice({
positionDuration:
hyperdrive.poolConfig.positionDuration || 0n,
baseAmount: isBaseAmount
? long.baseAmountPaid
: // TODO: move sharesAmountPaid into the sdk's Long interface
// instead of converting here
convertSharesToBase({
sharesAmount: long.baseAmountPaid,
vaultSharePrice: vaultSharePrice,
decimals: activeToken.decimals,
}),
bondAmount: long.bondAmount,
baseAmount: amountPaidInBase,
bondAmount: bondAmount,
}),
baseToken.decimals,
)}%`
Expand All @@ -142,30 +145,12 @@ export function OpenLongPreview({
className="daisy-tooltip daisy-tooltip-top daisy-tooltip-left cursor-help before:border"
data-tip={`Total ${baseToken.symbol} expected in return at the end of the term, excluding fees.`}
>
{long.bondAmount > 0 ? (
{bondAmount > 0 ? (
<span className="cursor-help border-b border-dashed border-success text-success">
{long.bondAmount -
(isBaseAmount
? long.baseAmountPaid
: convertSharesToBase({
sharesAmount: long.baseAmountPaid,
vaultSharePrice: vaultSharePrice,
decimals: baseToken.decimals,
}))
? "+"
: ""}
{long.baseAmountPaid
{yieldAtMaturity
? // TODO: Add ROI here in parenthesis after the yield amount
`${formatBalance({
balance:
long.bondAmount -
(isBaseAmount
? long.baseAmountPaid
: convertSharesToBase({
sharesAmount: long.baseAmountPaid,
vaultSharePrice: vaultSharePrice,
decimals: baseToken.decimals,
})),
`+${formatBalance({
balance: yieldAtMaturity,
decimals: baseToken.decimals,
places: baseToken.places,
})} ${baseToken.symbol}`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { OpenShort } from "@delvtech/hyperdrive-viem";
import { XMarkIcon } from "@heroicons/react/24/solid";
import { CheckIcon, XMarkIcon } from "@heroicons/react/24/solid";
import {
EmptyExtensions,
HyperdriveConfig,
Expand Down Expand Up @@ -51,14 +51,26 @@ export function CloseShortModalButton({
<div className="mt-5 flex w-full flex-wrap justify-between gap-4">
<div
className={classNames("daisy-badge daisy-badge-lg", {
"text-success": isMature,
"daisy-badge-neutral text-success": isMature,
})}
>
<Stat
horizontal
size="small"
label={isMature ? "" : "Term:"}
value={getRemainingTimeLabel(Number(short.maturity))}
label={isMature ? undefined : "Term:"}
value={
<span
className={classNames("flex items-center", {
"font-normal": isMature,
})}
>
{isMature ? <CheckIcon className="mr-2 h-4" /> : undefined}
{getRemainingTimeLabel({
maturitySeconds: Number(short.maturity),
condensed: true,
})}
</span>
}
/>
</div>
<div className="daisy-badge daisy-badge-lg">
Expand Down
Loading