diff --git a/package-lock.json b/package-lock.json index ed29a97..05c3fce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "v3-subgraph", - "version": "3.0.0", + "version": "3.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "v3-subgraph", - "version": "3.0.0", + "version": "3.0.1", "license": "AGPL-3.0-only", "devDependencies": { "@eslint/eslintrc": "3.2.0", diff --git a/package.json b/package.json index 866bc92..640fedc 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/entities/aave.ts b/src/entities/aave.ts index 7e26535..b5a960f 100644 --- a/src/entities/aave.ts +++ b/src/entities/aave.ts @@ -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 { @@ -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 { @@ -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 = 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 = 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!) diff --git a/src/entities/allocator.ts b/src/entities/allocator.ts index 15168a4..38241a9 100644 --- a/src/entities/allocator.ts +++ b/src/entities/allocator.ts @@ -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' @@ -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 @@ -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 @@ -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()], @@ -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) } @@ -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() } diff --git a/src/entities/leverageStrategy.ts b/src/entities/leverageStrategy.ts index eef36b1..8e95bf7 100644 --- a/src/entities/leverageStrategy.ts +++ b/src/entities/leverageStrategy.ts @@ -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' @@ -210,7 +210,6 @@ export function getBoostPositionAnnualReward( osTokenConfig: OsTokenConfig, strategyPosition: LeverageStrategyPosition, distributor: Distributor, - useDayApy: boolean, ): BigInt { const vaultAddress = Address.fromString(strategyPosition.vault) const proxyAddress = Address.fromBytes(strategyPosition.proxy) @@ -218,10 +217,11 @@ export function getBoostPositionAnnualReward( 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 @@ -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 @@ -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 } diff --git a/src/entities/merkleDistributor.ts b/src/entities/merkleDistributor.ts index c7fa0a7..4d0e8b3 100644 --- a/src/entities/merkleDistributor.ts +++ b/src/entities/merkleDistributor.ts @@ -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 { @@ -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 = 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 { diff --git a/src/entities/network.ts b/src/entities/network.ts index 55da025..e0acb4c 100644 --- a/src/entities/network.ts +++ b/src/entities/network.ts @@ -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() diff --git a/src/entities/osTokenHolder.ts b/src/entities/osTokenHolder.ts index 89574a2..a57c0ab 100644 --- a/src/entities/osTokenHolder.ts +++ b/src/entities/osTokenHolder.ts @@ -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' @@ -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) @@ -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 @@ -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], @@ -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() } diff --git a/src/entities/vault.ts b/src/entities/vault.ts index de4e12a..033a853 100644 --- a/src/entities/vault.ts +++ b/src/entities/vault.ts @@ -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 { @@ -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' @@ -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]) @@ -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 @@ -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 @@ -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 } diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 6d209f0..7a77db2 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -4,6 +4,9 @@ import { Multicall as MulticallContract, TryAggregateCallReturnDataStruct } from import { RewardSplitter as RewardSplitterContract } from '../../generated/Keeper/RewardSplitter' import { MULTICALL } from './constants' +const secondsInYear = '31536000' +const maxPercent = '100' + export function calculateMedian(values: Array): BigDecimal { if (values.length === 0) { return BigDecimal.zero() @@ -45,6 +48,18 @@ export function getAnnualReward(principal: BigInt, apy: BigDecimal): BigInt { return principal.toBigDecimal().times(apy).div(BigDecimal.fromString('100')).truncate(0).digits } +export function calculateApy(earnedAssets: BigInt, totalAssets: BigInt, durationInSeconds: BigInt): BigDecimal { + if (durationInSeconds.isZero() || totalAssets.isZero()) { + return BigDecimal.zero() + } + return earnedAssets + .toBigDecimal() + .times(BigDecimal.fromString(secondsInYear)) + .times(BigDecimal.fromString(maxPercent)) + .div(totalAssets.toBigDecimal()) + .div(durationInSeconds.toBigDecimal()) +} + export function getCompoundedApy(initialApyPercent: BigDecimal, secondaryApyPercent: BigDecimal): BigDecimal { const hundred = BigDecimal.fromString('100') diff --git a/src/mappings/erc20Token.ts b/src/mappings/erc20Token.ts index 2a0d077..a61f12e 100644 --- a/src/mappings/erc20Token.ts +++ b/src/mappings/erc20Token.ts @@ -84,7 +84,7 @@ function _handleOsTokenTransfer(event: Transfer): void { tokenHolderFrom.balance = tokenHolderFrom.balance.minus(amount) tokenHolderFrom.transfersCount = tokenHolderFrom.transfersCount.plus(BigInt.fromI32(1)) updateOsTokenHolderAssets(osToken, tokenHolderFrom) - tokenHolderFrom.apy = getOsTokenHolderApy(network, osToken, distributor, tokenHolderFrom, false) + tokenHolderFrom.apy = getOsTokenHolderApy(network, osToken, distributor, tokenHolderFrom) tokenHolderFrom.save() const user = createOrLoadUser(from) @@ -100,7 +100,7 @@ function _handleOsTokenTransfer(event: Transfer): void { tokenHolderTo.balance = tokenHolderTo.balance.plus(amount) tokenHolderTo.transfersCount = tokenHolderTo.transfersCount.plus(BigInt.fromI32(1)) updateOsTokenHolderAssets(osToken, tokenHolderTo) - tokenHolderTo.apy = getOsTokenHolderApy(network, osToken, distributor, tokenHolderTo, false) + tokenHolderTo.apy = getOsTokenHolderApy(network, osToken, distributor, tokenHolderTo) tokenHolderTo.save() const user = createOrLoadUser(to) diff --git a/src/mappings/erc20Vault.ts b/src/mappings/erc20Vault.ts index de2ef93..bd2748e 100644 --- a/src/mappings/erc20Vault.ts +++ b/src/mappings/erc20Vault.ts @@ -38,7 +38,7 @@ export function handleTransfer(event: Transfer): void { const allocatorFrom = loadAllocator(from, vaultAddress)! allocatorFrom.shares = allocatorFrom.shares.minus(shares) updateAllocatorAssets(osToken, osTokenConfig, vault, allocatorFrom) - allocatorFrom.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocatorFrom, false) + allocatorFrom.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocatorFrom) allocatorFrom.save() if (allocatorFrom.shares.isZero()) { decreaseUserVaultsCount(allocatorFrom.address) @@ -51,7 +51,7 @@ export function handleTransfer(event: Transfer): void { } allocatorTo.shares = allocatorTo.shares.plus(shares) updateAllocatorAssets(osToken, osTokenConfig, vault, allocatorTo) - allocatorTo.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocatorTo, false) + allocatorTo.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocatorTo) allocatorTo.save() createAllocatorAction(event, vaultAddress, AllocatorActionType.TransferIn, to, assets, shares) diff --git a/src/mappings/gnoVault.ts b/src/mappings/gnoVault.ts index 812f4e7..6b25d60 100644 --- a/src/mappings/gnoVault.ts +++ b/src/mappings/gnoVault.ts @@ -59,7 +59,7 @@ export function handleXdaiSwapped(event: XdaiSwapped): void { for (let j = 0; j < allocators.length; j++) { allocator = allocators[j] const earnedAssets = updateAllocatorAssets(osToken, osTokenConfig, vault, allocator) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator._periodEarnedAssets = allocator._periodEarnedAssets.plus(earnedAssets) allocator.save() } diff --git a/src/mappings/keeper.ts b/src/mappings/keeper.ts index 2920894..782f061 100644 --- a/src/mappings/keeper.ts +++ b/src/mappings/keeper.ts @@ -28,7 +28,7 @@ import { } from '../../generated/templates' import { Allocator, OsTokenHolder } from '../../generated/schema' import { createOrLoadOsToken, loadOsToken, updateOsTokenApy } from '../entities/osToken' -import { createOrLoadAllocator, getAllocatorApy, snapshotAllocator, updateAllocatorAssets } from '../entities/allocator' +import { createOrLoadAllocator, getAllocatorApy, updateAllocatorAssets } from '../entities/allocator' import { createOrLoadNetwork, increaseUserVaultsCount, loadNetwork } from '../entities/network' import { ConfigUpdated, @@ -253,12 +253,14 @@ export function handleRewardsUpdated(event: RewardsUpdated): void { } // update allocators + let earnedAssets: BigInt let allocator: Allocator let allocators: Array = vault.allocators.load() - const allocatorsAssetsDiffs: Array = [] for (let j = 0; j < allocators.length; j++) { allocator = allocators[j] - allocatorsAssetsDiffs.push(updateAllocatorAssets(osToken, osTokenConfig, vault, allocator)) + earnedAssets = updateAllocatorAssets(osToken, osTokenConfig, vault, allocator) + allocator._periodEarnedAssets = allocator._periodEarnedAssets.plus(earnedAssets) + allocator.save() } // update exit requests @@ -276,17 +278,8 @@ export function handleRewardsUpdated(event: RewardsUpdated): void { // update allocators apys for (let j = 0; j < allocators.length; j++) { allocator = allocators[j] - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() - snapshotAllocator( - osToken, - osTokenConfig, - vault, - distributor, - allocator, - allocatorsAssetsDiffs[j], - updateTimestamp, - ) } // update vault max boost apys @@ -298,7 +291,7 @@ export function handleRewardsUpdated(event: RewardsUpdated): void { const osTokenHolders: Array = osToken.holders.load() for (let i = 0; i < osTokenHolders.length; i++) { osTokenHolder = osTokenHolders[i] - osTokenHolder.apy = getOsTokenHolderApy(network, osToken, distributor, osTokenHolder, false) + osTokenHolder.apy = getOsTokenHolderApy(network, osToken, distributor, osTokenHolder) osTokenHolder.save() } diff --git a/src/mappings/leverageStrategy.ts b/src/mappings/leverageStrategy.ts index 8a19678..68c4205 100644 --- a/src/mappings/leverageStrategy.ts +++ b/src/mappings/leverageStrategy.ts @@ -39,11 +39,11 @@ function _updateAllocatorAndOsTokenHolderApys( ): void { const allocator = loadAllocator(userAddress, Address.fromString(vault.id)) if (allocator) { - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() } const osTokenHolder = loadOsTokenHolder(userAddress)! - osTokenHolder.apy = getOsTokenHolderApy(network, osToken, distributor, osTokenHolder, false) + osTokenHolder.apy = getOsTokenHolderApy(network, osToken, distributor, osTokenHolder) osTokenHolder.save() } diff --git a/src/mappings/osTokenVaultEscrow.ts b/src/mappings/osTokenVaultEscrow.ts index c8a7789..2eb318b 100644 --- a/src/mappings/osTokenVaultEscrow.ts +++ b/src/mappings/osTokenVaultEscrow.ts @@ -29,7 +29,7 @@ export function handlePositionCreated(event: PositionCreated): void { allocator.mintedOsTokenShares = BigInt.zero() } updateAllocatorMintedOsTokenShares(osToken, osTokenConfig, allocator, allocator.mintedOsTokenShares) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() const osTokenExitRequest = createOrLoadOsTokenExitRequest(vaultAddress, exitPositionTicket) diff --git a/src/mappings/periodicTasks.ts b/src/mappings/periodicTasks.ts index 59f407b..2fa73a3 100644 --- a/src/mappings/periodicTasks.ts +++ b/src/mappings/periodicTasks.ts @@ -81,7 +81,7 @@ export function handlePeriodicTasks(block: ethereum.Block): void { // update allocators apys for (let j = 0; j < allocators.length; j++) { allocator = allocators[j] - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() } @@ -92,7 +92,7 @@ export function handlePeriodicTasks(block: ethereum.Block): void { // update osToken holders apys for (let i = 0; i < osTokenHolders.length; i++) { osTokenHolder = osTokenHolders[i] - osTokenHolder.apy = getOsTokenHolderApy(network, osToken, distributor, osTokenHolder, false) + osTokenHolder.apy = getOsTokenHolderApy(network, osToken, distributor, osTokenHolder) osTokenHolder.save() } @@ -104,10 +104,21 @@ export function handlePeriodicTasks(block: ethereum.Block): void { function _updateSnapshots(network: Network, timestamp: BigInt): void { const newSnapshotsCount = timestamp.plus(BigInt.fromI32(secondsInHour)).div(BigInt.fromI32(secondsInDay)) - if (newSnapshotsCount.le(network.snapshotsCount)) { + const prevSnapshotsCount = network.lastSnapshotTimestamp + .plus(BigInt.fromI32(secondsInHour)) + .div(BigInt.fromI32(secondsInDay)) + if (newSnapshotsCount.le(prevSnapshotsCount)) { return } - network.snapshotsCount = newSnapshotsCount + if (network.lastSnapshotTimestamp.isZero()) { + // skip first snapshot + network.lastSnapshotTimestamp = timestamp + network.save() + return + } + + const duration = timestamp.minus(network.lastSnapshotTimestamp) + network.lastSnapshotTimestamp = timestamp network.save() const osToken = loadOsToken() @@ -119,30 +130,24 @@ function _updateSnapshots(network: Network, timestamp: BigInt): void { osToken.save() let osTokenHolder: OsTokenHolder - const distributor = loadDistributor() - if (!distributor) { - return - } const osTokenHolders: Array = osToken.holders.load() for (let i = 0; i < osTokenHolders.length; i++) { osTokenHolder = osTokenHolders[i] - snapshotOsTokenHolder(network, osToken, distributor, osTokenHolder, osTokenHolder._periodEarnedAssets, timestamp) + snapshotOsTokenHolder(network, osToken, osTokenHolder, osTokenHolder._periodEarnedAssets, duration, timestamp) osTokenHolder._periodEarnedAssets = BigInt.zero() osTokenHolder.save() } let vault: Vault - let osTokenConfig: OsTokenConfig const vaultIds = network.vaultIds for (let i = 0; i < vaultIds.length; i++) { vault = loadVault(Address.fromString(vaultIds[i]))! - osTokenConfig = loadOsTokenConfig(vault.osTokenConfig)! snapshotVault(vault, BigInt.zero(), timestamp) const allocators: Array = vault.allocators.load() for (let j = 0; j < allocators.length; j++) { const allocator = allocators[j] - snapshotAllocator(osToken, osTokenConfig, vault, distributor, allocator, allocator._periodEarnedAssets, timestamp) + snapshotAllocator(osToken, vault, allocator, allocator._periodEarnedAssets, duration, timestamp) allocator._periodEarnedAssets = BigInt.zero() allocator.save() } diff --git a/src/mappings/vault.ts b/src/mappings/vault.ts index eadbe7e..a35a8e7 100644 --- a/src/mappings/vault.ts +++ b/src/mappings/vault.ts @@ -75,7 +75,7 @@ export function handleDeposited(event: Deposited): void { } allocator.shares = allocator.shares.plus(shares) updateAllocatorAssets(osToken, osTokenConfig, vault, allocator) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() const txHash = event.transaction.hash.toHex() @@ -120,7 +120,7 @@ export function handleRedeemed(event: Redeemed): void { const allocator = loadAllocator(owner, vaultAddress)! allocator.shares = allocator.shares.minus(shares) updateAllocatorAssets(osToken, osTokenConfig, vault, allocator) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() if (allocator.shares.isZero()) { @@ -323,7 +323,7 @@ export function handleV1ExitQueueEntered(event: V1ExitQueueEntered): void { const allocator = loadAllocator(owner, event.address)! allocator.shares = allocator.shares.minus(shares) updateAllocatorAssets(osToken, osTokenConfig, vault, allocator) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() if (allocator.shares.isZero()) { @@ -403,7 +403,7 @@ export function handleV2ExitQueueEntered(event: V2ExitQueueEntered): void { const allocator = loadAllocator(owner, vaultAddress)! allocator.shares = allocator.shares.minus(shares) updateAllocatorAssets(osToken, osTokenConfig, vault, allocator) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() if (allocator.shares.isZero()) { @@ -529,7 +529,7 @@ export function handleOsTokenMinted(event: OsTokenMinted): void { const distributor = loadDistributor()! allocator.mintedOsTokenShares = allocator.mintedOsTokenShares.plus(shares) updateAllocatorMintedOsTokenShares(osToken, osTokenConfig, allocator, allocator.mintedOsTokenShares) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() createAllocatorAction(event, vaultAddress, AllocatorActionType.OsTokenMinted, holder, assets, shares) @@ -563,7 +563,7 @@ export function handleOsTokenBurned(event: OsTokenBurned): void { allocator.mintedOsTokenShares = BigInt.zero() } updateAllocatorMintedOsTokenShares(osToken, osTokenConfig, allocator, allocator.mintedOsTokenShares) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() const txHash = event.transaction.hash.toHex() @@ -609,7 +609,7 @@ export function handleOsTokenLiquidated(event: OsTokenLiquidated): void { allocator.mintedOsTokenShares = BigInt.zero() } updateAllocatorMintedOsTokenShares(osToken, osTokenConfig, allocator, allocator.mintedOsTokenShares) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() if (allocator.shares.isZero()) { @@ -658,7 +658,7 @@ export function handleOsTokenRedeemed(event: OsTokenRedeemed): void { allocator.mintedOsTokenShares = BigInt.zero() } updateAllocatorMintedOsTokenShares(osToken, osTokenConfig, allocator, allocator.mintedOsTokenShares) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() if (allocator.shares.isZero()) { @@ -846,7 +846,7 @@ export function handleMigrated(event: Migrated): void { } allocator.shares = allocator.shares.plus(shares) updateAllocatorAssets(osToken, osTokenConfig, vault, allocator) - allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator, false) + allocator.apy = getAllocatorApy(osToken, osTokenConfig, vault, distributor, allocator) allocator.save() const txHash = event.transaction.hash.toHex() diff --git a/src/schema.graphql b/src/schema.graphql index c700976..d267f95 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -489,8 +489,8 @@ type Network @entity { "The total number of non repeated vault allocators and osToken holders" usersCount: Int! - "The total number of snapshots" - snapshotsCount: BigInt! + "The timestamp of the last snapshot" + lastSnapshotTimestamp: BigInt! } type User @entity {