11import { fixed } from "@delvtech/fixed-point-wasm" ;
2- import { HyperdriveConfig } from "@delvtech/hyperdrive-appconfig" ;
2+ import {
3+ getYieldSource ,
4+ HyperdriveConfig ,
5+ } from "@delvtech/hyperdrive-appconfig" ;
36import { adjustAmountByPercentage } from "@delvtech/hyperdrive-js" ;
47import { InformationCircleIcon } from "@heroicons/react/24/outline" ;
5- import { MouseEvent , ReactElement , useState } from "react" ;
8+ import classNames from "classnames" ;
9+ import { MouseEvent , ReactElement } from "react" ;
610import { MAX_UINT256 } from "src/base/constants" ;
711import { formatRate } from "src/base/formatRate" ;
812import { isTestnetChain } from "src/chains/isTestnetChain" ;
@@ -32,6 +36,7 @@ import { OpenShortPreview } from "src/ui/hyperdrive/shorts/OpenShortPreview/Open
3236import { useMaxShort } from "src/ui/hyperdrive/shorts/hooks/useMaxShort" ;
3337import { useOpenShort } from "src/ui/hyperdrive/shorts/hooks/useOpenShort" ;
3438import { usePreviewOpenShort } from "src/ui/hyperdrive/shorts/hooks/usePreviewOpenShort" ;
39+ import { useShortRate } from "src/ui/hyperdrive/shorts/hooks/useShortRate" ;
3540import { PositionPicker } from "src/ui/markets/PositionPicker" ;
3641import { RewardsTooltip } from "src/ui/rewards/RewardsTooltip/RewardsTooltip" ;
3742import { useOpenShortRewards } from "src/ui/rewards/hooks/useOpenShortRewards" ;
@@ -48,8 +53,6 @@ import { useYieldSourceRate } from "src/ui/vaults/useYieldSourceRate";
4853import { formatUnits } from "viem" ;
4954import { useAccount , useChainId } from "wagmi" ;
5055
51- ( window as any ) . fixed = fixed ;
52-
5356interface 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