Skip to content

Commit 9461dae

Browse files
committed
fix: move actual business logic into pool rewards calculation class
1 parent 9e1a955 commit 9461dae

File tree

10 files changed

+236
-136
lines changed

10 files changed

+236
-136
lines changed

src/main/java/org/cardanofoundation/rewards/calculation/PoolRewardCalculation.java

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
package org.cardanofoundation.rewards.calculation;
22

3+
import org.cardanofoundation.rewards.data.provider.DataProvider;
4+
import org.cardanofoundation.rewards.entity.*;
5+
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
9+
import static org.cardanofoundation.rewards.constants.RewardConstants.TOTAL_LOVELACE;
10+
311
public class PoolRewardCalculation {
412

513
/*
@@ -103,4 +111,130 @@ public static double calculateMemberReward(double poolReward, double margin, dou
103111
double relativeMemberStake, double relativeStakeOfPool) {
104112
return Math.floor((poolReward - poolCost) * (1 - margin) * relativeMemberStake / relativeStakeOfPool);
105113
}
114+
115+
public static PoolRewardCalculationResult calculatePoolRewardInEpoch(String poolId, int epoch, DataProvider dataProvider) {
116+
// Step 1: Get Pool information of current epoch
117+
// Example: https://api.koios.rest/api/v0/pool_history?_pool_bech32=pool1z5uqdk7dzdxaae5633fqfcu2eqzy3a3rgtuvy087fdld7yws0xt&_epoch_no=210
118+
PoolRewardCalculationResult poolRewardCalculationResult = PoolRewardCalculationResult.builder()
119+
.epoch(epoch)
120+
.poolId(poolId)
121+
.poolReward(0.0)
122+
.build();
123+
124+
PoolHistory poolHistoryCurrentEpoch = dataProvider.getPoolHistory(poolId, epoch);
125+
if(poolHistoryCurrentEpoch == null) {
126+
return poolRewardCalculationResult;
127+
}
128+
129+
double poolStake = poolHistoryCurrentEpoch.getActiveStake();
130+
double poolFees = poolHistoryCurrentEpoch.getPoolFees();
131+
double poolMargin = poolHistoryCurrentEpoch.getMargin();
132+
double poolFixedCost = poolHistoryCurrentEpoch.getFixedCost();
133+
int blocksPoolHasMinted = poolHistoryCurrentEpoch.getBlockCount();
134+
135+
poolRewardCalculationResult.setPoolFee(poolFees);
136+
poolRewardCalculationResult.setPoolMargin(poolMargin);
137+
poolRewardCalculationResult.setPoolCost(poolFixedCost);
138+
139+
if (blocksPoolHasMinted == 0) {
140+
return poolRewardCalculationResult;
141+
}
142+
143+
// Step 2: Get Epoch information of current epoch
144+
// Source: https://api.koios.rest/api/v0/epoch_info?_epoch_no=211
145+
Epoch epochInfo = dataProvider.getEpochInfo(epoch);
146+
147+
double activeStakeInEpoch = 0;
148+
if (epochInfo.getActiveStake() != null) {
149+
activeStakeInEpoch = epochInfo.getActiveStake();
150+
}
151+
152+
// The Shelley era and the ada pot system started on mainnet in epoch 208.
153+
// Fee and treasury values are 0 for epoch 208.
154+
double totalFeesForCurrentEpoch = 0.0;
155+
if (epoch > 209) {
156+
totalFeesForCurrentEpoch = epochInfo.getFees();
157+
}
158+
159+
int totalBlocksInEpoch = epochInfo.getBlockCount();
160+
161+
if (epoch > 212 && epoch < 255) {
162+
totalBlocksInEpoch = epochInfo.getNonOBFTBlockCount();
163+
}
164+
165+
// Get the ada reserves for the next epoch because it was already updated yet
166+
AdaPots adaPotsForNextEpoch = dataProvider.getAdaPotsForEpoch(epoch + 1);
167+
double reserves = adaPotsForNextEpoch.getReserves();
168+
169+
// Step 3: Get total ada in circulation
170+
double adaInCirculation = TOTAL_LOVELACE - reserves;
171+
172+
// Step 4: Get protocol parameters for current epoch
173+
ProtocolParameters protocolParameters = dataProvider.getProtocolParametersForEpoch(epoch);
174+
double decentralizationParameter = protocolParameters.getDecentralisation();
175+
int optimalPoolCount = protocolParameters.getOptimalPoolCount();
176+
double influenceParam = protocolParameters.getPoolOwnerInfluence();
177+
double monetaryExpandRate = protocolParameters.getMonetaryExpandRate();
178+
double treasuryGrowRate = protocolParameters.getTreasuryGrowRate();
179+
180+
// Step 5: Calculate apparent pool performance
181+
double apparentPoolPerformance =
182+
PoolRewardCalculation.calculateApparentPoolPerformance(poolStake, activeStakeInEpoch,
183+
blocksPoolHasMinted, totalBlocksInEpoch, decentralizationParameter);
184+
poolRewardCalculationResult.setApparentPoolPerformance(apparentPoolPerformance);
185+
// Step 6: Calculate total available reward for pools (total reward pot after treasury cut)
186+
// -----
187+
double totalRewardPot = TreasuryCalculation.calculateTotalRewardPotWithEta(
188+
monetaryExpandRate, totalBlocksInEpoch, decentralizationParameter, reserves, totalFeesForCurrentEpoch);
189+
190+
double stakePoolRewardsPot = totalRewardPot - Math.floor(totalRewardPot * treasuryGrowRate);
191+
poolRewardCalculationResult.setStakePoolRewardsPot(stakePoolRewardsPot);
192+
// shelley-delegation.pdf 5.5.3
193+
// "[...]the relative stake of the pool owner(s) (the amount of ada
194+
// pledged during pool registration)"
195+
196+
// Step 7: Get the latest pool update before this epoch and extract the pledge
197+
double poolPledge = dataProvider.getPoolPledgeInEpoch(poolId, epoch);
198+
199+
PoolOwnerHistory poolOwnersHistoryInEpoch = dataProvider.getHistoryOfPoolOwnersInEpoch(poolId, epoch);
200+
double totalActiveStakeOfOwners = poolOwnersHistoryInEpoch.getActiveStake();
201+
202+
if (totalActiveStakeOfOwners < poolPledge) {
203+
return poolRewardCalculationResult;
204+
}
205+
206+
double relativeStakeOfPoolOwner = poolPledge / adaInCirculation;
207+
double relativePoolStake = poolStake / adaInCirculation;
208+
209+
// Step 8: Calculate optimal pool reward
210+
double optimalPoolReward =
211+
PoolRewardCalculation.calculateOptimalPoolReward(
212+
stakePoolRewardsPot,
213+
optimalPoolCount,
214+
influenceParam,
215+
relativePoolStake,
216+
relativeStakeOfPoolOwner);
217+
poolRewardCalculationResult.setOptimalPoolReward(optimalPoolReward);
218+
219+
// Step 9: Calculate pool reward as optimal pool reward * apparent pool performance
220+
double poolReward = PoolRewardCalculation.calculatePoolReward(optimalPoolReward, apparentPoolPerformance);
221+
poolRewardCalculationResult.setPoolReward(poolReward);
222+
223+
// Step 10: Calculate pool operator reward
224+
double poolOperatorReward = PoolRewardCalculation.calculateLeaderReward(poolReward, poolMargin, poolFixedCost,
225+
totalActiveStakeOfOwners / adaInCirculation, relativePoolStake);
226+
poolRewardCalculationResult.setOperatorReward(poolOperatorReward);
227+
// Step 11: Calculate pool member reward
228+
List<Reward> memberRewards = new ArrayList<>();
229+
for (Delegator delegator : poolHistoryCurrentEpoch.getDelegators()) {
230+
double memberReward = PoolRewardCalculation.calculateMemberReward(poolReward, poolMargin,
231+
poolFixedCost, delegator.getActiveStake() / adaInCirculation, relativePoolStake);
232+
memberRewards.add(Reward.builder()
233+
.amount(memberReward)
234+
.stakeAddress(delegator.getStakeAddress())
235+
.build());
236+
}
237+
poolRewardCalculationResult.setMemberRewards(memberRewards);
238+
return poolRewardCalculationResult;
239+
}
106240
}

src/main/java/org/cardanofoundation/rewards/data/fetcher/KoiosDataFetcher.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@
55
import org.cardanofoundation.rewards.entity.*;
66
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
8+
import rest.koios.client.backend.api.base.exception.ApiException;
9+
import rest.koios.client.backend.api.pool.model.PoolDelegatorHistory;
10+
import rest.koios.client.backend.factory.options.Options;
811

912
import java.io.File;
1013
import java.io.IOException;
14+
import java.util.ArrayList;
1115
import java.util.List;
1216

1317
import static org.cardanofoundation.rewards.enums.DataType.*;
@@ -206,10 +210,10 @@ private void fetchPoolHistoryByEpoch(String poolId, int epoch, boolean override)
206210

207211
@Override
208212
public void fetch(int epoch, boolean override) {
209-
fetchAdaPots(epoch, override);
213+
/*fetchAdaPots(epoch, override);
210214
fetchEpochInfo(epoch, override);
211215
fetchProtocolParameters(epoch, override);
212-
fetchAccountUpdates(epoch, override);
216+
fetchAccountUpdates(epoch, override);*/
213217

214218
List<String> poolIds = List.of(
215219
"pool1xxhs2zw5xa4g54d5p62j46nlqzwp8jklqvuv2agjlapwjx9qkg9",
@@ -223,9 +227,9 @@ public void fetch(int epoch, boolean override) {
223227
);
224228

225229
for (String poolId : poolIds) {
226-
fetchPoolPledgeInEpoch(poolId, epoch, override);
230+
//fetchPoolPledgeInEpoch(poolId, epoch, override);
227231
fetchPoolHistoryByEpoch(poolId, epoch, override);
228-
fetchPoolOwnersStakeInEpoch(poolId, epoch, override);
232+
//fetchPoolOwnersStakeInEpoch(poolId, epoch, override);
229233
}
230234
}
231235
}

src/main/java/org/cardanofoundation/rewards/data/provider/DataProvider.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ public interface DataProvider {
2121
public List<PoolDeregistration> getRetiredPoolsInEpoch(int epoch);
2222

2323
public List<AccountUpdate> getAccountUpdatesUntilEpoch(List<String> stakeAddresses, int epoch);
24+
2425
}

src/main/java/org/cardanofoundation/rewards/data/provider/KoiosDataProvider.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import rest.koios.client.backend.api.epoch.model.EpochInfo;
1414
import rest.koios.client.backend.api.epoch.model.EpochParams;
1515
import rest.koios.client.backend.api.network.model.Totals;
16+
import rest.koios.client.backend.api.pool.model.PoolDelegatorHistory;
1617
import rest.koios.client.backend.api.pool.model.PoolUpdate;
1718
import rest.koios.client.backend.factory.BackendFactory;
1819
import rest.koios.client.backend.factory.BackendService;
@@ -103,7 +104,13 @@ public PoolHistory getPoolHistory(String poolId, int epoch) {
103104
e.printStackTrace();
104105
}
105106

106-
return PoolHistoryMapper.fromKoiosPoolHistory(poolHistory);
107+
PoolHistory history = PoolHistoryMapper.fromKoiosPoolHistory(poolHistory);
108+
109+
if (history == null) return null;
110+
111+
List<Delegator> poolMemberInEpoch = getPoolMemberInEpoch(poolId, epoch);
112+
history.setDelegators(poolMemberInEpoch);
113+
return history;
107114
}
108115

109116
@Override
@@ -217,4 +224,21 @@ public List<org.cardanofoundation.rewards.entity.AccountUpdate> getAccountUpdate
217224

218225
return accountUpdates;
219226
}
227+
228+
private List<Delegator> getPoolMemberInEpoch(String poolId, int epoch) {
229+
List<Delegator> delegators = new ArrayList<>();
230+
try {
231+
List<PoolDelegatorHistory> poolDelegatorsHistory = koiosBackendService
232+
.getPoolService().getPoolDelegatorsHistory(poolId, epoch, Options.EMPTY).getValue();
233+
for (PoolDelegatorHistory poolDelegator : poolDelegatorsHistory) {
234+
delegators.add(Delegator.builder()
235+
.activeStake(Double.valueOf(poolDelegator.getAmount()))
236+
.stakeAddress(poolDelegator.getStakeAddress())
237+
.build());
238+
}
239+
} catch (ApiException e) {
240+
e.printStackTrace();
241+
}
242+
return delegators;
243+
}
220244
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.cardanofoundation.rewards.entity;
2+
3+
import lombok.*;
4+
5+
@Getter
6+
@Setter
7+
@Builder
8+
@NoArgsConstructor
9+
@AllArgsConstructor
10+
public class Delegator {
11+
String stakeAddress;
12+
Double activeStake;
13+
}

src/main/java/org/cardanofoundation/rewards/entity/PoolCalculationResult.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

src/main/java/org/cardanofoundation/rewards/entity/PoolHistory.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import lombok.*;
44

5+
import java.util.List;
6+
57
@Getter
68
@Setter
79
@Builder
@@ -13,6 +15,7 @@ public class PoolHistory {
1315
private Double poolFees;
1416
private Double margin;
1517
private Double fixedCost;
18+
private List<Delegator> delegators;
1619
private int blockCount;
1720
private int epoch;
1821
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package org.cardanofoundation.rewards.entity;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
import lombok.Setter;
6+
7+
import java.util.List;
8+
9+
@Builder
10+
@Getter
11+
@Setter
12+
public class PoolRewardCalculationResult {
13+
int epoch;
14+
String poolId;
15+
Double stakePoolRewardsPot;
16+
List<Reward> memberRewards;
17+
Double operatorReward;
18+
Double optimalPoolReward;
19+
Double poolReward;
20+
Double apparentPoolPerformance;
21+
Double poolFee;
22+
Double poolMargin;
23+
Double poolCost;
24+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package org.cardanofoundation.rewards.entity;
2+
3+
import lombok.*;
4+
5+
@Getter
6+
@Setter
7+
@Builder
8+
@NoArgsConstructor
9+
@AllArgsConstructor
10+
public class Reward {
11+
private double amount;
12+
private String stakeAddress;
13+
}

0 commit comments

Comments
 (0)