diff --git a/src/modules/staking/models/staking.model.ts b/src/modules/staking/models/staking.model.ts index 20a08545a..93a34a310 100644 --- a/src/modules/staking/models/staking.model.ts +++ b/src/modules/staking/models/staking.model.ts @@ -34,6 +34,8 @@ export class StakingModel { @Field() apr: string; @Field() + aprUncapped: string; + @Field() boostedApr: string; @Field(() => Int) minUnboundEpochs: number; @@ -44,6 +46,8 @@ export class StakingModel { @Field() rewardsRemainingDays: number; @Field() + rewardsRemainingDaysUncapped: number; + @Field() divisionSafetyConstant: string; @Field() produceRewardsEnabled: boolean; diff --git a/src/modules/staking/services/staking.compute.service.ts b/src/modules/staking/services/staking.compute.service.ts index 918521353..f6046ccc7 100644 --- a/src/modules/staking/services/staking.compute.service.ts +++ b/src/modules/staking/services/staking.compute.service.ts @@ -209,17 +209,9 @@ export class StakingComputeService { } async computeStakeFarmAPR(stakeAddress: string): Promise { - const [accumulatedRewards, rewardsCapacity, produceRewardsEnabled] = - await Promise.all([ - this.stakingAbi.accumulatedRewards(stakeAddress), - this.stakingAbi.rewardCapacity(stakeAddress), - this.stakingAbi.produceRewardsEnabled(stakeAddress), - ]); + const isProducingRewards = await this.isProducingRewards(stakeAddress); - if ( - !produceRewardsEnabled || - new BigNumber(accumulatedRewards).isEqualTo(rewardsCapacity) - ) { + if (isProducingRewards) { return '0'; } @@ -252,6 +244,37 @@ export class StakingComputeService { .toFixed(); } + @ErrorLoggerAsync({ + logArgs: true, + }) + @GetOrSetCache({ + baseKey: 'stake', + remoteTtl: CacheTtlInfo.ContractState.remoteTtl, + localTtl: CacheTtlInfo.ContractState.localTtl, + }) + async stakeFarmUncappedAPR(stakeAddress: string): Promise { + return await this.computeStakeFarmUncappedAPR(stakeAddress); + } + + async computeStakeFarmUncappedAPR(stakeAddress: string): Promise { + const isProducingRewards = await this.isProducingRewards(stakeAddress); + + if (isProducingRewards) { + return '0'; + } + + const [perBlockRewardAmount, farmTokenSupply] = await Promise.all([ + this.stakingAbi.perBlockRewardsAmount(stakeAddress), + this.stakingAbi.farmTokenSupply(stakeAddress), + ]); + + const rewardsUnboundedBig = new BigNumber( + perBlockRewardAmount, + ).multipliedBy(constantsConfig.BLOCKS_IN_YEAR); + + return rewardsUnboundedBig.dividedBy(farmTokenSupply).toFixed(); + } + @ErrorLoggerAsync({ logArgs: true, }) @@ -322,6 +345,25 @@ export class StakingComputeService { } async computeRewardsRemainingDays(stakeAddress: string): Promise { + const extraRewardsAPRBoundedPerBlock = + await this.computeExtraRewardsAPRBoundedPerBlock(stakeAddress); + + return await this.computeRewardsRemainingDaysBase( + stakeAddress, + extraRewardsAPRBoundedPerBlock, + ); + } + + async computeRewardsRemainingDaysUncapped( + stakeAddress: string, + ): Promise { + return await this.computeRewardsRemainingDaysBase(stakeAddress); + } + + async computeRewardsRemainingDaysBase( + stakeAddress: string, + extraRewardsAPRBoundedPerBlock?: BigNumber, + ): Promise { const [perBlockRewardAmount, accumulatedRewards, rewardsCapacity] = await Promise.all([ this.stakingAbi.perBlockRewardsAmount(stakeAddress), @@ -329,17 +371,16 @@ export class StakingComputeService { this.stakingAbi.rewardCapacity(stakeAddress), ]); + const perBlockRewards = extraRewardsAPRBoundedPerBlock + ? BigNumber.min( + extraRewardsAPRBoundedPerBlock, + perBlockRewardAmount, + ) + : new BigNumber(perBlockRewardAmount); + // 10 blocks per minute * 60 minutes per hour * 24 hours per day const blocksInDay = 10 * 60 * 24; - const extraRewardsAPRBoundedPerBlock = - await this.computeExtraRewardsAPRBoundedPerBlock(stakeAddress); - - const perBlockRewards = BigNumber.min( - extraRewardsAPRBoundedPerBlock, - perBlockRewardAmount, - ); - return parseFloat( new BigNumber(rewardsCapacity) .minus(accumulatedRewards) @@ -920,4 +961,22 @@ export class StakingComputeService { ); return deployedAt ?? undefined; } + + private async isProducingRewards(stakeAddress: string): Promise { + const [accumulatedRewards, rewardsCapacity, produceRewardsEnabled] = + await Promise.all([ + this.stakingAbi.accumulatedRewards(stakeAddress), + this.stakingAbi.rewardCapacity(stakeAddress), + this.stakingAbi.produceRewardsEnabled(stakeAddress), + ]); + + if ( + !produceRewardsEnabled || + new BigNumber(accumulatedRewards).isEqualTo(rewardsCapacity) + ) { + return true; + } + + return false; + } } diff --git a/src/modules/staking/staking.resolver.ts b/src/modules/staking/staking.resolver.ts index e4907bee6..22d1f86d0 100644 --- a/src/modules/staking/staking.resolver.ts +++ b/src/modules/staking/staking.resolver.ts @@ -144,6 +144,11 @@ export class StakingResolver { return this.stakingCompute.stakeFarmAPR(parent.address); } + @ResolveField() + async aprUncapped(@Parent() parent: StakingModel) { + return this.stakingCompute.stakeFarmUncappedAPR(parent.address); + } + @ResolveField() async boostedApr(@Parent() parent: StakingModel) { return this.stakingCompute.boostedAPR(parent.address); @@ -169,6 +174,13 @@ export class StakingResolver { return this.stakingCompute.computeRewardsRemainingDays(parent.address); } + @ResolveField() + async rewardsRemainingDaysUncapped(@Parent() parent: StakingModel) { + return this.stakingCompute.computeRewardsRemainingDaysUncapped( + parent.address, + ); + } + @ResolveField() async divisionSafetyConstant(@Parent() parent: StakingModel) { return this.stakingAbi.divisionSafetyConstant(parent.address);