diff --git a/apps/hyperdrive-trading/src/ui/hyperdrive/lp/hooks/useClosedLpShares.ts b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/hooks/useClosedLpShares.ts new file mode 100644 index 000000000..52ec3ed54 --- /dev/null +++ b/apps/hyperdrive-trading/src/ui/hyperdrive/lp/hooks/useClosedLpShares.ts @@ -0,0 +1,25 @@ +import { ClosedLpShares, getClosedLpSharesQuery } from "@hyperdrive/core"; +import { QueryStatus, useQuery } from "@tanstack/react-query"; +import { Address, usePublicClient } from "wagmi"; + +interface UseClosedLpSharesOptions { + account: Address | undefined; + hyperdriveAddress: Address | undefined; +} +export function useClosedLpShares({ + account, + hyperdriveAddress, +}: UseClosedLpSharesOptions): { + lpShares: ClosedLpShares[] | undefined; + lpSharesStatus: QueryStatus; +} { + const publicClient = usePublicClient(); + const { data: lpShares, status: lpSharesStatus } = useQuery( + getClosedLpSharesQuery({ + providerAddress: account, + hyperdriveAddress, + publicClient: publicClient as any, + }), + ); + return { lpShares, lpSharesStatus }; +} diff --git a/apps/hyperdrive-trading/src/ui/portfolio/ClosedLpTable/ClosedLpTable.tsx b/apps/hyperdrive-trading/src/ui/portfolio/ClosedLpTable/ClosedLpTable.tsx new file mode 100644 index 000000000..7a917182b --- /dev/null +++ b/apps/hyperdrive-trading/src/ui/portfolio/ClosedLpTable/ClosedLpTable.tsx @@ -0,0 +1,88 @@ +import { ReactElement } from "react"; +import { Hyperdrive } from "src/appconfig/types"; +import { SortableGridTable } from "src/ui/base/components/tables/SortableGridTable"; +import { formatBalance } from "src/ui/base/formatting/formatBalance"; +import { useClosedLpShares } from "src/ui/hyperdrive/lp/hooks/useClosedLpShares"; +import { useWithdrawalShares } from "src/ui/hyperdrive/lp/hooks/useWithdrawalShares"; +import { formatUnits } from "viem"; +import { useAccount } from "wagmi"; + +interface ClosedLpTablePRops { + hyperdrive: Hyperdrive; +} + +export function ClosedLpTable({ + hyperdrive, +}: ClosedLpTablePRops): ReactElement { + const { address: account } = useAccount(); + + const { lpShares } = useClosedLpShares({ + hyperdriveAddress: hyperdrive.address, + account, + }); + + // TODO: make a useClosedWithdrawalShares hook + const { withdrawalShares } = useWithdrawalShares({ + hyperdriveAddress: hyperdrive.address, + account, + }); + + return ( + [ + + LP + , + + {formatBalance( + formatUnits( + lpAmount, + (hyperdrive as Hyperdrive).baseToken.decimals, + ), + )} + , + + {`${formatBalance( + formatUnits(baseAmount, hyperdrive.baseToken.decimals), + )} ${hyperdrive.baseToken.symbol}`} + , + + {`${formatBalance( + formatUnits( + withdrawalShareAmount, + hyperdrive.baseToken.decimals, + ), + )}`} + , + + {new Date( + Number(closedTimestamp * 1000n), + ).toLocaleDateString()} + , + ], + ) + : [] + } + /> + ); +} diff --git a/apps/hyperdrive-trading/src/ui/portfolio/PositionsSection/PositionsSection.tsx b/apps/hyperdrive-trading/src/ui/portfolio/PositionsSection/PositionsSection.tsx index 6a67c522e..caf70ab52 100644 --- a/apps/hyperdrive-trading/src/ui/portfolio/PositionsSection/PositionsSection.tsx +++ b/apps/hyperdrive-trading/src/ui/portfolio/PositionsSection/PositionsSection.tsx @@ -4,6 +4,7 @@ import { ReactElement, useEffect } from "react"; import { useSearchParams } from "react-router-dom"; import { Hyperdrive } from "src/appconfig/types"; import { ClosedLongsTable } from "src/ui/portfolio/ClosedLongsTable/ClosedLongsTable"; +import { ClosedLpTable } from "src/ui/portfolio/ClosedLpTable/ClosedLpTable"; import { ClosedShortsTable } from "src/ui/portfolio/ClosedShortsTable/ClosedShortsTable"; import { OpenLongsTable } from "src/ui/portfolio/OpenLongsTable/OpenLongsTable"; import { OpenLpPosition } from "src/ui/portfolio/OpenLpPosition/OpenLpPosition"; @@ -98,8 +99,10 @@ export function PositionsSection({ return ; } case "LP": - // return Under construction; - return ; + if (activeOpenOrClosedTab === "Open") { + return ; + } + return ; default: assertNever(activePositionTab); } diff --git a/packages/hyperdrive/src/amm/lp/getClosedLpShares.ts b/packages/hyperdrive/src/amm/lp/getClosedLpShares.ts new file mode 100644 index 000000000..cb9bc7014 --- /dev/null +++ b/packages/hyperdrive/src/amm/lp/getClosedLpShares.ts @@ -0,0 +1,76 @@ +import { QueryObserverOptions } from "@tanstack/query-core"; +import { PublicClient, Address, Transport, Chain } from "viem"; +import { makeQueryKey } from "src/makeQueryKey"; +import { getRemoveLiquidityEvents } from "src/amm/lp/getRemoveLiquidityEvents"; + +export interface GetClosedLpSharesOptions { + providerAddress: Address; + hyperdriveAddress: Address; + publicClient: PublicClient; +} + +export interface ClosedLpShares { + hyperdriveAddress: `0x${string}`; + lpAmount: bigint; + baseAmount: bigint; + withdrawalShareAmount: bigint; + closedTimestamp: bigint; +} + +export async function getClosedLpShares({ + providerAddress, + hyperdriveAddress, + publicClient, +}: GetClosedLpSharesOptions): Promise { + const removeLiquidityEvents = await getRemoveLiquidityEvents({ + args: { providerAddress }, + hyperdriveAddress, + publicClient, + }); + + return Promise.all( + removeLiquidityEvents.map(async (event) => { + const { + eventData: { lpAmount, baseAmount, withdrawalShareAmount }, + } = event; + return { + hyperdriveAddress, + lpAmount, + baseAmount, + withdrawalShareAmount, + closedTimestamp: ( + await publicClient.getBlock({ + blockNumber: event.eventLog.blockNumber as bigint, + }) + ).timestamp, + }; + }), + ); +} + +export function getClosedLpSharesQuery({ + hyperdriveAddress, + publicClient, + providerAddress, +}: Partial): QueryObserverOptions< + Awaited> +> { + const queryEnabled = + !!providerAddress && !!hyperdriveAddress && !!publicClient; + + return { + enabled: queryEnabled, + queryKey: makeQueryKey("remove-liquidity", { + hyperdriveAddress, + providerAddress, + }), + queryFn: queryEnabled + ? () => + getClosedLpShares({ + providerAddress: providerAddress, + hyperdriveAddress, + publicClient, + }) + : undefined, + }; +} diff --git a/packages/hyperdrive/src/amm/lp/getRemoveLiquidityEvents.ts b/packages/hyperdrive/src/amm/lp/getRemoveLiquidityEvents.ts new file mode 100644 index 000000000..d2a3f0cf4 --- /dev/null +++ b/packages/hyperdrive/src/amm/lp/getRemoveLiquidityEvents.ts @@ -0,0 +1,59 @@ +import { HyperdriveABI } from "src/abis/Hyperdrive"; +import { + PublicClient, + Address, + decodeEventLog, + Transport, + Chain, + DecodeEventLogReturnType, + GetFilterLogsReturnType, + BlockTag, +} from "viem"; +interface CloseShortEvent { + eventData: DecodeEventLogReturnType< + typeof HyperdriveABI, + "RemoveLiquidity" + >["args"]; + eventLog: GetFilterLogsReturnType< + typeof HyperdriveABI, + "RemoveLiquidity" + >[number]; +} +interface GetRemoveLiquidityEventsOptions { + args: { providerAddress?: Address }; + fromBlock?: bigint; + toBlock?: bigint | BlockTag; + hyperdriveAddress: Address; + publicClient: PublicClient; +} + +export async function getRemoveLiquidityEvents({ + args: { providerAddress }, + fromBlock = 0n, + toBlock = "latest", + hyperdriveAddress, + publicClient, +}: GetRemoveLiquidityEventsOptions): Promise { + const closeLPLogs = await publicClient.getFilterLogs({ + filter: await publicClient.createContractEventFilter({ + abi: HyperdriveABI, + address: hyperdriveAddress, + eventName: "RemoveLiquidity", + args: { provider: providerAddress }, + fromBlock, + toBlock, + }), + }); + + return closeShortLogs.map((log) => ({ + // This is a typesafe copy of eventLog.args + eventData: decodeEventLog({ + abi: HyperdriveABI, + data: log.data, + topics: log.topics, + eventName: "RemoveLiquidity", + }).args, + + eventLog: log, + })); +} diff --git a/packages/hyperdrive/src/index.ts b/packages/hyperdrive/src/index.ts index a01a67c54..33f9b794f 100644 --- a/packages/hyperdrive/src/index.ts +++ b/packages/hyperdrive/src/index.ts @@ -34,14 +34,14 @@ export { getOpenShortsQuery, } from "src/amm/shorts/getOpenShorts"; export { getShorts } from "src/amm/shorts/getShortsOld"; - -// Liquidity -export { getLiquidity, getLiquidityQuery } from "src/amm/getLiquidity"; export { getClosedShorts, getClosedShortsQuery, } from "src/amm/shorts/getClosedShorts"; +// Liquidity +export { getLiquidity, getLiquidityQuery } from "src/amm/getLiquidity"; + // Trading Volume export { getTradingVolume, @@ -51,6 +51,11 @@ export { // LP Shares export { LP_ASSET_ID, WITHDRAW_SHARES_ASSET_ID } from "src/amm/lp/constants"; export { getLpShares, getLpSharesQuery } from "src/amm/lp/getLpShares"; +export { + getClosedLpShares, + getClosedLpSharesQuery, +} from "src/amm/lp/getClosedLpShares"; +export type { ClosedLpShares } from "src/amm/lp/getClosedLpShares"; export type { GetLpSharesOptions } from "src/amm/lp/getLpShares"; // Withdraw Shares