Skip to content

feat: fetch data from pythnet #2005

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 10 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
58 changes: 43 additions & 15 deletions apps/staking/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
getAmountByTargetAndState,
getCurrentEpoch,
PositionState,
PythnetClient,
PythStakingClient,
type StakeAccountPositions,
} from "@pythnetwork/staking-sdk";
Expand Down Expand Up @@ -43,6 +44,8 @@ type Data = {
cooldown2: bigint;
};
yieldRate: bigint;
m: bigint;
z: bigint;
integrityStakingPublishers: {
name: string | undefined;
publicKey: PublicKey;
Expand Down Expand Up @@ -96,18 +99,29 @@ export const getStakeAccount = async (

export const loadData = async (
client: PythStakingClient,
pythnetClient: PythnetClient,
hermesClient: HermesClient,
stakeAccount?: PublicKey | undefined,
): Promise<Data> =>
stakeAccount === undefined
? loadDataNoStakeAccount(client, hermesClient)
: loadDataForStakeAccount(client, hermesClient, stakeAccount);
? loadDataNoStakeAccount(client, pythnetClient, hermesClient)
: loadDataForStakeAccount(
client,
pythnetClient,
hermesClient,
stakeAccount,
);

const loadDataNoStakeAccount = async (
client: PythStakingClient,
pythnetClient: PythnetClient,
hermesClient: HermesClient,
): Promise<Data> => {
const { publishers, ...baseInfo } = await loadBaseInfo(client, hermesClient);
const { publishers, ...baseInfo } = await loadBaseInfo(
client,
pythnetClient,
hermesClient,
);

return {
...baseInfo,
Expand All @@ -128,6 +142,7 @@ const loadDataNoStakeAccount = async (

const loadDataForStakeAccount = async (
client: PythStakingClient,
pythnetClient: PythnetClient,
hermesClient: HermesClient,
stakeAccount: PublicKey,
): Promise<Data> => {
Expand All @@ -138,7 +153,7 @@ const loadDataForStakeAccount = async (
claimableRewards,
stakeAccountPositions,
] = await Promise.all([
loadBaseInfo(client, hermesClient),
loadBaseInfo(client, pythnetClient, hermesClient),
client.getStakeAccountCustody(stakeAccount),
client.getUnlockSchedule(stakeAccount),
client.getClaimableRewards(stakeAccount),
Expand Down Expand Up @@ -197,36 +212,49 @@ const loadDataForStakeAccount = async (

const loadBaseInfo = async (
client: PythStakingClient,
pythnetClient: PythnetClient,
hermesClient: HermesClient,
) => {
const [publishers, walletAmount, poolConfig, currentEpoch] =
const [publishers, walletAmount, poolConfig, currentEpoch, parameters] =
await Promise.all([
loadPublisherData(client, hermesClient),
loadPublisherData(client, pythnetClient, hermesClient),
client.getOwnerPythBalance(),
client.getPoolConfigAccount(),
getCurrentEpoch(client.connection),
pythnetClient.getStakeCapParameters(),
]);

return { yieldRate: poolConfig.y, walletAmount, publishers, currentEpoch };
return {
yieldRate: poolConfig.y,
walletAmount,
publishers,
currentEpoch,
m: parameters.m,
z: parameters.z,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit but I'd love to have more useful names for these if possible

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find a name for z as much as I try

};
};

const loadPublisherData = async (
client: PythStakingClient,
pythnetClient: PythnetClient,
hermesClient: HermesClient,
) => {
const [poolData, publisherRankings, publisherCaps] = await Promise.all([
client.getPoolDataAccount(),
getPublisherRankings(),
hermesClient.getLatestPublisherCaps({
parsed: true,
}),
]);
const [poolData, publisherRankings, publisherCaps, publisherNumberOfSymbols] =
await Promise.all([
client.getPoolDataAccount(),
getPublisherRankings(),
hermesClient.getLatestPublisherCaps({
parsed: true,
}),
pythnetClient.getPublisherNumberOfSymbols(),
]);

return extractPublisherData(poolData).map((publisher) => {
const publisherPubkeyString = publisher.pubkey.toBase58();
const publisherRanking = publisherRankings.find(
(ranking) => ranking.publisher === publisherPubkeyString,
);
const numberOfSymbols = publisherNumberOfSymbols[publisherPubkeyString];
const apyHistory = publisher.apyHistory.map(({ epoch, apy, selfApy }) => ({
date: epochToDate(epoch + 1n),
apy,
Expand All @@ -236,7 +264,7 @@ const loadPublisherData = async (
return {
apyHistory,
name: undefined, // TODO
numFeeds: publisherRanking?.numSymbols ?? 0,
numFeeds: numberOfSymbols ?? 0,
poolCapacity: getPublisherCap(publisherCaps, publisher.pubkey),
poolUtilization: publisher.totalDelegation,
poolUtilizationDelta: publisher.totalDelegationDelta,
Expand Down
27 changes: 27 additions & 0 deletions apps/staking/src/components/Header/help-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ import {
import { useState, useCallback } from "react";
import { MenuTrigger, Button } from "react-aria-components";

import { ProgramParameters } from "./program-pramaeters";
import { StateType, useApi } from "../../hooks/use-api";
import { GeneralFaq } from "../GeneralFaq";
import { GovernanceGuide } from "../GovernanceGuide";
import { Menu, MenuItem, Section, Separator } from "../Menu";
import { OracleIntegrityStakingGuide } from "../OracleIntegrityStakingGuide";
import { PublisherFaq } from "../PublisherFaq";

export const HelpMenu = () => {
const api = useApi();
const [faqOpen, setFaqOpen] = useState(false);
const openFaq = useCallback(() => {
setFaqOpen(true);
Expand All @@ -34,6 +37,11 @@ export const HelpMenu = () => {
setPublisherFaqOpen(true);
}, [setPublisherFaqOpen]);

const [parametersOpen, setParametersOpen] = useState(false);
const openParameters = useCallback(() => {
setParametersOpen(true);
}, [setParametersOpen]);

return (
<>
<MenuTrigger>
Expand Down Expand Up @@ -65,6 +73,17 @@ export const HelpMenu = () => {
Data Publisher Guide
</MenuItem>
</Section>
{(api.type === StateType.Loaded ||
api.type === StateType.LoadedNoStakeAccount) && (
<>
<Separator />
<Section>
<MenuItem onAction={openParameters}>
Current Program Parameters
</MenuItem>
</Section>
</>
)}
</Menu>
</MenuTrigger>
<GeneralFaq isOpen={faqOpen} onOpenChange={setFaqOpen} />
Expand All @@ -80,6 +99,14 @@ export const HelpMenu = () => {
isOpen={publisherFaqOpen}
onOpenChange={setPublisherFaqOpen}
/>
{(api.type === StateType.Loaded ||
api.type === StateType.LoadedNoStakeAccount) && (
<ProgramParameters
api={api}
isOpen={parametersOpen}
onOpenChange={setParametersOpen}
/>
)}
</>
);
};
101 changes: 101 additions & 0 deletions apps/staking/src/components/Header/program-pramaeters.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import type { ComponentProps, ReactNode } from "react";

import type { StateType, States } from "../../hooks/use-api";
import { StateType as DataStateType, useData } from "../../hooks/use-data";
import { tokensToString } from "../../tokens";
import { Link } from "../Link";
import { ModalDialog } from "../ModalDialog";
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;

type Props = Omit<ComponentProps<typeof ModalDialog>, "title" | "children"> & {
api: States[StateType.Loaded] | States[StateType.LoadedNoStakeAccount];
};

export const ProgramParameters = ({ api, ...props }: Props) => {
const data = useData(api.dashboardDataCacheKey, api.loadData, {
refreshInterval: REFRESH_INTERVAL,
});

return (
<ModalDialog
title="Program Parameters"
description={
<>
See the current program parameters. For more details, see{" "}
<Link
href="https://docs.pyth.network/home/oracle-integrity-staking/mathematical-representation"
className="underline"
target="_blank"
>
the docs
</Link>
</>
}
{...props}
>
<ul className="mb-4 mt-8 flex flex-col gap-4 sm:mb-8 sm:mt-16">
<Parameter
value={
data.type === DataStateType.Loaded ? (
<Tokens>{data.data.m}</Tokens>
) : (
<Loading />
)
}
variable="M"
>
A constant parameter representing the target stake per symbol
</Parameter>
<Parameter
value={
data.type === DataStateType.Loaded ? (
data.data.z.toString()
) : (
<Loading />
)
}
variable="Z"
>
A constant parameter to control cap contribution from symbols with a
low number of publishers
</Parameter>
<Parameter
value={
data.type === DataStateType.Loaded ? (
`${tokensToString(data.data.yieldRate * 100n)}% / epoch`
) : (
<Loading />
)
}
variable="y"
>
The cap to the rate of rewards for any pool
</Parameter>
</ul>
</ModalDialog>
);
};

type ParameterProps = {
value: ReactNode;
variable: ReactNode;
children: ReactNode;
};

const Parameter = ({ variable, value, children }: ParameterProps) => (
<li className="relative rounded-md bg-white/5 p-5">
<div className="absolute right-2 top-2 grid size-6 place-content-center rounded-md bg-pythpurple-100 text-center text-sm font-semibold text-pythpurple-950">
{variable}
</div>
<div className="mb-2 text-2xl font-semibold leading-none">{value}</div>
<p className="max-w-sm text-sm opacity-60">{children}</p>
</li>
);

const Loading = () => (
<div className="h-6 w-10 animate-pulse rounded-md bg-white/30" />
);
3 changes: 2 additions & 1 deletion apps/staking/src/components/Root/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
WALLETCONNECT_PROJECT_ID,
MAINNET_RPC,
HERMES_URL,
PYTHNET_RPC,
} from "../../config/server";
import { ApiProvider } from "../../hooks/use-api";
import { LoggerProvider } from "../../hooks/use-logger";
Expand Down Expand Up @@ -79,7 +80,7 @@ const HtmlWithProviders = ({ lang, ...props }: HTMLProps<HTMLHtmlElement>) => (
walletConnectProjectId={WALLETCONNECT_PROJECT_ID}
mainnetRpc={MAINNET_RPC}
>
<ApiProvider hermesUrl={HERMES_URL}>
<ApiProvider hermesUrl={HERMES_URL} pythnetRpcUrl={PYTHNET_RPC}>
<ToastProvider>
<html lang={lang} {...props} />
</ToastProvider>
Expand Down
1 change: 1 addition & 0 deletions apps/staking/src/config/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const WALLETCONNECT_PROJECT_ID = demandInProduction(
"WALLETCONNECT_PROJECT_ID",
);
export const MAINNET_RPC = process.env.MAINNET_RPC;
export const PYTHNET_RPC = getOr("PYTHNET_RPC", "https://pythnet.rpcpool.com");
export const HERMES_URL = getOr("HERMES_URL", "https://hermes.pyth.network");
export const BLOCKED_REGIONS = transformOr("BLOCKED_REGIONS", fromCsv, []);
export const IP_ALLOWLIST = transformOr("IP_ALLOWLIST", fromCsv, []);
Expand Down
Loading
Loading