Skip to content

feat(staking): add stats to navbar #2013

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

Merged
merged 2 commits into from
Oct 8, 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
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const CurrentStakeAccount = ({
return api.type === ApiStateType.Loaded && !isBlocked ? (
<div
className={clsx(
"hidden flex-col items-end justify-center text-xs xs:flex md:flex-row md:items-center md:text-sm",
"hidden flex-col items-end justify-center text-xs xs:flex xl:flex-row xl:items-center xl:text-sm",
className,
)}
{...props}
Expand Down
6 changes: 3 additions & 3 deletions apps/staking/src/components/Header/help-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { useState, useCallback } from "react";
import { MenuTrigger, Button } from "react-aria-components";

import { ProgramParameters } from "./program-pramaeters";
import { ProgramParameters } from "./program-parameters";
import { StateType, useApi } from "../../hooks/use-api";
import { GeneralFaq } from "../GeneralFaq";
import { GovernanceGuide } from "../GovernanceGuide";
Expand Down Expand Up @@ -45,9 +45,9 @@ export const HelpMenu = () => {
return (
<>
<MenuTrigger>
<Button className="group -mx-2 flex flex-row items-center gap-2 rounded-sm p-2 transition hover:bg-white/10 focus:outline-none focus-visible:ring-1 focus-visible:ring-pythpurple-400 pressed:bg-white/10 sm:-mx-4 sm:px-4">
<Button className="group -mx-2 flex flex-row items-center gap-2 rounded-sm p-2 transition hover:bg-white/10 focus:outline-none focus-visible:ring-1 focus-visible:ring-pythpurple-400 pressed:bg-white/10 md:-mx-4 md:px-4">
<QuestionMarkCircleIcon className="size-6 flex-none" />
<span className="sr-only xs:not-sr-only">Help</span>
<span className="sr-only md:not-sr-only">Help</span>
<ChevronDownIcon className="size-4 flex-none opacity-60 transition duration-300 group-data-[pressed]:-rotate-180" />
</Button>
<Menu placement="bottom end">
Expand Down
46 changes: 28 additions & 18 deletions apps/staking/src/components/Header/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CurrentStakeAccount } from "./current-stake-account";
import { HelpMenu } from "./help-menu";
import Logo from "./logo.svg";
import Logomark from "./logomark.svg";
import { Stats } from "./stats";
import { Link } from "../Link";
import { MaxWidth } from "../MaxWidth";
import { WalletButton } from "../WalletButton";
Expand All @@ -13,22 +14,31 @@ export const Header = ({
className,
...props
}: Omit<HTMLAttributes<HTMLElement>, "children">) => (
<header className={clsx("sticky top-0 w-full lg:px-4", className)} {...props}>
<div className="border-b border-neutral-600/50 bg-pythpurple-800 lg:border-x">
<MaxWidth className="flex h-header items-center justify-between gap-2 lg:-mx-4">
<Link
href="/"
className="-mx-2 h-[calc(var(--header-height)_-_0.5rem)] rounded-sm p-2 text-pythpurple-100 focus:outline-none focus-visible:ring-1 focus-visible:ring-pythpurple-400"
>
<Logo className="hidden h-full sm:block" />
<Logomark className="h-full sm:hidden" />
</Link>
<div className="flex flex-none flex-row items-stretch gap-4 sm:gap-8">
<CurrentStakeAccount />
<WalletButton className="flex-none" />
<HelpMenu />
</div>
</MaxWidth>
</div>
</header>
<>
<header
className={clsx("sticky top-0 w-full lg:px-4", className)}
{...props}
>
<div className="border-b border-neutral-600/50 bg-pythpurple-800 lg:border-x">
<MaxWidth className="flex h-header items-center justify-between gap-2 lg:-mx-4">
<div className="flex flex-row items-center gap-8 lg:gap-12">
<Link
href="/"
className="-mx-2 h-[calc(var(--header-height)_-_0.5rem)] rounded-sm p-2 text-pythpurple-100 focus:outline-none focus-visible:ring-1 focus-visible:ring-pythpurple-400"
>
<Logo className="hidden h-full lg:block" />
<Logomark className="h-full lg:hidden" />
</Link>
<Stats className="hidden gap-4 sm:flex lg:gap-6" />
</div>
<div className="flex flex-none flex-row items-stretch gap-4 md:gap-8">
<CurrentStakeAccount />
<WalletButton className="flex-none" />
<HelpMenu />
</div>
</MaxWidth>
</div>
</header>
<Stats className="border-b border-neutral-600/50 py-4 text-center sm:hidden" />
</>
);
85 changes: 85 additions & 0 deletions apps/staking/src/components/Header/stats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"use client";

import { PythStakingClient } from "@pythnetwork/staking-sdk";
import { useConnection } from "@solana/wallet-adapter-react";
import { Connection } from "@solana/web3.js";
import clsx from "clsx";
import type { HTMLProps } from "react";

import { StateType, useData } from "../../hooks/use-data";
import { Tokens } from "../Tokens";

const ONE_SECOND_IN_MS = 1000;
const ONE_MINUTE_IN_MS = 60 * ONE_SECOND_IN_MS;
const REFRESH_INTERVAL = 1 * ONE_MINUTE_IN_MS;
const INITIAL_REWARD_POOL_SIZE = 60_000_000_000_000n;

export const Stats = ({ className, ...props }: HTMLProps<HTMLDivElement>) => {
const { connection } = useConnection();
const state = useData("poolStats", () => fetchStats(connection), {
refreshInterval: REFRESH_INTERVAL,
});

return (
<div className={clsx("flex flex-row items-stretch", className)} {...props}>
<div className="flex-1 sm:flex-none">
{state.type === StateType.Loaded ? (
<Tokens className="mb-1 text-xl font-semibold leading-none">
{state.data.totalStaked}
</Tokens>
) : (
<Loading />
)}
<div className="text-xs leading-none text-pythpurple-400">
OIS Total Staked
</div>
</div>
<div className="border-l border-neutral-600/50" />
<div className="flex-1 sm:flex-none">
{state.type === StateType.Loaded ? (
<Tokens className="mb-1 text-xl font-semibold leading-none">
{state.data.rewardsDistributed}
</Tokens>
) : (
<Loading />
)}
<div className="text-xs leading-none text-pythpurple-400">
OIS Rewards Distributed
</div>
</div>
</div>
);
};

const Loading = () => (
<div className="mb-1 h-5 w-10 animate-pulse rounded-md bg-white/30" />
);

const fetchStats = async (connection: Connection) => {
const client = new PythStakingClient({ connection });
const poolData = await client.getPoolDataAccount();
const rewardCustodyAccount = await client.getRewardCustodyAccount();
const totalDelegated = sum(
poolData.delState.map(
({ totalDelegation, deltaDelegation }) =>
totalDelegation + deltaDelegation,
),
);
const totalSelfStaked = sum(
poolData.selfDelState.map(
({ totalDelegation, deltaDelegation }) =>
totalDelegation + deltaDelegation,
),
);

return {
totalStaked: totalDelegated + totalSelfStaked,
rewardsDistributed:
poolData.claimableRewards +
INITIAL_REWARD_POOL_SIZE -
rewardCustodyAccount.amount,
};
};

const sum = (values: bigint[]): bigint =>
values.reduce((acc, value) => acc + value, 0n);
2 changes: 2 additions & 0 deletions apps/staking/src/components/Tokens/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use client";

import clsx from "clsx";
import * as dnum from "dnum";
import { type ComponentProps, useMemo } from "react";
Expand Down
3 changes: 2 additions & 1 deletion apps/staking/src/components/WalletButton/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ const ButtonComponent = ({
...props
}: ButtonComponentProps) => (
<Button
className={clsx("w-36 text-sm sm:w-52 sm:text-base", className)}
className={clsx("w-36 text-sm lg:w-52 lg:text-base", className)}
size="nopad"
{...props}
>
<WalletIcon className="size-4 flex-none opacity-60" />
Expand Down
13 changes: 13 additions & 0 deletions governance/pyth_staking_sdk/src/pyth-staking-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -974,4 +974,17 @@ export class PythStakingClient {
const globalConfig = await this.getGlobalConfig();
return getMint(this.connection, globalConfig.pythTokenMint);
}

public async getRewardCustodyAccount(): Promise<Account> {
const poolConfigAddress = getPoolConfigAddress();
const config = await this.getGlobalConfig();

const rewardCustodyAccountAddress = getAssociatedTokenAddressSync(
config.pythTokenMint,
poolConfigAddress,
true,
);

return getAccount(this.connection, rewardCustodyAccountAddress);
}
}
Loading