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 3 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
Copy link
Contributor

Choose a reason for hiding this comment

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

is there a set of values that this can contain? Maybe we can use specific types for type and subType.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It appears there are, they are not all located in one place throughout the code in beefy. What would be the value in adding them in right now though?

Copy link
Contributor

Choose a reason for hiding this comment

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

I was hoping it would be something like type above. Definitely not worth it reading through the code. Also is it optional? I don't see it here for example https://api.beefy.finance/gov-vaults/arbitrum

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
91 changes: 76 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
Copy link
Contributor

Choose a reason for hiding this comment

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

I see five or so *-earnings vaults that do not have earnings defined do these need a fallback?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good idea! I can be a bit more defensive on a few things here

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,62 @@ 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) {
totalDaily += apr / 365
}
}
} else {
// "uncompound" apy to get daily apr
totalDaily = _yearlyToDaily(apyBreakdown.totalApy)
}

// Presence of rewardPoolApr indicates new api calc that has correct totals
// [Old gov pools had their apr in the vaultApr field]
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
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 g = Math.pow(10, Math.log10(apy + 1) / 365) - 1
satish-ravi marked this conversation as resolved.
Show resolved Hide resolved

if (isNaN(g)) {
return 0
}

return g
}

// CLM = Cowcentrated Liquidity Manager: https://docs.beefy.finance/beefy-products/clm
interface ClmVaultBalanceInfo {
token0: Address
Expand Down Expand Up @@ -232,7 +292,7 @@ const beefyBaseVaultsPositions = async ({
vaults,
multicallAddress,
prices,
apys,
apyBreakdown,
tvls,
t,
}: {
Expand All @@ -241,7 +301,7 @@ const beefyBaseVaultsPositions = async ({
vaults: BaseBeefyVault[]
multicallAddress: Address
prices: BeefyPrices
apys: BeefyApys
apyBreakdown: BeefyApyBreakdown
tvls: BeefyPrices
t: TFunction
}) => {
Expand Down Expand Up @@ -310,7 +370,7 @@ const beefyBaseVaultsPositions = async ({
networkId,
vault,
prices,
apys,
apyBreakdown,
tvls,
t,
})
Expand Down Expand Up @@ -427,12 +487,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 +502,7 @@ const hook: PositionsHook = {
vaults,
multicallAddress,
prices,
apys,
apyBreakdown,
tvls,
t,
})),
Expand Down
1 change: 1 addition & 0 deletions src/types/positions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export interface EarnDataProps {
rewardsPositionIds?: string[]
claimType?: ClaimType
withdrawalIncludesClaim?: boolean
dailyYieldRatePercentage?: number
satish-ravi marked this conversation as resolved.
Show resolved Hide resolved
// We'll add more fields here as needed
}

Expand Down
Loading