From 7eed7a4cdac2693d6a275813e1f9846c32afe188 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 14 Dec 2022 21:44:54 -0500 Subject: [PATCH] In case we have capped burn shares we redistribute the share from the over-burned amount to the non capped candidates. This helps to avoid that the legacy BM would get the rest in case there are capped shares. It still can be that a candidate exceeds the cap and by the adjustment becomes capped. We take that into account and the legacy BM would get some share in that case. Signed-off-by: HenrikJannsen --- .../dao/burningman/BurningManService.java | 11 +++++ .../burningman/model/BurningManCandidate.java | 45 +++++++++++++++++-- .../burningman/BurningManListItem.java | 12 ++--- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/burningman/BurningManService.java b/core/src/main/java/bisq/core/dao/burningman/BurningManService.java index b2247798a2..43989b21ea 100644 --- a/core/src/main/java/bisq/core/dao/burningman/BurningManService.java +++ b/core/src/main/java/bisq/core/dao/burningman/BurningManService.java @@ -189,6 +189,17 @@ public class BurningManService { .mapToDouble(BurningManCandidate::getAccumulatedDecayedBurnAmount) .sum(); burningManCandidates.forEach(candidate -> candidate.calculateShares(totalDecayedCompensationAmounts, totalDecayedBurnAmounts)); + + double sumAllCappedBurnAmountShares = burningManCandidates.stream() + .filter(candidate -> candidate.getBurnAmountShare() >= candidate.getMaxBoostedCompensationShare()) + .mapToDouble(BurningManCandidate::getMaxBoostedCompensationShare) + .sum(); + double sumAllNonCappedBurnAmountShares = burningManCandidates.stream() + .filter(candidate -> candidate.getBurnAmountShare() < candidate.getMaxBoostedCompensationShare()) + .mapToDouble(BurningManCandidate::getBurnAmountShare) + .sum(); + burningManCandidates.forEach(candidate -> candidate.calculateCappedBurnAmountShare(sumAllCappedBurnAmountShares, sumAllNonCappedBurnAmountShares)); + return burningManCandidatesByName; } diff --git a/core/src/main/java/bisq/core/dao/burningman/model/BurningManCandidate.java b/core/src/main/java/bisq/core/dao/burningman/model/BurningManCandidate.java index 0f6152e70a..39746fa66a 100644 --- a/core/src/main/java/bisq/core/dao/burningman/model/BurningManCandidate.java +++ b/core/src/main/java/bisq/core/dao/burningman/model/BurningManCandidate.java @@ -52,8 +52,13 @@ public class BurningManCandidate { private final Map> burnOutputModelsByMonth = new HashMap<>(); private long accumulatedBurnAmount; private long accumulatedDecayedBurnAmount; - protected double burnAmountShare; // Share of accumulated decayed burn amounts in relation to total burned amounts - protected double cappedBurnAmountShare; // Capped burnAmountShare. Cannot be larger than boostedCompensationShare + // Share of accumulated decayed burn amounts in relation to total burned amounts + protected double burnAmountShare; + // Capped burnAmountShare. Cannot be larger than boostedCompensationShare + protected double cappedBurnAmountShare; + // The burnAmountShare adjusted in case there are cappedBurnAmountShare. + // We redistribute the over-burned amounts to the group of not capped candidates. + private double adjustedBurnAmountShare; public BurningManCandidate() { } @@ -93,11 +98,42 @@ public class BurningManCandidate { public void calculateShares(double totalDecayedCompensationAmounts, double totalDecayedBurnAmounts) { compensationShare = totalDecayedCompensationAmounts > 0 ? accumulatedDecayedCompensationAmount / totalDecayedCompensationAmounts : 0; - burnAmountShare = totalDecayedBurnAmounts > 0 ? accumulatedDecayedBurnAmount / totalDecayedBurnAmounts : 0; - cappedBurnAmountShare = Math.min(getMaxBoostedCompensationShare(), burnAmountShare); } + public void calculateCappedBurnAmountShare(double sumAllCappedBurnAmountShares, + double sumAllNonCappedBurnAmountShares) { + double maxBoostedCompensationShare = getMaxBoostedCompensationShare(); + adjustedBurnAmountShare = burnAmountShare; + if (burnAmountShare < maxBoostedCompensationShare) { + if (sumAllCappedBurnAmountShares == 0) { + // If no one is capped we do not need to do any adjustment + cappedBurnAmountShare = burnAmountShare; + } else { + // The difference of the cappedBurnAmountShare and burnAmountShare will get redistributed to all + // non-capped candidates. + double distributionBase = 1 - sumAllCappedBurnAmountShares; + if (sumAllNonCappedBurnAmountShares == 0) { + // In case we get sumAllNonCappedBurnAmountShares our burnAmountShare is also 0. + cappedBurnAmountShare = burnAmountShare; + } else { + double adjustment = distributionBase / sumAllNonCappedBurnAmountShares; + adjustedBurnAmountShare = burnAmountShare * adjustment; + if (adjustedBurnAmountShare < maxBoostedCompensationShare) { + cappedBurnAmountShare = adjustedBurnAmountShare; + } else { + // We exceeded the cap by the adjustment. This will lead to the legacy BM getting the + // difference of the adjusted amount and the maxBoostedCompensationShare. + cappedBurnAmountShare = maxBoostedCompensationShare; + } + } + } + } else { + cappedBurnAmountShare = maxBoostedCompensationShare; + } + } + + public double getMaxBoostedCompensationShare() { return Math.min(BurningManService.MAX_BURN_SHARE, compensationShare * BurningManService.ISSUANCE_BOOST_FACTOR); } @@ -115,6 +151,7 @@ public class BurningManCandidate { ",\r\n accumulatedDecayedBurnAmount=" + accumulatedDecayedBurnAmount + ",\r\n burnAmountShare=" + burnAmountShare + ",\r\n cappedBurnAmountShare=" + cappedBurnAmountShare + + ",\r\n adjustedBurnAmountShare=" + adjustedBurnAmountShare + "\r\n}"; } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/burningman/BurningManListItem.java b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/burningman/BurningManListItem.java index aad6dd6d5c..ada8528c6e 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/burningman/BurningManListItem.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/burnbsq/burningman/BurningManListItem.java @@ -39,7 +39,7 @@ class BurningManListItem { private final long burnTarget, maxBurnTarget, accumulatedDecayedBurnAmount, accumulatedBurnAmount, accumulatedDecayedCompensationAmount, accumulatedCompensationAmount, expectedRevenue; private final int numBurnOutputs, numIssuances; - private final double cappedBurnAmountShare, burnAmountShare, compensationShare; + private final double cappedBurnAmountShare, adjustedBurnAmountShare, compensationShare; BurningManListItem(BurningManPresentationService burningManPresentationService, String name, @@ -60,14 +60,14 @@ class BurningManListItem { accumulatedDecayedBurnAmount = 0; accumulatedDecayedBurnAmountAsBsq = ""; - burnAmountShare = burningManCandidate.getBurnAmountShare(); + adjustedBurnAmountShare = burningManCandidate.getAdjustedBurnAmountShare(); cappedBurnAmountShare = burningManCandidate.getCappedBurnAmountShare(); // LegacyBurningManForDPT is the one defined by DAO voting, so only that would receive BTC if new BM do not cover 100%. if (burningManPresentationService.getLegacyBurningManForDPT().equals(burningManCandidate)) { expectedRevenue = burningManPresentationService.getExpectedRevenue(burningManCandidate); - cappedBurnAmountShareAsString = FormattingUtils.formatToPercentWithSymbol(burnAmountShare); + cappedBurnAmountShareAsString = FormattingUtils.formatToPercentWithSymbol(adjustedBurnAmountShare); } else { expectedRevenue = 0; cappedBurnAmountShareAsString = FormattingUtils.formatToPercentWithSymbol(0); @@ -94,12 +94,12 @@ class BurningManListItem { accumulatedBurnAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedBurnAmount); accumulatedDecayedBurnAmount = burningManCandidate.getAccumulatedDecayedBurnAmount(); accumulatedDecayedBurnAmountAsBsq = bsqFormatter.formatCoinWithCode(accumulatedDecayedBurnAmount); - burnAmountShare = burningManCandidate.getBurnAmountShare(); + adjustedBurnAmountShare = burningManCandidate.getAdjustedBurnAmountShare(); cappedBurnAmountShare = burningManCandidate.getCappedBurnAmountShare(); - if (burnAmountShare != cappedBurnAmountShare) { + if (adjustedBurnAmountShare != cappedBurnAmountShare) { cappedBurnAmountShareAsString = Res.get("dao.burningman.table.burnAmountShare.capped", FormattingUtils.formatToPercentWithSymbol(cappedBurnAmountShare), - FormattingUtils.formatToPercentWithSymbol(burnAmountShare)); + FormattingUtils.formatToPercentWithSymbol(adjustedBurnAmountShare)); } else { cappedBurnAmountShareAsString = FormattingUtils.formatToPercentWithSymbol(cappedBurnAmountShare); }