Skip to content

Commit 6820987

Browse files
authored
Remove buttons from pool rows, switch to Short APR (#1831)
* Remove buttons from Pool rows, Make multiplier label match latest design * Display short rate for variable * Update labels for position picker * Long -> Fixed, show short rate in tx preview * Polish open long stats * Add banner for short summary in tx preview * Update text * Update labels in portfolio
1 parent d6bb922 commit 6820987

File tree

13 files changed

+157
-262
lines changed

13 files changed

+157
-262
lines changed

apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongForm/OpenLongForm.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ export function OpenLongForm({
469469
onOpenLong?.(e);
470470
}}
471471
>
472-
Open Long
472+
Buy Fixed
473473
</button>
474474
);
475475
})()}

apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongPreview/OpenLongStats.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { convertSharesToBase } from "src/hyperdrive/convertSharesToBase";
1616
import { getDepositAssets } from "src/hyperdrive/getDepositAssets";
1717
import { useAppConfigForConnectedChain } from "src/ui/appconfig/useAppConfigForConnectedChain";
1818
import { PrimaryStat } from "src/ui/base/components/PrimaryStat";
19+
import { Tooltip } from "src/ui/base/components/Tooltip/Tooltip";
1920
import { formatBalance } from "src/ui/base/formatting/formatBalance";
2021
import { formatDate } from "src/ui/base/formatting/formatDate";
2122
import { useFixedRate } from "src/ui/hyperdrive/longs/hooks/useFixedRate";
@@ -153,7 +154,15 @@ export function OpenLongStats({
153154
"text-base-content/80": !amountPaid,
154155
})}
155156
>
156-
<img src={baseToken.iconUrl} className="mr-1 h-8 rounded-full" />
157+
<Tooltip
158+
tooltip={baseToken.symbol}
159+
className="self-center font-normal"
160+
>
161+
<img
162+
src={baseToken.iconUrl}
163+
className="mr-1.5 size-7 rounded-full"
164+
/>
165+
</Tooltip>
157166
{`${formatBalance({
158167
balance: amountPaidInBase + yieldAtMaturity,
159168
decimals: baseToken.decimals,
@@ -162,7 +171,6 @@ export function OpenLongStats({
162171
</span>
163172
)
164173
}
165-
valueUnit={`${baseToken.symbol}`}
166174
subValue={
167175
// Defillama fetches the token price via {chain}:{tokenAddress}. Since the token address differs on testnet, term length is displayed instead.
168176

apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortForm/OpenShortForm.tsx

Lines changed: 107 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { fixed } from "@delvtech/fixed-point-wasm";
2-
import { HyperdriveConfig } from "@delvtech/hyperdrive-appconfig";
2+
import {
3+
getYieldSource,
4+
HyperdriveConfig,
5+
} from "@delvtech/hyperdrive-appconfig";
36
import { adjustAmountByPercentage } from "@delvtech/hyperdrive-js";
47
import { InformationCircleIcon } from "@heroicons/react/24/outline";
5-
import { MouseEvent, ReactElement, useState } from "react";
8+
import classNames from "classnames";
9+
import { MouseEvent, ReactElement } from "react";
610
import { MAX_UINT256 } from "src/base/constants";
711
import { formatRate } from "src/base/formatRate";
812
import { isTestnetChain } from "src/chains/isTestnetChain";
@@ -32,6 +36,7 @@ import { OpenShortPreview } from "src/ui/hyperdrive/shorts/OpenShortPreview/Open
3236
import { useMaxShort } from "src/ui/hyperdrive/shorts/hooks/useMaxShort";
3337
import { useOpenShort } from "src/ui/hyperdrive/shorts/hooks/useOpenShort";
3438
import { usePreviewOpenShort } from "src/ui/hyperdrive/shorts/hooks/usePreviewOpenShort";
39+
import { useShortRate } from "src/ui/hyperdrive/shorts/hooks/useShortRate";
3540
import { PositionPicker } from "src/ui/markets/PositionPicker";
3641
import { RewardsTooltip } from "src/ui/rewards/RewardsTooltip/RewardsTooltip";
3742
import { useOpenShortRewards } from "src/ui/rewards/hooks/useOpenShortRewards";
@@ -48,8 +53,6 @@ import { useYieldSourceRate } from "src/ui/vaults/useYieldSourceRate";
4853
import { formatUnits } from "viem";
4954
import { useAccount, useChainId } from "wagmi";
5055

51-
(window as any).fixed = fixed;
52-
5356
interface OpenShortPositionFormProps {
5457
hyperdrive: HyperdriveConfig;
5558
onOpenShort?: (e: MouseEvent<HTMLButtonElement>) => void;
@@ -68,6 +71,12 @@ export function OpenShortForm({
6871
chainId: hyperdrive.chainId,
6972
});
7073
const appConfig = useAppConfigForConnectedChain();
74+
const yieldSource = getYieldSource({
75+
hyperdriveChainId: hyperdrive.chainId,
76+
hyperdriveAddress: hyperdrive.address,
77+
appConfig,
78+
});
79+
7180
const { baseToken, baseTokenDepositEnabled, tokenOptions } =
7281
useTokenDepositOptions({
7382
hyperdrive,
@@ -122,23 +131,13 @@ export function OpenShortForm({
122131
tokenChainId: activeToken.chainId,
123132
});
124133

125-
// TODO: Implement the two way input switch once getMaxShort is fixed on the sdk
126-
const [activeInput, setActiveInput] = useState<"bonds" | "budget">("bonds");
127-
128134
const {
129135
amount: amountOfBondsToShort,
130136
amountAsBigInt: amountOfBondsToShortAsBigInt,
131137
setAmount: setShortAmount,
132138
} = useNumericInput({
133139
decimals: hyperdrive.decimals,
134140
});
135-
const {
136-
amount: amountToPay,
137-
amountAsBigInt: amountToPayAsBigInt,
138-
setAmount: setPaymentAmount,
139-
} = useNumericInput({
140-
decimals: hyperdrive.decimals,
141-
});
142141

143142
const {
144143
traderDeposit,
@@ -187,17 +186,23 @@ export function OpenShortForm({
187186
budget: MAX_UINT256,
188187
});
189188

190-
const { maxBondsOut: maxBondsOutFromPayment } = useMaxShort({
191-
chainId: hyperdrive.chainId,
192-
hyperdriveAddress: hyperdrive.address,
193-
budget: amountToPayAsBigInt || 0n,
194-
});
195-
196189
const hasEnoughLiquidity = getIsValidTradeSize({
197190
tradeAmount: amountOfBondsToShortAsBigInt,
198191
maxTradeSize: maxBondsOut,
199192
});
200193

194+
const defaultBondAmount =
195+
hyperdrive.decimals > 6 ? BigInt(1e15) : BigInt(1e6);
196+
const { shortApr, shortRateStatus } = useShortRate({
197+
chainId: hyperdrive.chainId,
198+
// show the market short rate (aka bond amount of 1) if the user hasn't
199+
// already entered a short size
200+
bondAmount: amountOfBondsToShortAsBigInt || defaultBondAmount,
201+
hyperdriveAddress: hyperdrive.address,
202+
timestamp: BigInt(Math.floor(Date.now() / 1000)),
203+
variableApy: vaultRate?.netVaultRate,
204+
});
205+
201206
const {
202207
setSlippage,
203208
slippage,
@@ -232,23 +237,9 @@ export function OpenShortForm({
232237
},
233238
onExecuted: () => {
234239
setShortAmount("");
235-
setPaymentAmount("");
236240
},
237241
});
238242

239-
// TODO: Implement the two way input switch once getMaxShort is fixed on the sdk
240-
// Max button is wired up to the user's balance, or the pool's max long.
241-
// Whichever is smallest.
242-
// let maxButtonValue = "0";
243-
// if (activeTokenBalance && maxBondsOut) {
244-
// maxButtonValue = formatUnits(
245-
// activeTokenBalance.value > maxBondsOut
246-
// ? maxBondsOut
247-
// : activeTokenBalance?.value,
248-
// activeToken.decimals,
249-
// );
250-
// }
251-
252243
const exposureMultiplier =
253244
amountOfBondsToShortAsBigInt && traderDeposit
254245
? fixed(amountOfBondsToShortAsBigInt, activeToken.decimals)
@@ -293,19 +284,10 @@ export function OpenShortForm({
293284
},
294285
});
295286
setActiveToken(tokenAddress);
296-
297-
// TODO: Determine if there is a bug here.
298-
setPaymentAmount("0");
299287
}}
300288
/>
301289
}
302-
value={
303-
activeInput === "bonds"
304-
? amountOfBondsToShort || ""
305-
: maxBondsOutFromPayment
306-
? formatUnits(maxBondsOutFromPayment, baseToken.decimals)
307-
: ""
308-
}
290+
value={amountOfBondsToShort || ""}
309291
settings={
310292
<div className="mb-3 flex w-full items-center justify-between">
311293
<PositionPicker hyperdrive={hyperdrive} />
@@ -342,7 +324,6 @@ export function OpenShortForm({
342324
},
343325
});
344326
setShortAmount(newAmount);
345-
setActiveInput("bonds");
346327
}}
347328
bottomLeftElement={
348329
// Defillama fetches the token price via {chain}:{tokenAddress}.
@@ -366,6 +347,7 @@ export function OpenShortForm({
366347
}
367348
/>
368349
<TokenInput
350+
disabled
369351
variant="lighter"
370352
name={`${baseToken.symbol}-input`}
371353
token={
@@ -374,23 +356,11 @@ export function OpenShortForm({
374356
activeTokenAddress={activeToken.address}
375357
onChange={(tokenAddress) => {
376358
setActiveToken(tokenAddress);
377-
setPaymentAmount("0");
378359
}}
379360
/>
380361
}
381362
inputLabel="You pay"
382-
value={
383-
activeInput === "budget"
384-
? amountToPay || "0"
385-
: formatUnits(traderDeposit || 0n, activeToken.decimals)
386-
}
387-
// This input is disabled until the getMaxShort is fixed on the sdk.
388-
disabled
389-
// maxValue={maxButtonValue}
390-
// onChange={(newAmount) => {
391-
// setActiveInput("budget");
392-
// setPaymentAmount(newAmount);
393-
// }}
363+
value={formatUnits(traderDeposit || 0n, activeToken.decimals)}
394364
bottomLeftElement={
395365
// Defillama fetches the token price via {chain}:{tokenAddress}. Since the token address differs on testnet, price display is disabled there.
396366
!isTestnetChain(hyperdrive.chainId) ? (
@@ -425,59 +395,85 @@ export function OpenShortForm({
425395
</div>
426396
}
427397
primaryStats={
428-
<div className="flex justify-between px-4 py-8">
429-
<PrimaryStat
430-
label="Variable APY"
431-
value={
432-
<span className="text-h3 font-bold">
433-
{isNewPool ? (
434-
"✨New✨"
435-
) : rewards?.length ? (
436-
<RewardsTooltip
437-
hyperdriveAddress={hyperdrive.address}
438-
position="openShort"
439-
baseRate={vaultRate?.vaultRate}
440-
netRate={vaultRate?.netVaultRate}
441-
chainId={hyperdrive.chainId}
442-
>
443-
{formatRate({ rate: vaultRate?.netVaultRate ?? 0n })}
444-
</RewardsTooltip>
445-
) : (
446-
formatRate({ rate: vaultRate?.netVaultRate ?? 0n })
447-
)}
448-
</span>
449-
}
450-
valueContainerClassName="flex items-end"
451-
unitClassName="text-h3 font-bold"
452-
subValue={
453-
<span className="gradient-text">
454-
<span className="font-bold">{`${exposureMultiplier}x`}</span>{" "}
455-
capital exposure
456-
</span>
457-
}
458-
valueLoading={longPriceStatus === "loading"}
459-
/>
460-
<div className="daisy-divider daisy-divider-horizontal" />
461-
<PrimaryStat
462-
alignment="right"
463-
label="Fixed APR Cost"
464-
value={
465-
<span className="text-h3 font-bold">
466-
{formatRate({ rate: fixedRatePaid || fixedApr?.apr || 0n })}
467-
</span>
468-
}
469-
unitClassName="mb-1 font-bold"
470-
subValue={
471-
<Tooltip
472-
position="top"
473-
tooltip="Short positions provide the fixed rate yield to Long positions. Opening a Short is a one-time cost."
474-
className="gap-1 before:text-left"
475-
>
476-
What am I paying for?{" "}
477-
<InformationCircleIcon className="size-4 text-neutral-content" />
478-
</Tooltip>
479-
}
480-
/>
398+
// TOOD: Make this it's own component, OpenShortStats
399+
<div className="flex flex-col gap-4 py-8">
400+
<div className="flex w-full justify-between px-4">
401+
<PrimaryStat
402+
label="Variable APY"
403+
value={
404+
<span className="text-h3 font-bold">
405+
{isNewPool ? (
406+
"✨New✨"
407+
) : rewards?.length ? (
408+
<RewardsTooltip
409+
hyperdriveAddress={hyperdrive.address}
410+
position="openShort"
411+
baseRate={vaultRate?.vaultRate}
412+
netRate={vaultRate?.netVaultRate}
413+
chainId={hyperdrive.chainId}
414+
>
415+
{formatRate({ rate: shortApr?.apr ?? 0n })}
416+
</RewardsTooltip>
417+
) : (
418+
`${formatRate({ rate: shortApr?.apr ?? 0n })}`
419+
)}
420+
</span>
421+
}
422+
valueContainerClassName="flex items-end"
423+
unitClassName="text-h3 font-bold"
424+
subValue={
425+
<span
426+
className={classNames("gradient-text", {
427+
invisible: !shortApr?.apr,
428+
})}
429+
>
430+
<span className="font-bold">
431+
{`${exposureMultiplier}x`}
432+
</span>{" "}
433+
capital exposure
434+
</span>
435+
}
436+
valueLoading={longPriceStatus === "loading"}
437+
/>
438+
<div className="daisy-divider daisy-divider-horizontal" />
439+
<PrimaryStat
440+
alignment="right"
441+
label="Fixed APR Cost"
442+
value={
443+
<span className="text-h3 font-bold">
444+
{formatRate({ rate: fixedRatePaid || fixedApr?.apr || 0n })}
445+
</span>
446+
}
447+
unitClassName="mb-1 font-bold"
448+
subValue={
449+
<Tooltip
450+
position="top"
451+
tooltip="Short positions provide the fixed rate yield in exchange for the variable. Opening a Short is a one-time cost."
452+
className="gap-1 before:text-left"
453+
>
454+
What am I paying for?{" "}
455+
<InformationCircleIcon className="size-4 text-neutral-content" />
456+
</Tooltip>
457+
}
458+
/>
459+
</div>
460+
{!!amountOfBondsToShortAsBigInt && hasEnoughLiquidity ? (
461+
<div className="flex items-center rounded-md bg-base-200 py-3">
462+
<p className="mx-4 text-sm leading-bodyText text-neutral-content">
463+
Earn{" "}
464+
<span className="font-bold text-white">
465+
{formatRate({ rate: shortApr?.apr ?? 0n })}
466+
</span>{" "}
467+
if <span className="font-bold">{yieldSource.shortName}</span>{" "}
468+
remains constant at{" "}
469+
<span className="mx-0.5 font-bold text-white">
470+
{vaultRate?.netVaultRate
471+
? formatRate({ rate: vaultRate.netVaultRate })
472+
: null}
473+
</span>
474+
</p>
475+
</div>
476+
) : null}
481477
</div>
482478
}
483479
transactionPreview={

0 commit comments

Comments
 (0)