From 1547068ac179e3e8f39adbc9edcfb541a5a150ad Mon Sep 17 00:00:00 2001 From: Danny Delott Date: Thu, 12 Oct 2023 15:35:36 -0700 Subject: [PATCH 1/9] Rename Longs to Buys --- .../longs/OpenLongModalButton/OpenLongModalButton.tsx | 2 +- .../src/ui/portfolio/PositionTabs/PositionTabs.tsx | 8 ++++---- .../ui/portfolio/PositionsSection/PositionsSection.tsx | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx index f3d0c0bc2..2f7d1a282 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx @@ -28,7 +28,7 @@ export function OpenLongModalButton({ onClick={() => { setSearchParams({ ...searchParams, - position: "Longs", + position: "Buys", openOrClosed: "Open", }); showModal(); diff --git a/apps/hyperdrive-trading/src/ui/portfolio/PositionTabs/PositionTabs.tsx b/apps/hyperdrive-trading/src/ui/portfolio/PositionTabs/PositionTabs.tsx index df63d4a62..75c3ca0c8 100644 --- a/apps/hyperdrive-trading/src/ui/portfolio/PositionTabs/PositionTabs.tsx +++ b/apps/hyperdrive-trading/src/ui/portfolio/PositionTabs/PositionTabs.tsx @@ -1,7 +1,7 @@ import classNames from "classnames"; import { ReactElement } from "react"; -export type PositionTab = "Longs" | "Shorts" | "LP"; +export type PositionTab = "Buys" | "Shorts" | "LP"; export function PositionTabs({ onTabClick, @@ -13,15 +13,15 @@ export function PositionTabs({ return (

Close position @@ -51,18 +45,6 @@ export function CloseLongModalButton({ />

} - > - {({ showModal }) => ( - - )} - + /> ); } diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx index 2f7d1a282..f2eecd099 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx @@ -36,15 +36,17 @@ export function OpenLongModalButton({ >
-

Buy hy{hyperdrive.baseToken.symbol}

+

+ Buy {hyperdrive.baseToken.symbol} at a discount +

Earn {fixedAPR?.formatted || "-"}% APR on{" "} {hyperdrive.baseToken.symbol}

- 1 hy{hyperdrive.baseToken.symbol} is always worth one{" "} - {hyperdrive.baseToken.symbol} at maturity. It{"'"}s a predictable - fixed rate. + 1 hy{hyperdrive.baseToken.symbol} is worth 1{" "} + {hyperdrive.baseToken.symbol} at maturity, giving you predictable + fixed rate yield.

diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityModalButton/AddLiquidityModalButton.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityModalButton/AddLiquidityModalButton.tsx index 731ee9e66..fd2644d8d 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityModalButton/AddLiquidityModalButton.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityModalButton/AddLiquidityModalButton.tsx @@ -47,10 +47,10 @@ export function AddLiquidityModalButton({ Single-sided deposit with {hyperdrive.baseToken.symbol} - Idle capital earns yield source rate + Idle liquidity earns yield source rate - No terms or rollovers, easy-to-use + No terms or manual LP rollovers
{/* Using a div styled as a button here just as a visual cue. Don't diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/CloseShortModalButton/CloseShortModalButton.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/CloseShortModalButton/CloseShortModalButton.tsx index 0ba537aae..17a70e7d6 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/CloseShortModalButton/CloseShortModalButton.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/CloseShortModalButton/CloseShortModalButton.tsx @@ -28,10 +28,7 @@ export function CloseShortModalButton({ className="daisy-btn-ghost daisy-btn-sm daisy-btn-circle daisy-btn absolute right-4 top-4" onClick={closeModal} > - +

Close position diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortModalButton/OpenShortModalButton.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortModalButton/OpenShortModalButton.tsx index c3c396e08..9a4aeb19e 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortModalButton/OpenShortModalButton.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortModalButton/OpenShortModalButton.tsx @@ -44,8 +44,8 @@ export function OpenShortModalButton({ {hyperdrive.baseToken.symbol}

- Profit when the price of hy{hyperdrive.baseToken.symbol} drops, - and also earn the yield source rate. + Profit when hy{hyperdrive.baseToken.symbol} price drops, and + maximize exposure to the yield source.

From b2eb6b6dd2340cf43e6f134ae6a707a95d660c30 Mon Sep 17 00:00:00 2001 From: Danny Delott Date: Thu, 12 Oct 2023 17:54:10 -0700 Subject: [PATCH 6/9] Refactor to react-table --- .../OpenLongsTable/OpenLongsTable.tsx | 248 ++++++++++++++---- 1 file changed, 196 insertions(+), 52 deletions(-) diff --git a/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx b/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx index a3938d659..665b528a9 100644 --- a/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx +++ b/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx @@ -1,69 +1,213 @@ /* eslint-disable react/jsx-key */ +import { Long } from "@hyperdrive/sdk"; +import { useQuery } from "@tanstack/react-query"; +import { + Row, + createColumnHelper, + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; +import classNames from "classnames"; import { ReactElement } from "react"; import { Hyperdrive } from "src/appconfig/types"; -import { - CellWithTooltip, - SortableGridTable, -} from "src/ui/base/components/tables/SortableGridTable"; -import { useOpenLongRows } from "src/ui/portfolio/OpenLongsTable/useOpenLongRows"; +import { calculateAnnualizedPercentageChange } from "src/base/calculateAnnualizedPercentageChange"; +import { convertMillisecondsToDays } from "src/base/convertMillisecondsToDays"; +import { makeQueryKey } from "src/base/makeQueryKey"; +import { formatBalance } from "src/ui/base/formatting/formatBalance"; +import { useReadHyperdrive } from "src/ui/hyperdrive/hooks/useReadHyperdrive"; +import { CloseLongModalButton } from "src/ui/hyperdrive/longs/CloseLongModalButton/CloseLongModalButton"; +import { usePreviewCloseLong } from "src/ui/hyperdrive/longs/hooks/usePreviewCloseLong"; +import { parseUnits } from "viem"; import { useAccount } from "wagmi"; -interface OpenOrdersTableProps { +interface OpenLongsTableProps { hyperdrive: Hyperdrive; } +const columnHelper = createColumnHelper(); +const columns = (hyperdrive: Hyperdrive) => [ + columnHelper.display({ + header: `ID`, + cell: ({ row }) => {Number(row.original.maturity)}, + }), + columnHelper.display({ + header: `Size (hy${hyperdrive.baseToken.symbol})`, + cell: ({ row }) => { + return ( + + {formatBalance({ + balance: row.original.bondAmount, + decimals: hyperdrive.baseToken.decimals, + places: 2, + })} + + ); + }, + }), + columnHelper.display({ + header: `Fixed rate (APR)`, + cell: ({ row }) => { + return `${calculateAnnualizedPercentageChange({ + amountBefore: row.original.baseAmountPaid, + amountAfter: row.original.bondAmount, + days: convertMillisecondsToDays(hyperdrive.termLengthMS), + })}%`; + }, + }), + columnHelper.accessor("baseAmountPaid", { + header: `Paid (${hyperdrive.baseToken.symbol})`, + cell: (baseAmountPaid) => { + const amountPaid = baseAmountPaid.getValue(); + return formatBalance({ + balance: amountPaid, + decimals: hyperdrive.baseToken.decimals, + places: 2, + }); + }, + }), + columnHelper.display({ + header: `Value (${hyperdrive.baseToken.symbol})`, + cell: ({ row }) => { + return ; + }, + }), + columnHelper.display({ + header: `Matures on`, + cell: ({ row }) => { + const maturity = new Date(Number(row.original.maturity * 1000n)); + return {maturity.toDateString()}; + }, + }), +]; export function OpenLongsTable({ hyperdrive, -}: OpenOrdersTableProps): ReactElement { +}: OpenLongsTableProps): ReactElement { const { address: account } = useAccount(); - const { openLongRows = [], openLongRowsStatus } = useOpenLongRows({ - account, - hyperdrive, + const readHyperdrive = useReadHyperdrive(hyperdrive.address); + const queryEnabled = !!readHyperdrive && !!account; + const { data: longs } = useQuery({ + queryKey: makeQueryKey("longPositions", { account }), + queryFn: queryEnabled + ? () => readHyperdrive?.getOpenLongs({ account }) + : undefined, + enabled: queryEnabled, }); + const tableInstance = useReactTable({ + columns: columns(hyperdrive), + data: longs || [], + getCoreRowModel: getCoreRowModel(), + }); + + return ( +
+ {/* Modal needs to be rendered outside of the table so that dialog can be used. Otherwise react throws a dom nesting error */} + {tableInstance.getRowModel().rows.map((row) => { + const modalId = `${row.original.assetId}`; + return ( + + ); + })} + + + {tableInstance.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {tableInstance.getRowModel().rows.map((row) => { + return ( + { + const modalId = `${row.original.assetId}`; + (window as any)[modalId].showModal(); + }} + > + <> + {row.getVisibleCells().map((cell) => { + return ( + + ); + })} + + + ); + })} + +
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} +
+ {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} +
+
+ ); +} + +function CurrentValueCell({ + row, + hyperdrive, +}: { + row: Row; + hyperdrive: Hyperdrive; +}) { + const { address: account } = useAccount(); + const { baseAmountOut } = usePreviewCloseLong({ + hyperdriveAddress: hyperdrive.address, + maturityTime: row.original.maturity, + bondAmountIn: row.original.bondAmount, + minBaseAmountOut: parseUnits("0", hyperdrive.baseToken.decimals), + destination: account, + }); + const currentValue = + baseAmountOut && + formatBalance({ + balance: baseAmountOut, + decimals: hyperdrive.baseToken.decimals, + places: 4, + }); + + const isPositiveChangeInValue = + baseAmountOut && baseAmountOut > row.original.baseAmountPaid; return ( - - ), - }, - { - cell: ( - - ), - }, - { - cell: ( - - ), - }, - { - cell: ( - - ), - }, - ]} - // cols={["Position", "Bonds", "Amount paid", "Value", "Matures on", ""]} - rows={openLongRows} - showSkeleton={openLongRowsStatus === "loading"} - /> +
+ {currentValue?.toString()} +
+ {isPositiveChangeInValue ? "+" : ""} + {baseAmountOut + ? `${formatBalance({ + balance: baseAmountOut - row.original.baseAmountPaid, + decimals: hyperdrive.baseToken.decimals, + places: 4, + })} ${hyperdrive.baseToken.symbol}` + : undefined} +
+
); } From 4f902e5c17bfb4dfac0e45efc1e592b6dd10a006 Mon Sep 17 00:00:00 2001 From: Danny Delott Date: Thu, 12 Oct 2023 18:28:26 -0700 Subject: [PATCH 7/9] Tone down the pure black --- apps/hyperdrive-trading/tailwind.config.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/hyperdrive-trading/tailwind.config.js b/apps/hyperdrive-trading/tailwind.config.js index 80dacb5b2..86b0b9950 100644 --- a/apps/hyperdrive-trading/tailwind.config.js +++ b/apps/hyperdrive-trading/tailwind.config.js @@ -61,9 +61,15 @@ module.exports = { night: { // eslint-disable-next-line @typescript-eslint/no-var-requires ...require("daisyui/src/theming/themes")["[data-theme=lofi]"], + // Tone down the pure black + neutral: "#333333", + primary: "#333333", + secondary: "#333333", + accent: "#333333", error: "#f40000", success: "#019d60", + "--tab-radius": "0.4rem", "--rounded-box": "0.4rem", // border radius rounded-box utility class, used in card and other large boxes "--rounded-btn": "0.4rem", From 3e51c51962eddf61487049050fc0c7bdf7dab4df Mon Sep 17 00:00:00 2001 From: Danny Delott Date: Fri, 13 Oct 2023 00:42:00 -0700 Subject: [PATCH 8/9] cleanup --- .../OpenLongModalButton.tsx | 8 +- .../AddLiquidityModalButton.tsx | 2 +- .../OpenShortModalButton.tsx | 10 +- .../OpenLongsTable/OpenLongsTable.tsx | 132 ++++++++++-------- .../YourBalanceWell/YourBalanceWell.tsx | 2 +- 5 files changed, 82 insertions(+), 72 deletions(-) diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx index f2eecd099..5b67fbc3d 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/longs/OpenLongModalButton/OpenLongModalButton.tsx @@ -36,8 +36,8 @@ export function OpenLongModalButton({ >
-

- Buy {hyperdrive.baseToken.symbol} at a discount +

+ Buy hy{hyperdrive.baseToken.symbol}

Earn {fixedAPR?.formatted || "-"}% APR on{" "} @@ -45,8 +45,8 @@ export function OpenLongModalButton({

1 hy{hyperdrive.baseToken.symbol} is worth 1{" "} - {hyperdrive.baseToken.symbol} at maturity, giving you predictable - fixed rate yield. + {hyperdrive.baseToken.symbol} at maturity, giving you a + predictable fixed rate yield.

diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityModalButton/AddLiquidityModalButton.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityModalButton/AddLiquidityModalButton.tsx index fd2644d8d..fd3f74fab 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityModalButton/AddLiquidityModalButton.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/AddLiquidityModalButton/AddLiquidityModalButton.tsx @@ -34,7 +34,7 @@ export function AddLiquidityModalButton({ >
-

Add Liquidity

+

Add Liquidity

Earn trading fees and interest

diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortModalButton/OpenShortModalButton.tsx b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortModalButton/OpenShortModalButton.tsx index 9a4aeb19e..33a3d9715 100644 --- a/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortModalButton/OpenShortModalButton.tsx +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/shorts/OpenShortModalButton/OpenShortModalButton.tsx @@ -38,16 +38,18 @@ export function OpenShortModalButton({ >
-

Short hy{hyperdrive.baseToken.symbol}

+

+ Short hy{hyperdrive.baseToken.symbol} +

Earn {vaultRate?.formatted}% APY on{" "} {hyperdrive.baseToken.symbol}

- Profit when hy{hyperdrive.baseToken.symbol} price drops, and - maximize exposure to the yield source. + Profit when hy{hyperdrive.baseToken.symbol} price drops, while + maximizing exposure to the yield source.

-
+
Fixed rate up, hy{hyperdrive.baseToken.symbol} price down diff --git a/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx b/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx index 665b528a9..b32a046b5 100644 --- a/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx +++ b/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx @@ -26,60 +26,67 @@ interface OpenLongsTableProps { } const columnHelper = createColumnHelper(); -const columns = (hyperdrive: Hyperdrive) => [ - columnHelper.display({ - header: `ID`, - cell: ({ row }) => {Number(row.original.maturity)}, - }), - columnHelper.display({ - header: `Size (hy${hyperdrive.baseToken.symbol})`, - cell: ({ row }) => { - return ( - - {formatBalance({ - balance: row.original.bondAmount, - decimals: hyperdrive.baseToken.decimals, - places: 2, - })} - - ); - }, - }), - columnHelper.display({ - header: `Fixed rate (APR)`, - cell: ({ row }) => { - return `${calculateAnnualizedPercentageChange({ - amountBefore: row.original.baseAmountPaid, - amountAfter: row.original.bondAmount, - days: convertMillisecondsToDays(hyperdrive.termLengthMS), - })}%`; - }, - }), - columnHelper.accessor("baseAmountPaid", { - header: `Paid (${hyperdrive.baseToken.symbol})`, - cell: (baseAmountPaid) => { - const amountPaid = baseAmountPaid.getValue(); - return formatBalance({ - balance: amountPaid, - decimals: hyperdrive.baseToken.decimals, - places: 2, - }); - }, - }), - columnHelper.display({ - header: `Value (${hyperdrive.baseToken.symbol})`, - cell: ({ row }) => { - return ; - }, - }), - columnHelper.display({ - header: `Matures on`, - cell: ({ row }) => { - const maturity = new Date(Number(row.original.maturity * 1000n)); - return {maturity.toDateString()}; - }, - }), -]; + +function getColumns(hyperdrive: Hyperdrive) { + return [ + columnHelper.display({ + header: `ID`, + cell: ({ row }) => {Number(row.original.maturity)}, + }), + columnHelper.display({ + header: `Matures on`, + cell: ({ row }) => { + const maturity = new Date(Number(row.original.maturity * 1000n)); + return {maturity.toLocaleDateString()}; + }, + }), + columnHelper.display({ + id: "size", + header: `Size (hy${hyperdrive.baseToken.symbol})`, + cell: ({ row }) => { + return ( + + {formatBalance({ + balance: row.original.bondAmount, + decimals: hyperdrive.baseToken.decimals, + places: 2, + })} + + ); + }, + }), + columnHelper.accessor("baseAmountPaid", { + id: "amountPaid", + header: `Amount paid (${hyperdrive.baseToken.symbol})`, + cell: (baseAmountPaid) => { + const amountPaid = baseAmountPaid.getValue(); + return formatBalance({ + balance: amountPaid, + decimals: hyperdrive.baseToken.decimals, + places: 2, + }); + }, + }), + columnHelper.display({ + id: "fixedRate", + header: `Fixed rate (APR)`, + cell: ({ row }) => { + return `${calculateAnnualizedPercentageChange({ + amountBefore: row.original.baseAmountPaid, + amountAfter: row.original.bondAmount, + days: convertMillisecondsToDays(hyperdrive.termLengthMS), + })}%`; + }, + }), + columnHelper.display({ + id: "value", + header: `Market value (${hyperdrive.baseToken.symbol})`, + cell: ({ row }) => { + return ; + }, + }), + ]; +} export function OpenLongsTable({ hyperdrive, }: OpenLongsTableProps): ReactElement { @@ -95,7 +102,7 @@ export function OpenLongsTable({ enabled: queryEnabled, }); const tableInstance = useReactTable({ - columns: columns(hyperdrive), + columns: getColumns(hyperdrive), data: longs || [], getCoreRowModel: getCoreRowModel(), }); @@ -114,12 +121,12 @@ export function OpenLongsTable({ /> ); })} - +
{tableInstance.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( - { const modalId = `${row.original.assetId}`; (window as any)[modalId].showModal(); @@ -191,12 +198,13 @@ function CurrentValueCell({ return (
- {currentValue?.toString()} + {currentValue?.toString()}
{isPositiveChangeInValue ? "+" : ""} diff --git a/apps/hyperdrive-trading/src/ui/portfolio/YourBalanceWell/YourBalanceWell.tsx b/apps/hyperdrive-trading/src/ui/portfolio/YourBalanceWell/YourBalanceWell.tsx index 2c9a210bf..6a5d4de3e 100644 --- a/apps/hyperdrive-trading/src/ui/portfolio/YourBalanceWell/YourBalanceWell.tsx +++ b/apps/hyperdrive-trading/src/ui/portfolio/YourBalanceWell/YourBalanceWell.tsx @@ -26,7 +26,7 @@ export function YourBalanceWell({ token }: { token: Token }): ReactElement { +
{formatBalance({ balance: balance?.value || 0n, decimals: token.decimals, From a1a821416b3065e7057cf54071ccdccc40589d5c Mon Sep 17 00:00:00 2001 From: Danny Delott Date: Fri, 13 Oct 2023 15:56:24 -0700 Subject: [PATCH 9/9] pr fixes --- .../src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx b/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx index b32a046b5..577b80f6b 100644 --- a/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx +++ b/apps/hyperdrive-trading/src/ui/portfolio/OpenLongsTable/OpenLongsTable.tsx @@ -197,7 +197,7 @@ function CurrentValueCell({ baseAmountOut && baseAmountOut > row.original.baseAmountPaid; return ( -
+
{currentValue?.toString()}
+ {header.isPlaceholder ? null : flexRender( @@ -136,7 +143,7 @@ export function OpenLongsTable({ return (