ICP/SNS staking rewards calculation?

Anybody have the calculation scripts for ICP and SNS projects?

1 Like

I think you are looking for fn distribute_rewards.

I’m looking for a way to calculate the estimated APY when creating a neuron, specifically the Voting rewards calculation on the dashboard:

Hello @dostro
For the ICP Dashboard here is how we fetch the data for the Estimate Rewards

  const fetchData =  async () => {
      try {
        // https://ic-api.internetcomputer.org/api/v3/governance-metrics
        const response = await queryApi('v3/governance-metrics', ApiType.IC);

        const lastRewardRoundTotalAvailableE8s: any = response.data.metrics.find((element: any) => {
          return element.name === 'governance_latest_reward_round_total_available_e8s'
        });

        const lastRewardsRoundTotalAvailableIcp: number =
          Number(lastRewardRoundTotalAvailableE8s.subsets[0].value[1]) / 100000000;
        setLastRewardsRoundTotalAvailableIcp(lastRewardsRoundTotalAvailableIcp);

        const totalVotingPowerE8s: any = response.data.metrics.find((element: any) => {
          return element.name === 'governance_voting_power_total'
        });
        const totalVotingPower: number =
          Number(totalVotingPowerE8s.subsets[0].value[1]) / 100000000;
        setTotalVotingPower(totalVotingPower);
      } catch {
        setLastRewardsRoundTotalAvailableIcp(undefined);
        setTotalVotingPower(undefined);
      }
    };

And here is how we do the data calculations

    if (lastRewardsRoundTotalAvailableIcp !== undefined
      && totalVotingPower !== undefined
      && totalVotingPower > 0) {

      let estimatedRewardsPercentage: number | undefined;
      let estimatedNeuronDailyRewards: number | undefined;
      if (sliderState.dissolveDelayYears >= 0.5) {
        const dissolveDelayBonusSlider: number = 1 + sliderState.dissolveDelayYears / 8;
        const dailyRewardsIcpPerVotingPowerUnit: number =
          lastRewardsRoundTotalAvailableIcp / totalVotingPower;

        let ageBonus: number;
        if (neuronInfo !== undefined) {
          ageBonus =
            1 +
            Math.min(neuronInfo.ageSeconds, Constants.governanceMaxNeuronAgeForAgeBonusSeconds) /
            Constants.governanceMaxNeuronAgeForAgeBonusSeconds / 4;

          const dissolveDelayBonusActual: number =
            1 + (NeuronInfo.getDissolveDelayBonusPercentage(neuronInfo) ?? 0) / 100;
          estimatedNeuronDailyRewards =
            neuronInfo.votingPowerE8s / 100000000 *
            dailyRewardsIcpPerVotingPowerUnit *
            dissolveDelayBonusSlider / dissolveDelayBonusActual;

        } else
          ageBonus = 1;

        const estimatedRewardsIcpPerVotingPowerUnit: number =
          dailyRewardsIcpPerVotingPowerUnit * dissolveDelayBonusSlider * ageBonus;
        estimatedRewardsPercentage = estimatedRewardsIcpPerVotingPowerUnit * 365.25 * 100;

      } else {
        estimatedRewardsPercentage = 0;
        estimatedNeuronDailyRewards = neuronInfo !== undefined ? 0 : undefined;
      }
      setEstimatedRewardsPercentage(estimatedRewardsPercentage);
      setEstimatedNeuronDailyRewards(estimatedNeuronDailyRewards);
    } 
  }
4 Likes

Thank you! Will take a look in the coming weeks and let you know how it goes.

2 Likes

hi @ngurian,
could you please take a look?

I have a private method that returns dailyRewards (lastRewardsRoundTotalAvailableIcp / totalVotingPower)

private async getIcpDashboardData(): Promise<
    | {
        dailyRewardsPerVotingPowerUnit: number
      }
    | undefined
  > {
    if (this.icpDashboardData) return this.icpDashboardData

    try {
      const response = await fetch(
        "https://ic-api.internetcomputer.org/api/v3/governance-metrics",
      )

      const data = await response.json()

      const latestRewards =
        Number(
          data.metrics.find(
            (element: any) =>
              element.name ===
              "governance_latest_reward_round_total_available_e8s",
          ).subsets[0].value[1],
        ) /
        10 ** this.token.getTokenDecimals()

      const votingPower: any =
        Number(
          data.metrics.find(
            (element: any) => element.name === "governance_voting_power_total",
          ).subsets[0].value[1],
        ) /
        10 ** this.token.getTokenDecimals()

      this.icpDashboardData = {
        dailyRewardsPerVotingPowerUnit: latestRewards / votingPower,
      }

      return this.icpDashboardData
    } catch (e) {
      console.error("ICP Dahsboard Error: ", e)
    }
  }

Also I have two separate methods for calculating apr and rewards:

  1. APR:
    Here I can’t understand what is 4 (ageBonus last devide action)? Is it a constant value or where it can be taken?
async calculateEstAPR(
    lockValueInMonths: number,
    lockValue: number,
  ): Promise<string | undefined> {
    const data = await this.getIcpDashboardData()
    if (data === undefined) return

    const dissolveDelayBonusSlider =
      1 + lockValueInMonths / (this.getMaximumLockTimeInMonths() / 12)

    const ageBonus =
      1 +
      Math.min(lockValue, Number(this.params.max_neuron_age_for_age_bonus[0])) /
        Number(this.params.max_neuron_age_for_age_bonus[0]) /
        4

    const estimatedRewardsIcpPerVotingPowerUnit: number =
    data.dailyRewardsPerVotingPowerUnit * dissolveDelayBonusSlider * ageBonus

    return `${(estimatedRewardsIcpPerVotingPowerUnit * 365.25 * 100).toFixed(
      2,
    )}%`
  }
  1. Projected Rewards:
    I am confused where should I take the votingPowerE8s variable??
async calculateProjectRewards(
    amount: string,
    lockValueInMonths: number,
  ): Promise<TokenValue | undefined> {
    const data = await this.getIcpDashboardData()
    if (data === undefined) return
    const { dailyRewardsPerVotingPowerUnit } = data

    const dissolveDelayBonusSlider =
      1 + lockValueInMonths / 12 / this.getMaximumLockTimeInMonths() / 12

    const dissolveDelayBonusActual =
      1 +
      (Number(this.params.max_dissolve_delay_bonus_percentage[0]) ?? 0) / 100

    const votingPowerE8s = 1 // ???

    const estimatedNeuronDailyRewards =
      ((votingPowerE8s / 10 ** this.token.getTokenDecimals()) *
        dailyRewardsPerVotingPowerUnit *
        dissolveDelayBonusSlider) /
      dissolveDelayBonusActual

    const value = (estimatedNeuronDailyRewards * lockValueInMonths * 30)
      .toFixed(this.token.getTokenDecimals())
      .replace(TRIM_ZEROS, "")

    return {
      getTokenValue: () => `${value} ${this.token.getTokenSymbol()}`,
      getUSDValue: () =>
        this.token.getTokenRateFormatted(value) || "Not listed",
    }
  }
1 Like