diff --git a/apps/hyperdrive-trading/src/ui/base/numericInput.ts b/apps/hyperdrive-trading/src/ui/base/numericInput.ts new file mode 100644 index 000000000..508bc1e4f --- /dev/null +++ b/apps/hyperdrive-trading/src/ui/base/numericInput.ts @@ -0,0 +1,5 @@ +/** + * Tailwind classes to hide the up and down arrow buttons in a numeric input + */ +export const HIDE_NUMERIC_INPUT_ARROWS_CLASS = + "[appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" as const; diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useDevLogging.ts b/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useDevLogging.ts index c52c2f1a1..30f57de0d 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useDevLogging.ts +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useDevLogging.ts @@ -1,11 +1,11 @@ import { useEffect } from "react"; import { Hyperdrive } from "src/appconfig/types"; -import { useHyperdrivePoolInfo } from "src/ui/hyperdrive/hooks//useHyperdrivePoolInfo"; -import { useHyperdrivePoolConfig } from "src/ui/hyperdrive/hooks/useHyperdrivePoolConfig"; +import { usePoolConfig } from "src/ui/hyperdrive/hooks/usePoolConfig"; +import { usePoolInfo } from "src/ui/hyperdrive/hooks/usePoolInfo"; import { formatUnits } from "viem"; export function useDevLogging(market: Hyperdrive): void { - const { poolConfig } = useHyperdrivePoolConfig(market.address); + const { poolConfig } = usePoolConfig(market.address); useEffect(() => { if (import.meta.env.DEV) { console.log("Pool Config:"); @@ -17,7 +17,7 @@ export function useDevLogging(market: Hyperdrive): void { } }, [poolConfig, market.baseToken.decimals]); - const { poolInfo } = useHyperdrivePoolInfo(market.address); + const { poolInfo } = usePoolInfo(market.address); useEffect(() => { if (import.meta.env.DEV) { console.log("Pool Info:"); diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useHyperdrivePoolConfig.ts b/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/usePoolConfig.ts similarity index 92% rename from apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useHyperdrivePoolConfig.ts rename to apps/hyperdrive-trading/src/ui/hyperdrive/hooks/usePoolConfig.ts index 9fe6eecc0..858bd1f07 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useHyperdrivePoolConfig.ts +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/usePoolConfig.ts @@ -5,7 +5,7 @@ import { useReadHyperdrive } from "src/ui/hyperdrive/hooks/useReadHyperdrive"; import { ContractFunctionResult } from "viem"; import { Address } from "wagmi"; -export function useHyperdrivePoolConfig(hyperdriveAddress: Address): { +export function usePoolConfig(hyperdriveAddress: Address): { poolConfig: | ContractFunctionResult | undefined; diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useHyperdrivePoolInfo.ts b/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/usePoolInfo.ts similarity index 74% rename from apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useHyperdrivePoolInfo.ts rename to apps/hyperdrive-trading/src/ui/hyperdrive/hooks/usePoolInfo.ts index 384248684..b8f0e710c 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/useHyperdrivePoolInfo.ts +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/hooks/usePoolInfo.ts @@ -3,14 +3,14 @@ import { QueryStatus, useQuery } from "@tanstack/react-query"; import { makeQueryKey } from "src/base/makeQueryKey"; import { useReadHyperdrive } from "src/ui/hyperdrive/hooks/useReadHyperdrive"; import { Address } from "wagmi"; -export function useHyperdrivePoolInfo(marketAddress: Address): { +export function usePoolInfo(hyperdriveAddress: Address): { poolInfo: PoolInfo | undefined; poolInfoStatus: QueryStatus; } { - const readHyperdrive = useReadHyperdrive(marketAddress); + const readHyperdrive = useReadHyperdrive(hyperdriveAddress); const queryEnabled = !!readHyperdrive; const { data: poolInfo, status: poolInfoStatus } = useQuery({ - queryKey: makeQueryKey("poolInfo", { marketAddress }), + queryKey: makeQueryKey("poolInfo", { marketAddress: hyperdriveAddress }), queryFn: queryEnabled ? () => readHyperdrive.getPoolInfo() : undefined, enabled: queryEnabled, }); diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/CloseLongForm/CloseLongForm.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/CloseLongForm/CloseLongForm.tsx index 8c59c0230..62748ea5b 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/CloseLongForm/CloseLongForm.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/CloseLongForm/CloseLongForm.tsx @@ -57,18 +57,21 @@ export function CloseLongForm({
{/* Amount to close section */} {long && ( -
-
Amount to close
+
setAmount(newAmount)} />
diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongForm/OpenLongForm.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongForm/OpenLongForm.tsx index f4a07c2d7..336ce344b 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongForm/OpenLongForm.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongForm/OpenLongForm.tsx @@ -5,8 +5,9 @@ import { ReactElement } from "react"; import { Link } from "react-router-dom"; import { Hyperdrive } from "src/appconfig/types"; import { MAX_UINT256 } from "src/base/constants"; +import { formatBalance } from "src/ui/base/formatting/formatBalance"; import { useNumericInput } from "src/ui/base/hooks/useNumericInput"; -import { useHyperdrivePoolInfo } from "src/ui/hyperdrive/hooks/useHyperdrivePoolInfo"; +import { usePoolInfo } from "src/ui/hyperdrive/hooks/usePoolInfo"; import { useMaxLong } from "src/ui/hyperdrive/longs/hooks/useMaxLong"; import { useOpenLong } from "src/ui/hyperdrive/longs/hooks/useOpenLong"; import { usePreviewOpenLong } from "src/ui/hyperdrive/longs/hooks/usePreviewOpenLong"; @@ -60,7 +61,7 @@ export function OpenLongForm({ market }: OpenLongFormProps): ReactElement { ? amountAsBigInt === undefined || amountAsBigInt < tokenAllowance : false; - const { poolInfo } = useHyperdrivePoolInfo(market.address); + const { poolInfo } = usePoolInfo(market.address); const { longAmountOut, status: openLongPreviewStatus } = usePreviewOpenLong({ market, baseAmount: amountAsBigInt, @@ -110,6 +111,16 @@ export function OpenLongForm({ market }: OpenLongFormProps): ReactElement { token={market.baseToken} value={amount ?? ""} maxValue={maxAmount} + inputLabel="Amount to spend" + stat={ + baseTokenBalance + ? `Balance: ${formatBalance({ + balance: baseTokenBalance?.value, + decimals: market.baseToken.decimals, + places: 4, + })} ${market.baseToken.symbol}` + : undefined + } onChange={(newAmount) => setAmount(newAmount)} /> {/* New Position Section */} diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityForm/AddLiquidityForm.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityForm/AddLiquidityForm.tsx index 81a59f6e9..791a776bd 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityForm/AddLiquidityForm.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityForm/AddLiquidityForm.tsx @@ -5,6 +5,7 @@ import { Link } from "react-router-dom"; import { Hyperdrive } from "src/appconfig/types"; import { MAX_UINT256 } from "src/base/constants"; import { parseUnits } from "src/base/parseUnits"; +import { formatBalance } from "src/ui/base/formatting/formatBalance"; import { useNumericInput } from "src/ui/base/hooks/useNumericInput"; import { AddLiquidityPreview } from "src/ui/hyperdrive/lp/AddLiquidityPreview/AddLiquidityPreview"; import { useAddLiquidity } from "src/ui/hyperdrive/lp/hooks/useAddLiquidity"; @@ -90,7 +91,16 @@ export function AddLiquidityForm({ token={market.baseToken} value={amount ?? ""} maxValue={baseTokenBalance?.formatted} - maxLabel="Balance" + inputLabel="Amount to deposit" + stat={ + baseTokenBalance + ? `Balance: ${formatBalance({ + balance: baseTokenBalance?.value, + decimals: market.baseToken.decimals, + places: 4, + })} ${market.baseToken.symbol}` + : undefined + } onChange={(newAmount) => setAmount(newAmount)} /> diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/RedeemWithdrawalSharesForm/RedeemWithdrawalSharesForm.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/RedeemWithdrawalSharesForm/RedeemWithdrawalSharesForm.tsx index 80a3b6bec..29b17cd8a 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/RedeemWithdrawalSharesForm/RedeemWithdrawalSharesForm.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/RedeemWithdrawalSharesForm/RedeemWithdrawalSharesForm.tsx @@ -63,11 +63,16 @@ export function RedeemWithdrawalSharesForm({ token={{ name: "Hyperdrive Withdrawal Shares", // TODO: What should the symbol be? - symbol: "Shares", + symbol: "Withdrawal shares", decimals: baseDecimals, address: "0x00", }} value={amount ?? ""} + stat={`Redeemable balance: ${formatBalance({ + balance: maxRedeemableShares ?? withdrawalShares, + decimals: baseDecimals, + places: 4, + })}`} maxValue={formatUnits( maxRedeemableShares ?? withdrawalShares, baseDecimals, diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/RemoveLiquidityForm/RemoveLiquidityForm.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/RemoveLiquidityForm/RemoveLiquidityForm.tsx index 90c97c91b..b6bafb71f 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/RemoveLiquidityForm/RemoveLiquidityForm.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/RemoveLiquidityForm/RemoveLiquidityForm.tsx @@ -77,13 +77,21 @@ export function RemoveLiquidityForm({ setAmount(newAmount)} />
diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/CloseShortForm/CloseShortForm.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/CloseShortForm/CloseShortForm.tsx index 20f45b5cf..78c37514f 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/CloseShortForm/CloseShortForm.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/CloseShortForm/CloseShortForm.tsx @@ -57,17 +57,24 @@ export function CloseShortForm({ {/* Amount to close section */} {short && (
-
Amount to close
setAmount(newAmount)} />
diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortForm/OpenShortForm.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortForm/OpenShortForm.tsx index a06f2fedb..7ae653b46 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortForm/OpenShortForm.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortForm/OpenShortForm.tsx @@ -5,7 +5,7 @@ import { Link } from "react-router-dom"; import { Hyperdrive, Token } from "src/appconfig/types"; import { MAX_UINT256 } from "src/base/constants"; import { useNumericInput } from "src/ui/base/hooks/useNumericInput"; -import { useHyperdrivePoolInfo } from "src/ui/hyperdrive/hooks/useHyperdrivePoolInfo"; +import { usePoolInfo } from "src/ui/hyperdrive/hooks/usePoolInfo"; 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"; @@ -13,7 +13,6 @@ import { OpenShortPreview } from "src/ui/hyperdrive/shorts/OpenShortPreview/Open import { useTokenAllowance } from "src/ui/token/hooks/useTokenAllowance"; import { useTokenApproval } from "src/ui/token/hooks/useTokenApproval"; import { TokenInput } from "src/ui/token/TokenInput"; -import { formatUnits } from "viem"; import { useAccount, useBalance } from "wagmi"; interface OpenShortPositionFormProps { @@ -36,7 +35,7 @@ export function OpenShortForm({ decimals: market.baseToken.decimals, }); - const { poolInfo } = useHyperdrivePoolInfo(market.address); + const { poolInfo } = usePoolInfo(market.address); const { baseAmountIn, status: openShortPreviewStatus } = usePreviewOpenShort({ market, amountBondShorts: amountAsBigInt, @@ -81,7 +80,7 @@ export function OpenShortForm({ const current = new Date(); const expiryDate = new Date(current.getTime() + market.termLengthMS); const bondToken = { - symbol: "Bonds", + symbol: `hy${market.baseToken.symbol}`, address: "0x0", decimals: 18, name: "Bonds", @@ -106,17 +105,8 @@ export function OpenShortForm({
setAmount(newAmount)} /> diff --git a/apps/hyperdrive-trading/src/ui/portfolio/OpenLpPosition/OpenLpPosition.tsx b/apps/hyperdrive-trading/src/ui/portfolio/OpenLpPosition/OpenLpPosition.tsx index a07d293cb..d779fa43f 100644 --- a/apps/hyperdrive-trading/src/ui/portfolio/OpenLpPosition/OpenLpPosition.tsx +++ b/apps/hyperdrive-trading/src/ui/portfolio/OpenLpPosition/OpenLpPosition.tsx @@ -8,7 +8,7 @@ import { SortableGridTable, } from "src/ui/base/components/tables/SortableGridTable"; import { formatBalance } from "src/ui/base/formatting/formatBalance"; -import { useHyperdrivePoolInfo } from "src/ui/hyperdrive/hooks/useHyperdrivePoolInfo"; +import { usePoolInfo } from "src/ui/hyperdrive/hooks/usePoolInfo"; import { useLpShares } from "src/ui/hyperdrive/lp/hooks/useLpShares"; import { usePreviewRedeemWithdrawalShares } from "src/ui/hyperdrive/lp/hooks/usePreviewRedeemWithdrawalShares"; import { usePreviewRemoveLiquidity } from "src/ui/hyperdrive/lp/hooks/usePreviewRemoveLiquidity"; @@ -52,7 +52,7 @@ export function OpenLpPosition({ destination: account, }); - const { poolInfo } = useHyperdrivePoolInfo(hyperdrive.address); + const { poolInfo } = usePoolInfo(hyperdrive.address); const rows: Row[] = []; if (lpShares) { diff --git a/apps/hyperdrive-trading/src/ui/token/TokenInput.tsx b/apps/hyperdrive-trading/src/ui/token/TokenInput.tsx index 08ff99dc1..b7f140f7d 100644 --- a/apps/hyperdrive-trading/src/ui/token/TokenInput.tsx +++ b/apps/hyperdrive-trading/src/ui/token/TokenInput.tsx @@ -1,14 +1,29 @@ -import { ReactElement } from "react"; +import classNames from "classnames"; +import { ReactElement, ReactNode } from "react"; import { Token } from "src/appconfig/types"; +import { HIDE_NUMERIC_INPUT_ARROWS_CLASS } from "src/ui/base/numericInput"; interface TokenInputProps { token: Token; value: string; onChange: (newAmount: string) => void; + /** + * If provided, the MAX button will be shown + */ maxValue?: string; - maxLabel?: string; - showMax?: boolean; + /** + * If provided, this text will be used instead for the input's label + */ + inputLabel?: string; + /** + * Optional stat to show, useful for things like wallet balances + */ + stat?: ReactNode; disabled?: boolean; + /** + * If true, this will render the input with error styling + */ + hasError?: boolean; autoFocus?: boolean; } @@ -17,17 +32,31 @@ export function TokenInput({ token, onChange, maxValue, - maxLabel = "Max", - showMax = true, + inputLabel = "Enter amount", + stat, + hasError = false, disabled = false, autoFocus = false, }: TokenInputProps): ReactElement { return ( -
-
+
+ +
- - {showMax && maxValue !== undefined && ( -
- {maxValue && ( - onChange(maxValue)} - className="mr-auto cursor-pointer underline hover:no-underline" - > - Max - + className={classNames( + "daisy-input-bordered daisy-input flex-1", + HIDE_NUMERIC_INPUT_ARROWS_CLASS, + { + "daisy-input-error text-error": hasError, + }, )} -

- {maxLabel}: {maxValue} -

-
- )} + value={value} + placeholder="0" + onChange={(event) => onChange(event.target.value)} + /> + {maxValue !== undefined ? ( + + ) : null} +
); }