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

Fix users apys #104

Merged
merged 3 commits into from
Jan 8, 2025
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
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "v3-subgraph",
"version": "3.0.0",
"version": "3.0.1",
"description": "Subgraph for the StakeWise Protocol",
"repository": "https://github.com/stakewise/v3-subgraph",
"license": "AGPL-3.0-only",
Expand Down
30 changes: 2 additions & 28 deletions src/entities/aave.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Address, BigDecimal, BigInt, Bytes, ethereum, log } from '@graphprotocol/graph-ts'
import { Aave, AavePosition, OsToken } from '../../generated/schema'
import { Aave, AavePosition } from '../../generated/schema'
import { AaveProtocolDataProvider as AaveProtocolDataProviderContract } from '../../generated/PeriodicTasks/AaveProtocolDataProvider'
import { AaveLeverageStrategy } from '../../generated/PeriodicTasks/AaveLeverageStrategy'
import {
Expand All @@ -10,12 +10,10 @@ import {
OS_TOKEN,
WAD,
} from '../helpers/constants'
import { calculateMedian, chunkedMulticall, getCompoundedApy } from '../helpers/utils'
import { getOsTokenApy } from './osToken'
import { calculateMedian, chunkedMulticall } from '../helpers/utils'

const aaveId = '1'
const snapshotsPerWeek = 168
const snapshotsPerDay = 24
const getBorrowStateSelector = '0xe70631bc'

export function loadAave(): Aave | null {
Expand Down Expand Up @@ -134,30 +132,6 @@ export function updateAavePosition(position: AavePosition): void {
position.save()
}

export function getAaveSupplyApy(aave: Aave, osToken: OsToken, useDayApy: boolean): BigDecimal {
// assumes that updates happen every hour
const apysCount = aave.supplyApys.length
let apy: BigDecimal
if (!useDayApy || apysCount < snapshotsPerDay) {
apy = aave.supplyApy
} else {
const apys: Array<BigDecimal> = aave.supplyApys
apy = calculateMedian(apys.slice(apysCount - snapshotsPerDay))
}
// earned osToken shares earn extra staking rewards, apply compounding
return getCompoundedApy(apy, getOsTokenApy(osToken, useDayApy))
}

export function getAaveBorrowApy(aave: Aave, useDayApy: boolean): BigDecimal {
// assumes that updates happen every hour
const apysCount = aave.borrowApys.length
if (!useDayApy || apysCount < snapshotsPerDay) {
return aave.borrowApy
}
const apys: Array<BigDecimal> = aave.borrowApys
return calculateMedian(apys.slice(apysCount - snapshotsPerDay))
}

function _getBorrowStateCall(user: Address): Bytes {
const encodedGetBorrowStateArgs = ethereum.encode(ethereum.Value.fromAddress(user))
return Bytes.fromHexString(getBorrowStateSelector).concat(encodedGetBorrowStateArgs!)
Expand Down
22 changes: 10 additions & 12 deletions src/entities/allocator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Vault,
} from '../../generated/schema'
import { WAD } from '../helpers/constants'
import { chunkedVaultMulticall, getAnnualReward } from '../helpers/utils'
import { calculateApy, chunkedVaultMulticall, getAnnualReward } from '../helpers/utils'
import { convertOsTokenSharesToAssets, getOsTokenApy } from './osToken'
import { convertSharesToAssets, getVaultApy, getVaultOsTokenMintApy, loadVault } from './vault'
import { loadOsTokenConfig } from './osTokenConfig'
Expand Down Expand Up @@ -182,13 +182,12 @@ export function getAllocatorApy(
vault: Vault,
distributor: Distributor,
allocator: Allocator,
useDayApy: boolean,
): BigDecimal {
const vaultAddress = Address.fromString(allocator.vault)
const allocatorAddress = Address.fromBytes(allocator.address)

const vaultApy = getVaultApy(vault, useDayApy)
const osTokenApy = getOsTokenApy(osToken, useDayApy)
const vaultApy = getVaultApy(vault, false)
const osTokenApy = getOsTokenApy(osToken, false)

let totalAssets = allocator.assets
let stakingAssets = allocator.assets
Expand All @@ -212,14 +211,14 @@ export function getAllocatorApy(

const mintedOsTokenAssets = convertOsTokenSharesToAssets(osToken, allocator.mintedOsTokenShares)
totalEarnedAssets = totalEarnedAssets.minus(
getAnnualReward(mintedOsTokenAssets, getVaultOsTokenMintApy(osToken, osTokenConfig, useDayApy)),
getAnnualReward(mintedOsTokenAssets, getVaultOsTokenMintApy(osToken, osTokenConfig)),
)

const boostPosition = loadLeverageStrategyPosition(vaultAddress, allocatorAddress)
if (boostPosition !== null) {
const aave = loadAave()!
totalEarnedAssets = totalEarnedAssets.plus(
getBoostPositionAnnualReward(osToken, aave, vault, osTokenConfig, boostPosition, distributor, useDayApy),
getBoostPositionAnnualReward(osToken, aave, vault, osTokenConfig, boostPosition, distributor),
)
const boostedOsTokenShares = boostPosition.osTokenShares.plus(boostPosition.exitingOsTokenShares)
let extraOsTokenShares: BigInt
Expand All @@ -243,7 +242,7 @@ export function getAllocatorApy(
}

const allocatorApy = totalEarnedAssets.divDecimal(totalAssets.toBigDecimal()).times(BigDecimal.fromString('100'))
if (!useDayApy && vault.apy.lt(vault.allocatorMaxBoostApy) && allocatorApy.gt(vault.allocatorMaxBoostApy)) {
if (vault.apy.lt(vault.allocatorMaxBoostApy) && allocatorApy.gt(vault.allocatorMaxBoostApy)) {
log.warning(
'[getAllocatorApy] Calculated APY is higher than max boost APY: maxBoostApy={} allocatorApy={} vault={} allocator={}',
[vault.allocatorMaxBoostApy.toString(), allocatorApy.toString(), vault.id, allocator.address.toHex()],
Expand Down Expand Up @@ -306,7 +305,6 @@ export function updateAllocatorAssets(
allocator.assets = convertSharesToAssets(vault, allocator.shares)
allocator.ltv = getAllocatorLtv(allocator, osToken)
allocator.ltvStatus = getAllocatorLtvStatus(allocator, osTokenConfig)
allocator.save()
return allocator.assets.minus(assetsBefore)
}

Expand Down Expand Up @@ -362,19 +360,19 @@ export function updateAllocatorMintedOsTokenShares(

export function snapshotAllocator(
osToken: OsToken,
osTokenConfig: OsTokenConfig,
vault: Vault,
distributor: Distributor,
allocator: Allocator,
earnedAssets: BigInt,
duration: BigInt,
timestamp: BigInt,
): void {
const totalAssets = getAllocatorTotalAssets(osToken, vault, allocator)
const allocatorSnapshot = new AllocatorSnapshot(timestamp.toString())
allocatorSnapshot.timestamp = timestamp.toI64()
allocatorSnapshot.allocator = allocator.id
allocatorSnapshot.earnedAssets = earnedAssets
allocatorSnapshot.totalAssets = getAllocatorTotalAssets(osToken, vault, allocator)
allocatorSnapshot.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, true)
allocatorSnapshot.totalAssets = totalAssets
allocatorSnapshot.apy = calculateApy(earnedAssets, totalAssets, duration)
allocatorSnapshot.ltv = allocator.ltv
allocatorSnapshot.save()
}
Expand Down
18 changes: 9 additions & 9 deletions src/entities/leverageStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import { AaveLeverageStrategy } from '../../generated/PeriodicTasks/AaveLeverage
import { AAVE_LEVERAGE_STRATEGY, WAD } from '../helpers/constants'
import { loadAllocator } from './allocator'
import { convertAssetsToOsTokenShares, convertOsTokenSharesToAssets, getOsTokenApy } from './osToken'
import { getAnnualReward } from '../helpers/utils'
import { getAnnualReward, getCompoundedApy } from '../helpers/utils'
import { getVaultApy, getVaultOsTokenMintApy } from './vault'
import { getAaveBorrowApy, getAaveSupplyApy, loadAavePosition } from './aave'
import { loadAavePosition } from './aave'
import { convertStringToDistributionType, DistributionType, getPeriodicDistributionApy } from './merkleDistributor'
import { loadOsTokenHolder } from './osTokenHolder'
import { getIsOsTokenVault } from './network'
Expand Down Expand Up @@ -210,18 +210,18 @@ export function getBoostPositionAnnualReward(
osTokenConfig: OsTokenConfig,
strategyPosition: LeverageStrategyPosition,
distributor: Distributor,
useDayApy: boolean,
): BigInt {
const vaultAddress = Address.fromString(strategyPosition.vault)
const proxyAddress = Address.fromBytes(strategyPosition.proxy)

const vaultPosition = loadAllocator(proxyAddress, vaultAddress)!
const aavePosition = loadAavePosition(proxyAddress)!

const vaultApy = getVaultApy(vault, useDayApy)
const osTokenApy = getOsTokenApy(osToken, useDayApy)
const borrowApy = getAaveBorrowApy(aave, useDayApy)
const supplyApy = getAaveSupplyApy(aave, osToken, useDayApy)
const vaultApy = getVaultApy(vault, false)
const osTokenApy = getOsTokenApy(osToken, false)
const borrowApy = aave.borrowApy
// earned osToken shares earn extra staking rewards, apply compounding
const supplyApy = getCompoundedApy(aave.supplyApy, osTokenApy)

let totalDepositedAssets = vaultPosition.assets
let totalMintedOsTokenShares = vaultPosition.mintedOsTokenShares
Expand Down Expand Up @@ -255,7 +255,7 @@ export function getBoostPositionAnnualReward(
totalEarnedAssets = totalEarnedAssets.plus(convertOsTokenSharesToAssets(osToken, totalEarnedOsTokenShares))

// minted osToken shares lose mint APY
const osTokenMintApy = getVaultOsTokenMintApy(osToken, osTokenConfig, useDayApy)
const osTokenMintApy = getVaultOsTokenMintApy(osToken, osTokenConfig)
totalEarnedAssets = totalEarnedAssets.minus(getAnnualReward(totalMintedOsTokenAssets, osTokenMintApy))

// borrowed assets lose borrow APY
Expand All @@ -276,7 +276,7 @@ export function getBoostPositionAnnualReward(
}

// get the distribution APY
distributionApy = getPeriodicDistributionApy(distribution, osToken, useDayApy)
distributionApy = getPeriodicDistributionApy(distribution, osToken)
if (distributionApy.equals(BigDecimal.zero())) {
continue
}
Expand Down
21 changes: 3 additions & 18 deletions src/entities/merkleDistributor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const distributorId = '1'
const secondsInYear = '31536000'
const maxPercent = '100'
const snapshotsPerWeek = 168
const snapshotsPerDay = 24
const leverageStrategyDistAddress = Address.zero()

export enum DistributionType {
Expand Down Expand Up @@ -122,26 +121,12 @@ export function getLeverageStrategyTargetApy(distData: Bytes): BigDecimal {
return tuple[1].toBigInt().divDecimal(BigDecimal.fromString('100'))
}

export function getPeriodicDistributionApy(
distribution: PeriodicDistribution,
osToken: OsToken,
useDayApy: boolean,
): BigDecimal {
// assumes that updates happen every hour
const apysCount = distribution.apys.length
let apy: BigDecimal
if (!useDayApy || apysCount < snapshotsPerDay) {
apy = distribution.apy
} else {
const apys: Array<BigDecimal> = distribution.apys
apy = calculateAverage(apys.slice(apysCount - snapshotsPerDay))
}

export function getPeriodicDistributionApy(distribution: PeriodicDistribution, osToken: OsToken): BigDecimal {
if (Address.fromBytes(distribution.token).equals(OS_TOKEN)) {
// earned osToken shares earn extra staking rewards, apply compounding
return getCompoundedApy(apy, getOsTokenApy(osToken, useDayApy))
return getCompoundedApy(distribution.apy, getOsTokenApy(osToken, false))
}
return apy
return distribution.apy
}

export function convertDistributionTypeToString(distType: DistributionType): string {
Expand Down
2 changes: 1 addition & 1 deletion src/entities/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function createOrLoadNetwork(): Network {
network.vaultIds = []
network.osTokenVaultIds = []
network.oraclesConfigIpfsHash = ''
network.snapshotsCount = BigInt.zero()
network.lastSnapshotTimestamp = BigInt.zero()
network.assetsUsdRate = BigDecimal.zero()
network.swiseUsdRate = BigDecimal.zero()
network.daiUsdRate = BigDecimal.zero()
Expand Down
21 changes: 8 additions & 13 deletions src/entities/osTokenHolder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
OsTokenHolderSnapshot,
Vault,
} from '../../generated/schema'
import { getAnnualReward } from '../helpers/utils'
import { calculateApy, getAnnualReward } from '../helpers/utils'
import { convertOsTokenSharesToAssets, getOsTokenApy, osTokenId } from './osToken'
import { getBoostPositionAnnualReward, loadLeverageStrategyPosition } from './leverageStrategy'
import { loadVault } from './vault'
Expand Down Expand Up @@ -45,9 +45,8 @@ export function getOsTokenHolderApy(
osToken: OsToken,
distributor: Distributor,
osTokenHolder: OsTokenHolder,
useDayApy: boolean,
): BigDecimal {
const osTokenApy = getOsTokenApy(osToken, useDayApy)
const osTokenApy = getOsTokenApy(osToken, false)

let principalAssets = osTokenHolder.assets
let totalEarnedAssets = getAnnualReward(principalAssets, osTokenApy)
Expand All @@ -69,7 +68,7 @@ export function getOsTokenHolderApy(
.plus(position.exitingAssets)
.plus(convertOsTokenSharesToAssets(osToken, position.osTokenShares.plus(position.exitingOsTokenShares)))
totalEarnedAssets = totalEarnedAssets.plus(
getBoostPositionAnnualReward(osToken, aave, vault, osTokenConfig, position, distributor, useDayApy),
getBoostPositionAnnualReward(osToken, aave, vault, osTokenConfig, position, distributor),
)
// we only take the first boosted position
break
Expand All @@ -82,12 +81,7 @@ export function getOsTokenHolderApy(
const osTokenHolderApy = totalEarnedAssets
.divDecimal(principalAssets.toBigDecimal())
.times(BigDecimal.fromString('100'))
if (
!useDayApy &&
vault &&
osTokenApy.lt(vault.osTokenHolderMaxBoostApy) &&
osTokenHolderApy.gt(vault.osTokenHolderMaxBoostApy)
) {
if (vault && osTokenApy.lt(vault.osTokenHolderMaxBoostApy) && osTokenHolderApy.gt(vault.osTokenHolderMaxBoostApy)) {
log.warning(
'[getOsTokenHolderApy] Calculated APY is higher than max boost APY: maxBoostApy={} osTokenHolderApy={} vault={} holder={}',
[vault.osTokenHolderMaxBoostApy.toString(), osTokenHolderApy.toString(), vault.id, osTokenHolder.id],
Expand Down Expand Up @@ -152,16 +146,17 @@ export function updateOsTokenHolderAssets(osToken: OsToken, osTokenHolder: OsTok
export function snapshotOsTokenHolder(
network: Network,
osToken: OsToken,
distributor: Distributor,
osTokenHolder: OsTokenHolder,
earnedAssets: BigInt,
duration: BigInt,
timestamp: BigInt,
): void {
const totalAssets = getOsTokenHolderTotalAssets(network, osToken, osTokenHolder)
const snapshot = new OsTokenHolderSnapshot(timestamp.toString())
snapshot.timestamp = timestamp.toI64()
snapshot.osTokenHolder = osTokenHolder.id
snapshot.earnedAssets = earnedAssets
snapshot.totalAssets = getOsTokenHolderTotalAssets(network, osToken, osTokenHolder)
snapshot.apy = getOsTokenHolderApy(network, osToken, distributor, osTokenHolder, true)
snapshot.totalAssets = totalAssets
snapshot.apy = calculateApy(earnedAssets, totalAssets, duration)
snapshot.save()
}
16 changes: 8 additions & 8 deletions src/entities/vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import { convertAssetsToOsTokenShares, convertOsTokenSharesToAssets, getOsTokenApy } from './osToken'
import { isGnosisNetwork, loadNetwork } from './network'
import { getV2PoolState, loadV2Pool, updatePoolApy } from './v2pool'
import { calculateAverage, chunkedMulticall, getAnnualReward } from '../helpers/utils'
import { calculateAverage, chunkedMulticall, getAnnualReward, getCompoundedApy } from '../helpers/utils'
import { createOrLoadOwnMevEscrow } from './mevEscrow'
import { VaultCreated } from '../../generated/templates/VaultFactory/VaultFactory'
import {
Expand All @@ -30,7 +30,6 @@ import {
Vault as VaultTemplate,
} from '../../generated/templates'
import { createTransaction } from './transaction'
import { getAaveBorrowApy, getAaveSupplyApy } from './aave'
import { getAllocatorId } from './allocator'
import { convertStringToDistributionType, DistributionType, getPeriodicDistributionApy } from './merkleDistributor'

Expand Down Expand Up @@ -178,8 +177,8 @@ export function getVaultApy(vault: Vault, useDayApy: boolean): BigDecimal {
return calculateAverage(apys.slice(apys.length - snapshotsPerDay))
}

export function getVaultOsTokenMintApy(osToken: OsToken, osTokenConfig: OsTokenConfig, useDayApy: boolean): BigDecimal {
const osTokenApy = getOsTokenApy(osToken, useDayApy)
export function getVaultOsTokenMintApy(osToken: OsToken, osTokenConfig: OsTokenConfig): BigDecimal {
const osTokenApy = getOsTokenApy(osToken, false)
const feePercentBigDecimal = BigDecimal.fromString(osToken.feePercent.toString())
if (osTokenConfig.ltvPercent.isZero()) {
log.error('getVaultOsTokenMintApy osTokenConfig.ltvPercent is zero osTokenConfig={}', [osTokenConfig.id])
Expand Down Expand Up @@ -378,8 +377,9 @@ export function updateVaultMaxBoostApy(
const wad = BigInt.fromString(WAD)

const osTokenApy = getOsTokenApy(osToken, false)
const borrowApy = getAaveBorrowApy(aave, false)
const supplyApy = getAaveSupplyApy(aave, osToken, false)
const borrowApy = aave.borrowApy
// earned osToken shares earn extra staking rewards, apply compounding
const supplyApy = getCompoundedApy(aave.supplyApy, osTokenApy)

const vaultLeverageLtv = osTokenConfig.ltvPercent.lt(osTokenConfig.leverageMaxMintLtvPercent)
? osTokenConfig.ltvPercent
Expand All @@ -394,7 +394,7 @@ export function updateVaultMaxBoostApy(

// calculate vault staking rate and the rate paid for minting osToken
const vaultApy = getVaultApy(vault, false)
const osTokenMintApy = getVaultOsTokenMintApy(osToken, osTokenConfig, false)
const osTokenMintApy = getVaultOsTokenMintApy(osToken, osTokenConfig)

// calculate max boost apy for vault allocator
const allocatorDepositedAssets = wad
Expand Down Expand Up @@ -462,7 +462,7 @@ export function updateVaultMaxBoostApy(
}

// get the distribution APY
distributionApy = getPeriodicDistributionApy(distribution, osToken, false)
distributionApy = getPeriodicDistributionApy(distribution, osToken)
if (distributionApy.equals(BigDecimal.zero())) {
continue
}
Expand Down
Loading