Skip to content

Commit

Permalink
fix: correct epoch for getting the latest pool update
Browse files Browse the repository at this point in the history
  • Loading branch information
fabianbormann committed Feb 7, 2024
1 parent 81e3ee7 commit 493b66e
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -239,4 +239,130 @@ public static PoolRewardCalculationResult calculatePoolRewardInEpoch(String pool
poolRewardCalculationResult.setMemberRewards(memberRewards);
return poolRewardCalculationResult;
}

public static PoolRewardCalculationResult calculatePoolRewardInEpoch(String poolId, Epoch epochInfo,
AdaPots adaPotsForNextEpoch,
ProtocolParameters protocolParameters,
DataProvider dataProvider) {
// Step 1: Get Pool information of current epoch
// Example: https://api.koios.rest/api/v0/pool_history?_pool_bech32=pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt&_epoch_no=210
int epoch = epochInfo.getNumber();
PoolRewardCalculationResult poolRewardCalculationResult = PoolRewardCalculationResult.builder()
.epoch(epoch)
.poolId(poolId)
.poolReward(0.0)
.build();

PoolHistory poolHistoryCurrentEpoch = dataProvider.getPoolHistory(poolId, epoch);
if(poolHistoryCurrentEpoch == null) {
return poolRewardCalculationResult;
}

double poolStake = poolHistoryCurrentEpoch.getActiveStake();
double poolFees = poolHistoryCurrentEpoch.getPoolFees();
double poolMargin = poolHistoryCurrentEpoch.getMargin();
double poolFixedCost = poolHistoryCurrentEpoch.getFixedCost();
int blocksPoolHasMinted = poolHistoryCurrentEpoch.getBlockCount();

poolRewardCalculationResult.setPoolFee(poolFees);
poolRewardCalculationResult.setPoolMargin(poolMargin);
poolRewardCalculationResult.setPoolCost(poolFixedCost);
poolRewardCalculationResult.setRewardAddress(poolHistoryCurrentEpoch.getRewardAddress());

if (blocksPoolHasMinted == 0) {
return poolRewardCalculationResult;
}

double activeStakeInEpoch = 0;
if (epochInfo.getActiveStake() != null) {
activeStakeInEpoch = epochInfo.getActiveStake();
}

// The Shelley era and the ada pot system started on mainnet in epoch 208.
// Fee and treasury values are 0 for epoch 208.
double totalFeesForCurrentEpoch = 0.0;
if (epoch > 209) {
totalFeesForCurrentEpoch = epochInfo.getFees();
}

int totalBlocksInEpoch = epochInfo.getBlockCount();

if (epoch > 212 && epoch < 255) {
totalBlocksInEpoch = epochInfo.getNonOBFTBlockCount();
}

// Get the ada reserves for the next epoch because it was already updated (int the previous step)
double reserves = adaPotsForNextEpoch.getReserves();

// Step 3: Get total ada in circulation
double adaInCirculation = TOTAL_LOVELACE - reserves;

// Step 4: Get protocol parameters for current epoch
double decentralizationParameter = protocolParameters.getDecentralisation();
int optimalPoolCount = protocolParameters.getOptimalPoolCount();
double influenceParam = protocolParameters.getPoolOwnerInfluence();
double monetaryExpandRate = protocolParameters.getMonetaryExpandRate();
double treasuryGrowRate = protocolParameters.getTreasuryGrowRate();

// Step 5: Calculate apparent pool performance
double apparentPoolPerformance =
PoolRewardCalculation.calculateApparentPoolPerformance(poolStake, activeStakeInEpoch,
blocksPoolHasMinted, totalBlocksInEpoch, decentralizationParameter);
poolRewardCalculationResult.setApparentPoolPerformance(apparentPoolPerformance);
// Step 6: Calculate total available reward for pools (total reward pot after treasury cut)
// -----
double totalRewardPot = TreasuryCalculation.calculateTotalRewardPotWithEta(
monetaryExpandRate, totalBlocksInEpoch, decentralizationParameter, reserves, totalFeesForCurrentEpoch);

double stakePoolRewardsPot = (totalRewardPot - Math.floor(totalRewardPot * treasuryGrowRate));
poolRewardCalculationResult.setStakePoolRewardsPot(stakePoolRewardsPot);
// shelley-delegation.pdf 5.5.3
// "[...]the relative stake of the pool owner(s) (the amount of ada
// pledged during pool registration)"

// Step 7: Get the latest pool update before this epoch and extract the pledge
double poolPledge = dataProvider.getPoolPledgeInEpoch(poolId, epoch);

PoolOwnerHistory poolOwnersHistoryInEpoch = dataProvider.getHistoryOfPoolOwnersInEpoch(poolId, epoch);
double totalActiveStakeOfOwners = poolOwnersHistoryInEpoch.getActiveStake();
poolRewardCalculationResult.setPoolOwnerStakeAddresses(poolOwnersHistoryInEpoch.getStakeAddresses());

if (totalActiveStakeOfOwners < poolPledge) {
return poolRewardCalculationResult;
}

double relativeStakeOfPoolOwner = poolPledge / adaInCirculation;
double relativePoolStake = poolStake / adaInCirculation;

// Step 8: Calculate optimal pool reward
double optimalPoolReward =
PoolRewardCalculation.calculateOptimalPoolReward(
stakePoolRewardsPot,
optimalPoolCount,
influenceParam,
relativePoolStake,
relativeStakeOfPoolOwner);
poolRewardCalculationResult.setOptimalPoolReward(optimalPoolReward);

// Step 9: Calculate pool reward as optimal pool reward * apparent pool performance
double poolReward = PoolRewardCalculation.calculatePoolReward(optimalPoolReward, apparentPoolPerformance);
poolRewardCalculationResult.setPoolReward(poolReward);

// Step 10: Calculate pool operator reward
double poolOperatorReward = PoolRewardCalculation.calculateLeaderReward(poolReward, poolMargin, poolFixedCost,
totalActiveStakeOfOwners / adaInCirculation, relativePoolStake);
poolRewardCalculationResult.setOperatorReward(poolOperatorReward);
// Step 11: Calculate pool member reward
List<Reward> memberRewards = new ArrayList<>();
for (Delegator delegator : poolHistoryCurrentEpoch.getDelegators()) {
double memberReward = PoolRewardCalculation.calculateMemberReward(poolReward, poolMargin,
poolFixedCost, delegator.getActiveStake() / adaInCirculation, relativePoolStake);
memberRewards.add(Reward.builder()
.amount(memberReward)
.stakeAddress(delegator.getStakeAddress())
.build());
}
poolRewardCalculationResult.setMemberRewards(memberRewards);
return poolRewardCalculationResult;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public PoolHistory getPoolHistory(String poolId, int epoch) {
Integer blockCount = dbSyncBlockRepository.getBlocksMadeByPoolInEpoch(poolId, epoch);
poolHistory.setBlockCount(blockCount);

DbSyncPoolUpdate dbSyncPoolUpdate = dbSyncPoolUpdateRepository.findLastestUpdateForEpoch(poolId, epoch - 1);
DbSyncPoolUpdate dbSyncPoolUpdate = dbSyncPoolUpdateRepository.findLastestUpdateForEpoch(poolId, epoch);
poolHistory.setFixedCost(dbSyncPoolUpdate.getFixedCost());
poolHistory.setMargin(dbSyncPoolUpdate.getMargin());
poolHistory.setEpoch(epoch);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package org.cardanofoundation.rewards.calculation;

import org.cardanofoundation.rewards.data.provider.DbSyncDataProvider;
import org.cardanofoundation.rewards.entity.PoolHistory;
import org.cardanofoundation.rewards.entity.PoolRewardCalculationResult;
import org.cardanofoundation.rewards.entity.Reward;
import org.cardanofoundation.rewards.entity.TreasuryCalculationResult;
import org.cardanofoundation.rewards.entity.*;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -14,7 +11,6 @@

import java.util.List;

import static org.cardanofoundation.rewards.util.CurrencyConverter.adaToLovelace;
import static org.cardanofoundation.rewards.util.CurrencyConverter.lovelaceToAda;

/*
Expand All @@ -35,6 +31,10 @@ public void testCalculateEpochRewards(final int epoch) {
double totalDifference = 0.0;
double totalDistributedRewards = 0.0;

AdaPots adaPotsForNextEpoch = dataProvider.getAdaPotsForEpoch(epoch + 1);
ProtocolParameters protocolParameters = dataProvider.getProtocolParametersForEpoch(epoch);
Epoch epochInfo = dataProvider.getEpochInfo(epoch);

for (int i = 0; i < poolIds.size(); i++) {
String poolId = poolIds.get(i);

Expand All @@ -43,7 +43,7 @@ public void testCalculateEpochRewards(final int epoch) {
System.out.println("Pool rewards for pool " + poolId + " in epoch " + epoch + " fetched in " + (System.currentTimeMillis() - start) + " ms");

start = System.currentTimeMillis();
PoolRewardCalculationResult poolRewardCalculationResult = PoolRewardCalculation.calculatePoolRewardInEpoch(poolId, epoch, dataProvider);
PoolRewardCalculationResult poolRewardCalculationResult = PoolRewardCalculation.calculatePoolRewardInEpoch(poolId, epochInfo, adaPotsForNextEpoch, protocolParameters, dataProvider);
System.out.println("Pool reward for pool " + poolId + " in epoch " + epoch + " calculated in " + (System.currentTimeMillis() - start) + " ms");

System.out.println("Pool (" + i + "/" + poolIds.size() + ") " + poolId + " reward: " + poolRewardCalculationResult.getPoolReward());
Expand Down Expand Up @@ -71,19 +71,23 @@ public void testCalculateEpochRewards(final int epoch) {
if (poolRewardCalculationResult.getPoolOwnerStakeAddresses().contains(reward.getStakeAddress())) {
double poolOwnerReward = poolRewardCalculationResult.getOperatorReward();
difference = Math.abs(reward.getAmount() - poolOwnerReward);
totalDistributedRewards += poolOwnerReward;
}

if (difference > 0.0) {
System.out.println("The difference between expected member " + reward.getStakeAddress() + " reward and actual member reward is : " + lovelaceToAda(difference) + " ADA");
}

totalDifference += difference;
totalDistributedRewards += reward.getAmount();
}

System.out.println("Total difference: " + lovelaceToAda(totalDifference) + " ADA");
}

System.out.println("Total difference for epoch " + epoch + ": " + lovelaceToAda(totalDifference) + " ADA");
System.out.println("Total distributed rewards for epoch " + epoch + ": " + lovelaceToAda(totalDistributedRewards) + " ADA");
System.out.println("Total available rewards for epoch " + epoch + ": " + lovelaceToAda(adaPotsForNextEpoch.getRewards()));
}

@Test
Expand Down

0 comments on commit 493b66e

Please sign in to comment.