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

unify get pool apy #342

Merged
merged 3 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
87 changes: 15 additions & 72 deletions packages/huma-shared/src/solana/utils/poolUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BN } from '@coral-xyz/anchor'

import { getPoolApyV2 } from '../../utils/apy'
import { SOLANA_BP_FACTOR } from '../const'

export const getSolanaPoolApy = (
Expand All @@ -13,78 +14,20 @@ export const getSolanaPoolApy = (
defaultMaxSeniorJuniorRatio: number,
fixedSeniorYieldInBps: number,
tranchesRiskAdjustmentInBps: number,
): {
blendedApy: number
seniorTrancheApy: number
juniorTrancheApy: number
} => {
const BP_FACTOR_NUMBER = SOLANA_BP_FACTOR.toNumber()
const APY = yieldInBps / BP_FACTOR_NUMBER

const totalDeployedAssets = seniorDeployedAssets.add(juniorDeployedAssets)
const seniorMaxAssets = liquidityCap
.sub(totalDeployedAssets)
.add(seniorDeployedAssets)
const currentMaxSeniorJuniorRatio = seniorMaxAssets
.div(juniorDeployedAssets)
.toNumber()

let juniorAssets = liquidityCap.div(new BN(defaultMaxSeniorJuniorRatio + 1))
let seniorAssets = liquidityCap.sub(juniorAssets)
if (currentMaxSeniorJuniorRatio < defaultMaxSeniorJuniorRatio) {
juniorAssets = juniorDeployedAssets
seniorAssets = seniorMaxAssets
}

const totalProfit = liquidityCap
.mul(new BN(Math.round(APY * BP_FACTOR_NUMBER)))
.div(SOLANA_BP_FACTOR)
const postPoolProfitRatio =
(1 - protocolFeeInBps / BP_FACTOR_NUMBER) *
(1 -
rewardRateInBpsForPoolOwner / BP_FACTOR_NUMBER -
rewardRateInBpsForEA / BP_FACTOR_NUMBER)
const poolPostProfit = totalProfit
.mul(new BN(Math.round(postPoolProfitRatio * BP_FACTOR_NUMBER)))
.div(SOLANA_BP_FACTOR)
const blendedApy =
poolPostProfit.mul(SOLANA_BP_FACTOR).div(liquidityCap).toNumber() /
BP_FACTOR_NUMBER

let seniorTrancheApy = 0
let juniorProfit = new BN(0)
if (fixedSeniorYieldInBps > 0) {
seniorTrancheApy = fixedSeniorYieldInBps / BP_FACTOR_NUMBER
juniorProfit = poolPostProfit.sub(
seniorAssets
.mul(new BN(Math.round(seniorTrancheApy * BP_FACTOR_NUMBER)))
.div(SOLANA_BP_FACTOR),
)
} else {
const riskAdjustment = tranchesRiskAdjustmentInBps / BP_FACTOR_NUMBER
const seniorProfit = seniorAssets
.mul(
new BN(
Math.round(
postPoolProfitRatio * (1 - riskAdjustment) * APY * BP_FACTOR_NUMBER,
),
),
)
.div(SOLANA_BP_FACTOR)
seniorTrancheApy = postPoolProfitRatio * (1 - riskAdjustment) * APY
juniorProfit = poolPostProfit.sub(seniorProfit)
}

const juniorTrancheApy =
juniorProfit.mul(SOLANA_BP_FACTOR).div(juniorAssets).toNumber() /
BP_FACTOR_NUMBER

return {
blendedApy,
seniorTrancheApy,
juniorTrancheApy,
}
}
) =>
getPoolApyV2(
protocolFeeInBps,
yieldInBps,
rewardRateInBpsForPoolOwner,
rewardRateInBpsForEA,
liquidityCap.toString(),
seniorDeployedAssets.toString(),
juniorDeployedAssets.toString(),
defaultMaxSeniorJuniorRatio,
fixedSeniorYieldInBps,
tranchesRiskAdjustmentInBps,
SOLANA_BP_FACTOR.toNumber(),
)

export const getSolanaUtilizationRate = (
seniorTrancheAssets: BN | undefined,
Expand Down
209 changes: 209 additions & 0 deletions packages/huma-shared/src/utils/apy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
import { BigNumber } from 'ethers'

export interface FclConfig {
Copy link
Collaborator

Choose a reason for hiding this comment

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

FlcConfig

minLiquidity: string
riskYieldMultiplierInBps: number
flcIndex: number
maxLiquidity: string
}

export interface FclConfigWithApy extends FclConfig {
Copy link
Collaborator

Choose a reason for hiding this comment

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

FlcConfigWithApy

apy: number
firstLossCoverIndex: number
}

export interface PoolApy {
flcConfigsWithApy: FclConfigWithApy[]
Copy link
Collaborator

Choose a reason for hiding this comment

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

FlcConfigWithApy

blendedApy: number
seniorTrancheApy: number
juniorTrancheApy: number
}

const getFlcTotalAssetsAfterRisk = (
flcConfigs: {
minLiquidity: string
riskYieldMultiplierInBps: number
}[],
BP_FACTOR: number,
): BigNumber => {
const flcTotalAssetsAfterRisk = flcConfigs.reduce(
(acc, flcConfig) =>
acc.add(
BigNumber.from(flcConfig.minLiquidity)
.mul(flcConfig.riskYieldMultiplierInBps)
.div(BP_FACTOR),
),
BigNumber.from(0),
)
return flcTotalAssetsAfterRisk
}

const getPoolApyV2Base = (
protocolFeeInBps: number,
yieldInBps: number,
rewardRateInBpsForPoolOwner: number,
rewardRateInBpsForEA: number,
liquidityCap: string,
seniorDeployedAssets: string,
juniorDeployedAssets: string,
maxSeniorJuniorRatio: number,
fixedSeniorYieldInBps: number,
tranchesRiskAdjustmentInBps: number,
BP_FACTOR: number,
FirstLossCoverIndexes: string[] = [],
flcConfigs: FclConfig[] = [],
): PoolApy => {
const APY = yieldInBps / BP_FACTOR

const liquidityCapBN = BigNumber.from(liquidityCap)
const seniorDeployedAssetsBN = BigNumber.from(seniorDeployedAssets)
const juniorDeployedAssetsBN = BigNumber.from(juniorDeployedAssets)
const totalDeployedAssetsBN = seniorDeployedAssetsBN.add(
juniorDeployedAssetsBN,
)
const seniorMaxAssetsBN = liquidityCapBN
.sub(totalDeployedAssetsBN)
.add(seniorDeployedAssetsBN)
const currentMaxSeniorJuniorRatio = seniorMaxAssetsBN
.div(juniorDeployedAssetsBN)
.toNumber()

let juniorAssetsBN = liquidityCapBN.div(maxSeniorJuniorRatio + 1)
let seniorAssetsBN = liquidityCapBN.sub(juniorAssetsBN)
if (currentMaxSeniorJuniorRatio < maxSeniorJuniorRatio) {
juniorAssetsBN = juniorDeployedAssetsBN
seniorAssetsBN = seniorMaxAssetsBN
}

const totalProfitBN = liquidityCapBN
.mul(Math.round(APY * BP_FACTOR))
.div(BP_FACTOR)
const postPoolProfitRatio =
(1 - protocolFeeInBps / BP_FACTOR) *
(1 -
rewardRateInBpsForPoolOwner / BP_FACTOR -
rewardRateInBpsForEA / BP_FACTOR)
const poolPostProfitBN = totalProfitBN
.mul(Math.round(postPoolProfitRatio * BP_FACTOR))
.div(BP_FACTOR)
const blendedApy =
poolPostProfitBN.mul(BP_FACTOR).div(liquidityCap).toNumber() / BP_FACTOR

let seniorTrancheApy = 0
let seniorPostProfitBN = BigNumber.from(0)
if (fixedSeniorYieldInBps > 0) {
seniorTrancheApy = fixedSeniorYieldInBps / BP_FACTOR
seniorPostProfitBN = poolPostProfitBN.sub(
seniorAssetsBN
.mul(Math.round(seniorTrancheApy * BP_FACTOR))
.div(BP_FACTOR),
)
} else {
const riskAdjustment = tranchesRiskAdjustmentInBps / BP_FACTOR
const seniorProfitBN = seniorAssetsBN
.mul(
Math.round(
postPoolProfitRatio * (1 - riskAdjustment) * APY * BP_FACTOR,
),
)
.div(BP_FACTOR)
seniorTrancheApy = postPoolProfitRatio * (1 - riskAdjustment) * APY
seniorPostProfitBN = poolPostProfitBN.sub(seniorProfitBN)
}

const flcTotalAssetsAfterRiskBN = getFlcTotalAssetsAfterRisk(
flcConfigs,
BP_FACTOR,
)
const weightBN = juniorAssetsBN.add(flcTotalAssetsAfterRiskBN)

const juniorProfitBN = juniorAssetsBN.mul(seniorPostProfitBN).div(weightBN)
const juniorTrancheApy =
juniorProfitBN.mul(BP_FACTOR).div(juniorAssetsBN).toNumber() / BP_FACTOR
const flcTotalProfitBN = flcTotalAssetsAfterRiskBN
.mul(seniorPostProfitBN)
.div(weightBN)
const flcConfigsWithApy = flcConfigs.map((flcConfig) => {
const minLiquidityBN = BigNumber.from(flcConfig.minLiquidity)
const flcAssetsAfterRiskBN = minLiquidityBN
.mul(flcConfig.riskYieldMultiplierInBps)
.div(BP_FACTOR)

let apy = 0
if (minLiquidityBN.gt(0) && flcAssetsAfterRiskBN.gt(0)) {
const flcProfitBN = flcAssetsAfterRiskBN
.mul(flcTotalProfitBN)
.div(flcTotalAssetsAfterRiskBN)
apy =
flcProfitBN.mul(BP_FACTOR).div(flcConfig.minLiquidity).toNumber() /
BP_FACTOR
}

return {
...flcConfig,
firstLossCoverIndex: Number(FirstLossCoverIndexes[flcConfig.flcIndex]),
apy,
}
})

return {
flcConfigsWithApy,
blendedApy,
seniorTrancheApy,
juniorTrancheApy,
}
}

export const getPoolApyV2 = (
protocolFeeInBps: number,
yieldInBps: number,
rewardRateInBpsForPoolOwner: number,
rewardRateInBpsForEA: number,
liquidityCap: string,
seniorDeployedAssets: string,
juniorDeployedAssets: string,
maxSeniorJuniorRatio: number,
fixedSeniorYieldInBps: number,
tranchesRiskAdjustmentInBps: number,
BP_FACTOR: number,
FirstLossCoverIndexes: string[] = [],
flcConfigs: FclConfig[] = [],
): PoolApy & { maxJuniorTrancheApy: number } => {
const realApyResult = getPoolApyV2Base(
protocolFeeInBps,
yieldInBps,
rewardRateInBpsForPoolOwner,
rewardRateInBpsForEA,
liquidityCap,
seniorDeployedAssets,
juniorDeployedAssets,
maxSeniorJuniorRatio,
fixedSeniorYieldInBps,
tranchesRiskAdjustmentInBps,
BP_FACTOR,
FirstLossCoverIndexes,
flcConfigs,
)

const liquidityCapBN = BigNumber.from(liquidityCap)
const juniorAssetsBN = liquidityCapBN.div(maxSeniorJuniorRatio + 1)
const seniorAssetsBN = liquidityCapBN.sub(juniorAssetsBN)
const maxJuniorApyResult = getPoolApyV2Base(
protocolFeeInBps,
yieldInBps,
rewardRateInBpsForPoolOwner,
rewardRateInBpsForEA,
liquidityCap,
seniorAssetsBN.toString(),
juniorAssetsBN.toString(),
maxSeniorJuniorRatio,
fixedSeniorYieldInBps,
tranchesRiskAdjustmentInBps,
BP_FACTOR,
)

return {
...realApyResult,
maxJuniorTrancheApy: maxJuniorApyResult.juniorTrancheApy,
}
}
9 changes: 5 additions & 4 deletions packages/huma-shared/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
export * from './JsonRpcConnector'
export * from './apy'
export * from './chain'
export * from './common'
export * from './config'
export * from './const'
export * from './credit'
export * from './env'
export * from './errors'
export * from './JsonRpcConnector'
export * from './jsonRpcEndpoints'
export * from './notifi'
export * from './number'
export * from './pool'
export * from './poolContract'
export * from './realWorldReceivable'
export * from './request'
export * from './scientificToDecimal'
export * from './string'
export * from './style'
export * from './time'
export * from './transaction'
export * from './web3'
export * from './notifi'
export * from './realWorldReceivable'
export * from './env'
Loading
Loading