diff --git a/apps/staking/src/api.ts b/apps/staking/src/api.ts index 26b012a94f..bf38e98ebf 100644 --- a/apps/staking/src/api.ts +++ b/apps/staking/src/api.ts @@ -54,7 +54,8 @@ type Data = { poolUtilizationDelta: bigint; numFeeds: number; qualityRanking: number; - apyHistory: { date: Date; apy: number }[]; + delegationFee: bigint; + apyHistory: { date: Date; apy: number; selfApy: number }[]; positions?: | { warmup?: bigint | undefined; @@ -226,9 +227,10 @@ const loadPublisherData = async ( const publisherRanking = publisherRankings.find( (ranking) => ranking.publisher === publisherPubkeyString, ); - const apyHistory = publisher.apyHistory.map(({ epoch, apy }) => ({ + const apyHistory = publisher.apyHistory.map(({ epoch, apy, selfApy }) => ({ date: epochToDate(epoch + 1n), apy, + selfApy, })); return { @@ -243,6 +245,7 @@ const loadPublisherData = async ( selfStake: publisher.selfDelegation, selfStakeDelta: publisher.selfDelegationDelta, stakeAccount: publisher.stakeAccount ?? undefined, + delegationFee: publisher.delegationFee, }; }); }; diff --git a/apps/staking/src/components/OracleIntegrityStaking/index.tsx b/apps/staking/src/components/OracleIntegrityStaking/index.tsx index 615204d434..66d8e82ac7 100644 --- a/apps/staking/src/components/OracleIntegrityStaking/index.tsx +++ b/apps/staking/src/components/OracleIntegrityStaking/index.tsx @@ -1044,6 +1044,7 @@ const compareApy = ( poolCapacity: b.poolCapacity, poolUtilization: b.poolUtilization + b.poolUtilizationDelta, yieldRate, + delegationFee: b.delegationFee, }) - calculateApy({ isSelf: false, @@ -1051,6 +1052,7 @@ const compareApy = ( poolCapacity: a.poolCapacity, poolUtilization: a.poolUtilization + a.poolUtilizationDelta, yieldRate, + delegationFee: a.delegationFee, })); const comparePoolCapacity = ( @@ -1174,7 +1176,8 @@ type PublisherProps = { poolUtilizationDelta: bigint; numFeeds: number; qualityRanking: number; - apyHistory: { date: Date; apy: number }[]; + delegationFee: bigint; + apyHistory: { date: Date; apy: number; selfApy: number }[]; positions?: | { warmup?: bigint | undefined; @@ -1235,6 +1238,7 @@ const Publisher = ({ poolUtilization: publisher.poolUtilization + publisher.poolUtilizationDelta, yieldRate, + delegationFee: publisher.delegationFee, }).toFixed(2), [ isSelf, @@ -1373,9 +1377,9 @@ const Publisher = ({
({ + data={publisher.apyHistory.map(({ date, apy, selfApy }) => ({ date, - value: apy, + value: isSelf ? selfApy : apy, }))} />
@@ -1662,6 +1666,7 @@ const NewApy = ({ calculateApy({ poolCapacity: publisher.poolCapacity, yieldRate, + delegationFee: publisher.delegationFee, ...(isSelf ? { isSelf: true, diff --git a/governance/pyth_staking_sdk/src/types.ts b/governance/pyth_staking_sdk/src/types.ts index 92abacd464..c102415332 100644 --- a/governance/pyth_staking_sdk/src/types.ts +++ b/governance/pyth_staking_sdk/src/types.ts @@ -63,6 +63,7 @@ export type PublisherData = { totalDelegationDelta: bigint; selfDelegation: bigint; selfDelegationDelta: bigint; + delegationFee: bigint; apyHistory: { epoch: bigint; apy: number; selfApy: number }[]; }[]; diff --git a/governance/pyth_staking_sdk/src/utils/apy.ts b/governance/pyth_staking_sdk/src/utils/apy.ts index 5790c3e24d..f9e51b7ba8 100644 --- a/governance/pyth_staking_sdk/src/utils/apy.ts +++ b/governance/pyth_staking_sdk/src/utils/apy.ts @@ -4,16 +4,25 @@ export const convertEpochYieldToApy = (epochYield: bigint) => { return (Number(epochYield) * 52 * 100) / FRACTION_PRECISION; }; +export const computeDelegatorRewardPercentage = (delegationFee: bigint) => { + return 1 - Number(delegationFee) / FRACTION_PRECISION; +}; + export const calculateApy = ( options: { selfStake: bigint; poolCapacity: bigint; yieldRate: bigint; + delegationFee: bigint; } & ({ isSelf: true } | { isSelf: false; poolUtilization: bigint }), ) => { const { selfStake, poolCapacity, yieldRate, isSelf } = options; const eligibleSelfStake = selfStake > poolCapacity ? poolCapacity : selfStake; + if (poolCapacity === 0n) { + return 0; + } + const apyPercentage = convertEpochYieldToApy(yieldRate); if (isSelf) { @@ -24,6 +33,9 @@ export const calculateApy = ( } const { poolUtilization } = options; + const delegatorPercentage = computeDelegatorRewardPercentage( + options.delegationFee, + ); const delegatorPoolUtilization = poolUtilization - selfStake; const delegatorPoolCapacity = poolCapacity - eligibleSelfStake; @@ -33,10 +45,13 @@ export const calculateApy = ( : delegatorPoolUtilization; if (poolUtilization === selfStake) { - return apyPercentage; + return ( + (selfStake >= poolCapacity ? 0 : apyPercentage) * delegatorPercentage + ); } return ( - (apyPercentage * Number(eligibleStake)) / Number(delegatorPoolUtilization) + (apyPercentage * delegatorPercentage * Number(eligibleStake)) / + Number(delegatorPoolUtilization) ); }; diff --git a/governance/pyth_staking_sdk/src/utils/pool.ts b/governance/pyth_staking_sdk/src/utils/pool.ts index b5f71bb1d7..9bc91b181c 100644 --- a/governance/pyth_staking_sdk/src/utils/pool.ts +++ b/governance/pyth_staking_sdk/src/utils/pool.ts @@ -1,6 +1,9 @@ import { PublicKey } from "@solana/web3.js"; -import { convertEpochYieldToApy } from "./apy"; +import { + computeDelegatorRewardPercentage, + convertEpochYieldToApy, +} from "./apy"; import { FRACTION_PRECISION_N } from "../constants"; import type { PoolDataAccount, PublisherData } from "../types"; @@ -24,14 +27,19 @@ export const extractPublisherData = ( (poolData.selfDelState[index]?.deltaDelegation ?? 0n), selfDelegation: poolData.selfDelState[index]?.totalDelegation ?? 0n, selfDelegationDelta: poolData.selfDelState[index]?.deltaDelegation ?? 0n, + delegationFee: poolData.delegationFees[index] ?? 0n, apyHistory: poolData.events .filter((event) => event.epoch > 0n) .map((event) => ({ epoch: event.epoch, - apy: convertEpochYieldToApy( - (event.y * (event.eventData[index]?.otherRewardRatio ?? 0n)) / - FRACTION_PRECISION_N, - ), + apy: + convertEpochYieldToApy( + (event.y * (event.eventData[index]?.otherRewardRatio ?? 0n)) / + FRACTION_PRECISION_N, + ) * + computeDelegatorRewardPercentage( + poolData.delegationFees[index] ?? 0n, + ), selfApy: convertEpochYieldToApy( (event.y * (event.eventData[index]?.selfRewardRatio ?? 0n)) / FRACTION_PRECISION_N,