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

feat(earn): Add dailyYieldRatePercentage to beefy dataProps response #620

Merged
merged 7 commits into from
Oct 16, 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
7 changes: 4 additions & 3 deletions src/apps/beefy/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export type BeefyVault = {
id: string
name: string
type: string // cowcentrated, gov
subType?: string
token: string
tokenAddress: Address | undefined
tokenDecimals: number
Expand Down Expand Up @@ -137,10 +138,10 @@ export async function getBeefyPrices(
}
}

export async function getApys() {
export async function getApyBreakdown() {
return got
.get(`https://api.beefy.finance/apy/`)
.json<Record<string, number | undefined>>()
.get(`https://api.beefy.finance/apy/breakdown/`)
.json<Record<string, Record<string, number>>>()
}

export async function getTvls(
Expand Down
92 changes: 77 additions & 15 deletions src/apps/beefy/positions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { beefyV2AppMulticallAbi } from './abis/beefy-v2-app-multicall'
import {
BaseBeefyVault,
GovVault,
getApys,
getApyBreakdown,
getBeefyPrices,
getBeefyVaults,
getTvls,
Expand All @@ -30,7 +30,7 @@ import { TFunction } from 'i18next'
import { networkIdToNativeAssetAddress } from '../../runtime/isNative'

type BeefyPrices = Awaited<ReturnType<typeof getBeefyPrices>>
type BeefyApys = Awaited<ReturnType<typeof getApys>>
type BeefyApyBreakdown = Awaited<ReturnType<typeof getApyBreakdown>>
type BeefyTvls = Awaited<ReturnType<typeof getTvls>>

// Fetched addresses from https://github.com/beefyfinance/beefy-v2/blob/main/src/config/config.tsx
Expand Down Expand Up @@ -58,14 +58,14 @@ const beefyAppTokenDefinition = ({
networkId,
vault,
prices,
apys,
apyBreakdown,
tvls,
t,
}: {
networkId: NetworkId
vault: BaseBeefyVault
prices: BeefyPrices
apys: BeefyApys
apyBreakdown: BeefyApyBreakdown
tvls: BeefyTvls
t: TFunction
}): AppTokenPositionDefinition => {
Expand All @@ -74,7 +74,7 @@ const beefyAppTokenDefinition = ({
vault.tokenAddress?.toLowerCase() ??
networkIdToNativeAssetAddress[networkId]
const tvl = tvls[vault.id]
const apy = apys[vault.id]
const apy = apyBreakdown[vault.id].totalApy || 0
return {
type: 'app-token-definition',
networkId,
Expand Down Expand Up @@ -132,6 +132,10 @@ const beefyAppTokenDefinition = ({
manageUrl: `${BEEFY_VAULT_BASE_URL}${vault.id}`,
contractCreatedAt: new Date(vault.createdAt * 1000).toISOString(),
claimType: ClaimType.Rewards,
dailyYieldRatePercentage: getDailyYieldRatePercentage(
apyBreakdown[vault.id],
vault,
),
},
availableShortcutIds: ['deposit', 'withdraw', 'swap-deposit'],
shortcutTriggerArgs: ({ tokensByTokenId }) => {
Expand Down Expand Up @@ -160,6 +164,63 @@ const beefyAppTokenDefinition = ({
}
}

// Based on https://github.com/beefyfinance/beefy-v2/blob/4413697f3d3cb5e090d8bb6958b621a673f0d739/src/features/data/actions/apy.ts#L46
function getDailyYieldRatePercentage(
apyBreakdown: BeefyApyBreakdown[string],
vault: BaseBeefyVault,
) {
// https://github.com/beefyfinance/beefy-v2/blob/4413697f3d3cb5e090d8bb6958b621a673f0d739/src/helpers/apy.ts#L103
const components = [
'vaultApr',
'clmApr',
'tradingApr',
'merklApr',
'stellaSwapApr',
'liquidStakingApr',
'composablePoolApr',
'rewardPoolApr',
'rewardPoolTradingApr',
]

let totalDaily = 0
// Calculate the total daily apy from components
if (Object.keys(apyBreakdown).some((key) => components.includes(key))) {
for (const component of components) {
const apr = apyBreakdown[component]
if (apr && !isNaN(Number(apr))) {
totalDaily += apr / 365
}
}
} else {
// "uncompound" apy to get daily apr
totalDaily = _yearlyToDaily(apyBreakdown.totalApy)
}

// At some point the Beefy team decided to change this 'rewardPoolApr' on this specific vault type to be a soft-boost
// instead of being calculated as regular APR, so to be backwards compatible they subtract it from the total daily APR
if (vault.type === 'gov' && vault.subType === 'cowcentrated') {
Copy link
Contributor

Choose a reason for hiding this comment

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

vault.type itself can be cowcentrated right? could we document what a pool with type=='gov' and subType=='cowncentrated' is? Is that Old gov pools? if so can we make it explicit in the comment?

Copy link
Contributor

Choose a reason for hiding this comment

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

my comment (and the one about subtype values) was mainly because I was confused why something would have type=gov and subtype=cowcentrated when type can also be cowcentrated. And I also didn't find any vault like that in the vaults response for arbitrum. If this was just copied from beefy as is, probably fine to leave as is.

// anything in 'rewardPoolApr' (i.e. not in 'rewardPoolTradingApr') is considered a soft-boost on the pool
// and is should not be counted towards the daily yield rate
const additionalApr = apyBreakdown.rewardPoolApr || 0
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const additionalApr = apyBreakdown.rewardPoolApr || 0
const additionalApr = apyBreakdown.rewardPoolApr ?? 0

Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like you did the opposite change 😄

totalDaily -= additionalApr / 365
}

// TODO: handle merkl campaigns which are off-chain rewards
// https://github.com/beefyfinance/beefy-v2/blob/4413697f3d3cb5e090d8bb6958b621a673f0d739/src/features/data/actions/apy.ts#L132

return totalDaily * 100
}

const _yearlyToDaily = (apy: number) => {
const dailyApy = Math.pow(10, Math.log10(apy + 1) / 365) - 1

if (isNaN(dailyApy)) {
return 0
}

return dailyApy
}

// CLM = Cowcentrated Liquidity Manager: https://docs.beefy.finance/beefy-products/clm
interface ClmVaultBalanceInfo {
token0: Address
Expand Down Expand Up @@ -232,7 +293,7 @@ const beefyBaseVaultsPositions = async ({
vaults,
multicallAddress,
prices,
apys,
apyBreakdown,
tvls,
t,
}: {
Expand All @@ -241,7 +302,7 @@ const beefyBaseVaultsPositions = async ({
vaults: BaseBeefyVault[]
multicallAddress: Address
prices: BeefyPrices
apys: BeefyApys
apyBreakdown: BeefyApyBreakdown
tvls: BeefyPrices
t: TFunction
}) => {
Expand Down Expand Up @@ -310,7 +371,7 @@ const beefyBaseVaultsPositions = async ({
networkId,
vault,
prices,
apys,
apyBreakdown,
tvls,
t,
})
Expand Down Expand Up @@ -427,12 +488,13 @@ const hook: PositionsHook = {
return []
}

const [{ vaults, govVaults }, prices, apys, tvls] = await Promise.all([
getBeefyVaults(networkId),
getBeefyPrices(networkId),
getApys(),
getTvls(networkId),
])
const [{ vaults, govVaults }, prices, apyBreakdown, tvls] =
await Promise.all([
getBeefyVaults(networkId),
getBeefyPrices(networkId),
getApyBreakdown(),
getTvls(networkId),
])

return [
...(await beefyBaseVaultsPositions({
Expand All @@ -441,7 +503,7 @@ const hook: PositionsHook = {
vaults,
multicallAddress,
prices,
apys,
apyBreakdown,
tvls,
t,
})),
Expand Down
3 changes: 2 additions & 1 deletion src/types/positions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,14 @@ export interface EarnDataProps {
termsUrl?: string
cantSeparateCompoundedInterest?: boolean
tvl?: SerializedDecimalNumber // In USD
yieldRates: YieldRate[]
yieldRates: YieldRate[] // List of components of yield for a pool that, summed up, give the total yield rate
earningItems: EarningItem[]
depositTokenId: string
withdrawTokenId: string
rewardsPositionIds?: string[]
claimType?: ClaimType
withdrawalIncludesClaim?: boolean
dailyYieldRatePercentage?: number // The daily yield rate percentage
// We'll add more fields here as needed
}

Expand Down
Loading