Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Pool Information section to details page #1671

Merged
merged 3 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 33 additions & 31 deletions apps/hyperdrive-trading/src/hyperdrive/getLpApy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,78 +40,80 @@
}
);

/**
* Calculates APY metrics for a Hyperdrive liquidity pool
*/
export async function getLpApy({
readHyperdrive,
hyperdrive,
}: {
readHyperdrive: ReadHyperdrive;
hyperdrive: HyperdriveConfig;
}): Promise<LpApyResult> {
// Get current block and configuration
const currentBlock = (await readHyperdrive.drift.getBlock()) as Block;
const currentBlockNumber = currentBlock.blockNumber!;
// Appconfig tells us how many days to look back for historical rates
const chainConfig = appConfig.chains[hyperdrive.chainId];

// Get yield source and calculate historical block range
const yieldSource = getYieldSource({
hyperdriveAddress: hyperdrive.address,
hyperdriveChainId: hyperdrive.chainId,
appConfig,
});

const numBlocksForHistoricalRate = isForkChain(hyperdrive.chainId)
? 1000n // roughly 3 hours for cloudchain
: appConfig.chains[hyperdrive.chainId].dailyAverageBlocks *
BigInt(yieldSource.historicalRatePeriod);
: chainConfig.dailyAverageBlocks * BigInt(yieldSource.historicalRatePeriod);

const targetFromBlock = currentBlockNumber - numBlocksForHistoricalRate;

// Check if pool is newer than one day
const blocksSinceInitialization =
currentBlockNumber - hyperdrive.initializationBlock;
const isNew = blocksSinceInitialization < chainConfig.dailyAverageBlocks;

let lpApy: bigint | undefined;
let netLpApy: bigint | undefined;

const blocksSinceInitialization =
(currentBlockNumber || 0n) - hyperdrive.initializationBlock;
const isYoungerThanOneDay =
blocksSinceInitialization <
appConfig.chains[hyperdrive.chainId].dailyAverageBlocks;

// if the pool was deployed less than one day ago, it's new.
if (!isYoungerThanOneDay) {
const lpApyResult = await readHyperdrive.getLpApy({
fromBlock: [31337].includes(hyperdrive.chainId)
? // local devnets don't have a lot of blocks, so start from the beginning
1n
: targetFromBlock,
});
// Calculate APY for established pools
if (!isNew) {
// Calculate base LP APY
const fromBlock = [31337].includes(hyperdrive.chainId)
? 1n
: targetFromBlock;
const lpApyResult = await readHyperdrive.getLpApy({ fromBlock });
lpApy = lpApyResult.lpApy;
netLpApy = lpApy;

// Add rewards APY if available
const publicClient = getPublicClient(wagmiConfig as any, {

Check warning on line 90 in apps/hyperdrive-trading/src/hyperdrive/getLpApy.ts

View workflow job for this annotation

GitHub Actions / verify (lint)

Unexpected any. Specify a different type
chainId: hyperdrive.chainId,
}) as PublicClient;

const rewardsFn = getRewardsFn({
yieldSourceId: hyperdrive.yieldSource,
appConfig,
});

if (rewardsFn) {
const rewards = await rewardsFn(publicClient);
rewards?.forEach((reward) => {
if (reward.type === "transferableToken") {
netLpApy = fixed(reward.apy).add(
// safe to cast because if we get here then lpApy has been set
netLpApy as bigint,
).bigint;
netLpApy = fixed(reward.apy).add(netLpApy as bigint).bigint;
}
});
}
}

// Figure out if the pool is younger than 1 rate period
// Calculate rate period based on pool age
const isPoolYoungerThanOneRatePeriod =
hyperdrive.initializationBlock > targetFromBlock;

// If we don't have enough blocks to go back 1 full historical period, then
// grab the all-time rate instead.
let ratePeriodDays = yieldSource.historicalRatePeriod;
if (isPoolYoungerThanOneRatePeriod) {
ratePeriodDays = convertMillisecondsToDays(
Date.now() - Number(hyperdrive.initializationTimestamp * 1000n),
);
}
const ratePeriodDays = isPoolYoungerThanOneRatePeriod
? convertMillisecondsToDays(
Date.now() - Number(hyperdrive.initializationTimestamp * 1000n),
)
: yieldSource.historicalRatePeriod;

return {
lpApy,
Expand Down
7 changes: 7 additions & 0 deletions apps/hyperdrive-trading/src/ui/analytics/Plausible.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ export interface PlausibleEventParamsMap {
};
};

aboutPoolOpen: {
props: {
chainId: number;
poolAddress: Address;
};
};

positionTypeChange: {
props: {
chainId: number;
Expand Down
128 changes: 128 additions & 0 deletions apps/hyperdrive-trading/src/ui/markets/PoolDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import {
appConfig,
getBaseToken,
getToken,
getYieldSource,
HyperdriveConfig,
makeAddressUrl,
} from "@delvtech/hyperdrive-appconfig";
import { ArrowLeftIcon } from "@heroicons/react/16/solid";
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
import { Link, useSearch } from "@tanstack/react-router";
import classNames from "classnames";
import { ReactElement } from "react";
import { ExternalLink } from "src/ui/analytics/ExternalLink";
import { AccordionSection2 } from "src/ui/base/components/AccordionSection/AccordionSection";
import CustomBanner from "src/ui/base/components/CustomBanner";
import { LabelValue } from "src/ui/base/components/LabelValue";
import { formatAddress } from "src/ui/base/formatting/formatAddress";
import { useMarketState } from "src/ui/hyperdrive/hooks/useMarketState";
import { OpenLongForm } from "src/ui/hyperdrive/longs/OpenLongForm/OpenLongForm";
import { AddLiquidityForm } from "src/ui/hyperdrive/lp/AddLiquidityForm/AddLiquidityForm";
Expand Down Expand Up @@ -75,11 +82,132 @@ export function PoolDetails({
}
})()}

<AboutThisPool hyperdrive={hyperdrive} />
<FAQ />
</div>
</div>
);
}
function AboutThisPool({
hyperdrive,
}: {
hyperdrive: HyperdriveConfig;
}): ReactElement {
const baseToken = getBaseToken({
appConfig,
hyperdriveAddress: hyperdrive.address,
hyperdriveChainId: hyperdrive.chainId,
});
const sharesToken = getToken({
appConfig,
chainId: hyperdrive.chainId,
tokenAddress: hyperdrive.poolConfig.vaultSharesToken,
});
const chainConfig = appConfig.chains[hyperdrive.chainId];
return (
// Add mx-2 so this lines up with the rest of the form
<div className="mx-2 flex flex-col gap-6">
<div className="flex flex-col gap-6">
<AccordionSection2
heading={
<div
className={classNames(
"pb-1 text-left text-md font-normal text-base-content/80 hover:text-white group-data-[open]:text-white",
)}
>
<h4>Pool Information</h4>
</div>
}
onChange={(open) =>
open &&
window.plausible("aboutPoolOpen", {
props: {
chainId: hyperdrive.chainId,
poolAddress: hyperdrive.address,
},
})
}
>
<div className="flex flex-col gap-4 text-md leading-bodyText text-base-content/80">
<LabelValue
label="Chain"
value={
<div className="flex items-center gap-2">
<img
src={chainConfig.iconUrl}
className="size-6 rounded-full"
/>
{chainConfig.name}
</div>
}
/>
<LabelValue
label="Pool address"
value={
<ExternalLink
href={makeAddressUrl(hyperdrive.address, chainConfig)}
className="daisy-link"
newTab
>
<div className="flex items-center gap-2">
{formatAddress(hyperdrive.address)}
<ArrowTopRightOnSquareIcon className="-mt-0.5 inline h-4" />
</div>
</ExternalLink>
}
/>
<LabelValue
label="Base token"
value={
<ExternalLink
href={makeAddressUrl(baseToken.address, chainConfig)}
className="daisy-link"
newTab
>
<div
className="daisy-tooltip daisy-tooltip-left flex items-center gap-2"
data-tip={baseToken.name}
>
<img
src={baseToken.iconUrl}
className="size-6 rounded-full"
/>
{formatAddress(baseToken.address)}
<ArrowTopRightOnSquareIcon className="-mt-0.5 inline h-4" />
</div>
</ExternalLink>
}
/>
{sharesToken ? (
<LabelValue
label="Shares token"
value={
<ExternalLink
href={makeAddressUrl(sharesToken.address, chainConfig)}
newTab
className="daisy-link"
>
<div
className="daisy-tooltip daisy-tooltip-left flex items-center gap-2"
data-tip={sharesToken.name}
>
<img
src={sharesToken.iconUrl}
className="size-6 rounded-full"
/>
{formatAddress(sharesToken.address)}
<ArrowTopRightOnSquareIcon className="-mt-0.5 inline h-4" />
</div>
</ExternalLink>
}
/>
) : null}
</div>
</AccordionSection2>
</div>
</div>
);
}
function FAQ() {
const { address: account } = useAccount();
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ export interface Reward {
chainId: number;
claimContract: Address;
claimable: bigint;
total: bigint;
claimed: bigint;
claimableLastUpdated: number;
rewardToken: Address;
merkleProof: string[] | null;
merkleProofLastUpdated: number;
Expand All @@ -31,9 +28,6 @@ function getDummyRewardsResponse(account: Address) {
chainId: base.id,
claimContract: zeroAddress,
claimable: parseFixed("1000000").bigint,
total: parseFixed("1000000").bigint,
claimed: parseFixed("0").bigint,
claimableLastUpdated: 123456789,
rewardToken: appConfig.tokens.find(
(token) => token.chainId === 8453 && token.symbol === "MORPHO",
)!.address,
Expand All @@ -45,10 +39,7 @@ function getDummyRewardsResponse(account: Address) {
// to the claimContract yet
chainId: base.id,
claimContract: zeroAddress,
total: parseFixed("1000000").bigint,
claimed: parseFixed("0").bigint,
claimable: parseFixed("0").bigint,
claimableLastUpdated: 123456789,
rewardToken: appConfig.tokens.find(
(token) => token.chainId === 8453 && token.symbol === "USDC",
)!.address,
Expand Down
Loading