code.vegaprotocol.io/vega@v0.79.0/core/rewards/contribution_reward_calculator.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package rewards 17 18 import ( 19 "time" 20 21 "code.vegaprotocol.io/vega/core/types" 22 "code.vegaprotocol.io/vega/libs/num" 23 "code.vegaprotocol.io/vega/protos/vega" 24 ) 25 26 func calculateRewardsByScores(rewardBalance num.Decimal, partyScores []*types.PartyContributionScore, takerFeeContributionInRewardToken map[string]*num.Uint, cap num.Decimal) (map[string]*num.Uint, *num.Uint) { 27 total := num.UintZero() 28 partyToAmount := map[string]*num.Uint{} 29 remainingRounds := 10 30 if cap.IsZero() { 31 remainingRounds = 1 32 } 33 for { 34 totalPerRound := num.UintZero() 35 for _, p := range partyScores { 36 currentPartyReward, ok := partyToAmount[p.Party] 37 if !ok { 38 currentPartyReward = num.UintZero() 39 } 40 partyRewardD := rewardBalance.Mul(p.Score) 41 if !cap.IsZero() { 42 partyTakeFeeContributionInRewardAsset, ok := takerFeeContributionInRewardToken[p.Party] 43 if ok { 44 partyRewardD = num.MinD(currentPartyReward.ToDecimal().Add(partyRewardD), cap.Mul(partyTakeFeeContributionInRewardAsset.ToDecimal())) 45 } else { 46 partyRewardD = num.DecimalZero() 47 } 48 } 49 partyReward, _ := num.UintFromDecimal(partyRewardD) 50 if !partyReward.IsZero() { 51 var partyRewardDelta *num.Uint 52 if _, ok := partyToAmount[p.Party]; !ok { 53 partyRewardDelta = partyReward 54 } else { 55 partyRewardDelta = num.UintZero().Sub(partyReward, partyToAmount[p.Party]) 56 } 57 partyToAmount[p.Party] = partyReward 58 totalPerRound.AddSum(partyRewardDelta) 59 } 60 } 61 rewardBalance = rewardBalance.Sub(totalPerRound.ToDecimal()) 62 remainingRounds -= 1 63 total.AddSum(totalPerRound) 64 if rewardBalance.LessThan(num.DecimalOne()) || totalPerRound.IsZero() || remainingRounds <= 0 { 65 break 66 } 67 } 68 return partyToAmount, total 69 } 70 71 // given party contribution scores, reward multipliers and distribution strategy calculate the payout per party. 72 func calculateRewardsByContributionIndividual(epochSeq, asset, accountID string, balance *num.Uint, partyContribution []*types.PartyContributionScore, rewardFactors map[string]num.Decimal, timestamp time.Time, ds *vega.DispatchStrategy, takerFeeContributionInRewardToken map[string]*num.Uint) *payout { 73 po := &payout{ 74 asset: asset, 75 fromAccount: accountID, 76 epochSeq: epochSeq, 77 timestamp: timestamp.Unix(), 78 partyToAmount: map[string]*num.Uint{}, 79 lockedForEpochs: ds.LockPeriod, 80 } 81 82 var partyScores []*types.PartyContributionScore 83 if ds.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA { 84 partyScores = proRataRewardCalculator(partyContribution, rewardFactors) 85 } else if ds.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK { 86 partyScores = rankingRewardCalculator(partyContribution, ds.RankTable, rewardFactors) 87 } else if ds.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK_LOTTERY { 88 partyScores = rankingLotteryRewardCalculator(partyContribution, ds.RankTable, rewardFactors, timestamp) 89 } 90 91 cap := num.DecimalZero() 92 if ds.CapRewardFeeMultiple != nil { 93 cap = num.MustDecimalFromString(*ds.CapRewardFeeMultiple) 94 } 95 po.partyToAmount, po.totalReward = calculateRewardsByScores(balance.ToDecimal(), partyScores, takerFeeContributionInRewardToken, cap) 96 if po.totalReward.IsZero() { 97 return nil 98 } 99 return po 100 } 101 102 // given party contribution scores, reward multipliers and distribution strategy calculate the payout per party in a team. 103 func calculateRewardsByContributionTeam(epochSeq, asset, accountID string, balance *num.Uint, teamContribution []*types.PartyContributionScore, teamPartyContribution map[string][]*types.PartyContributionScore, rewardFactors map[string]num.Decimal, timestamp time.Time, ds *vega.DispatchStrategy, takerFeeContributionInRewardToken map[string]*num.Uint) *payout { 104 po := &payout{ 105 asset: asset, 106 fromAccount: accountID, 107 epochSeq: epochSeq, 108 timestamp: timestamp.Unix(), 109 partyToAmount: map[string]*num.Uint{}, 110 lockedForEpochs: ds.LockPeriod, 111 } 112 var teamScores []*types.PartyContributionScore 113 if ds.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA { 114 teamScores = proRataRewardCalculator(teamContribution, map[string]num.Decimal{}) 115 } else if ds.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK { 116 teamScores = rankingRewardCalculator(teamContribution, ds.RankTable, map[string]num.Decimal{}) 117 } else if ds.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK_LOTTERY { 118 teamScores = rankingLotteryRewardCalculator(teamContribution, ds.RankTable, map[string]num.Decimal{}, timestamp) 119 } 120 121 partyScores := []*types.PartyContributionScore{} 122 totalScore := num.DecimalZero() 123 for _, teamScore := range teamScores { 124 partyScores = append(partyScores, calcPartyInTeamRewardShare(teamScore, teamPartyContribution[teamScore.Party], rewardFactors)...) 125 } 126 for _, pcs := range partyScores { 127 totalScore = totalScore.Add(pcs.Score) 128 } 129 130 capAtOne(partyScores, totalScore) 131 132 cap := num.DecimalZero() 133 if ds.CapRewardFeeMultiple != nil { 134 cap = num.MustDecimalFromString(*ds.CapRewardFeeMultiple) 135 } 136 po.partyToAmount, po.totalReward = calculateRewardsByScores(balance.ToDecimal(), partyScores, takerFeeContributionInRewardToken, cap) 137 138 if po.totalReward.IsZero() { 139 return nil 140 } 141 return po 142 } 143 144 func calcPartyInTeamRewardShare(teamScore *types.PartyContributionScore, partyToMetricScore []*types.PartyContributionScore, rewardFactors map[string]num.Decimal) []*types.PartyContributionScore { 145 ps := make([]*types.PartyContributionScore, 0, len(partyToMetricScore)) 146 147 totalScores := num.DecimalZero() 148 for _, pcs := range partyToMetricScore { 149 if pcs.Score.IsZero() { 150 continue 151 } 152 rewardFactor := num.DecimalOne() 153 if factor, ok := rewardFactors[pcs.Party]; ok { 154 rewardFactor = factor 155 } 156 ps = append(ps, &types.PartyContributionScore{Party: pcs.Party, Score: rewardFactor}) 157 totalScores = totalScores.Add(rewardFactor) 158 } 159 if totalScores.IsZero() { 160 return []*types.PartyContributionScore{} 161 } 162 163 for _, pcs := range ps { 164 pcs.Score = pcs.Score.Mul(teamScore.Score).Div(totalScores) 165 } 166 return ps 167 }