diff --git a/go.mod b/go.mod index 9215bf7..7cce271 100644 --- a/go.mod +++ b/go.mod @@ -251,11 +251,11 @@ replace ( // use cosmos fork of keyring github.com/99designs/keyring => github.com/cosmos/keyring v1.2.0 // use Settlus flavored Cosmos-SDK https://github.com/settlus/cosmos-sdk/releases - github.com/cosmos/cosmos-sdk => github.com/settlus/cosmos-sdk v0.47.12-settlus.2 + github.com/cosmos/cosmos-sdk => github.com/settlus/cosmos-sdk v0.47.12-settlus.3 // use Evmos geth fork github.com/ethereum/go-ethereum => github.com/evmos/go-ethereum v1.10.26-evmos-rc4 // use Settlus flavored Evmos - github.com/evmos/evmos/v19 => github.com/settlus/evmos/v19 v19.0.0-settlus.3.rc + github.com/evmos/evmos/v19 => github.com/settlus/evmos/v19 v19.0.0-settlus.3 // Security Advisory https://github.com/advisories/GHSA-h395-qcrw-5vmq github.com/gin-gonic/gin => github.com/gin-gonic/gin v1.9.1 // use cosmos flavored protobufs diff --git a/go.sum b/go.sum index 78f477f..0d392f1 100644 --- a/go.sum +++ b/go.sum @@ -1213,10 +1213,10 @@ github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KR github.com/segmentio/kafka-go v0.1.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/segmentio/kafka-go v0.2.0/go.mod h1:X6itGqS9L4jDletMsxZ7Dz+JFWxM6JHfPOCvTvk+EJo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/settlus/cosmos-sdk v0.47.12-settlus.2 h1:ho5ED+o0UI3ITiEckNdlqxgIxE++tYU2CgwfBanbttA= -github.com/settlus/cosmos-sdk v0.47.12-settlus.2/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= -github.com/settlus/evmos/v19 v19.0.0-settlus.3.rc h1:z9ctKKoVsTw18fhFLW+OTUqwA76gMK63f/1ECU8T0dY= -github.com/settlus/evmos/v19 v19.0.0-settlus.3.rc/go.mod h1:cH2IV7ZrZlp+2KfeExMvtlQe8sxOYt2cC4J3ZiiwJHU= +github.com/settlus/cosmos-sdk v0.47.12-settlus.3 h1:RINSLTQes1z/7xOVXeeQetW6Iojrw0GtgBSRS3diLjw= +github.com/settlus/cosmos-sdk v0.47.12-settlus.3/go.mod h1:ADjORYzUQqQv/FxDi0H0K5gW/rAk1CiDR3ZKsExfJV0= +github.com/settlus/evmos/v19 v19.0.0-settlus.3 h1:P5n6lCDofIf4c2Kg3uNdj0nYad+A0Or7AwrfXwfQizc= +github.com/settlus/evmos/v19 v19.0.0-settlus.3/go.mod h1:oxzKo9nicHfPnGDV+sItCvz6Hkv1QhTFtG1dplSGcSE= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= diff --git a/x/oracle/keeper/feeder.go b/x/oracle/keeper/feeder.go index a2702d0..a14274f 100644 --- a/x/oracle/keeper/feeder.go +++ b/x/oracle/keeper/feeder.go @@ -287,7 +287,7 @@ func (k Keeper) RewardBallotWinners(ctx sdk.Context, validatorClaimMap map[strin logger.Debug("RewardBallotWinner", "rewards", rewards) var distributedReward sdk.Coins - var probonoReward sdk.Coins + var totalContribution sdk.DecCoins for addr, voter := range validatorClaimMap { // skip if the validator abstained or missed the vote @@ -313,12 +313,15 @@ func (k Keeper) RewardBallotWinners(ctx sdk.Context, validatorClaimMap map[strin } if !rewardCoins.IsZero() { - if receiverVal.IsProbono() { - probonoReward = probonoReward.Add(rewardCoins...) - continue - } - k.DistributionKeeper.AllocateTokensToValidator(ctx, receiverVal, sdk.NewDecCoinsFromCoins(rewardCoins...)) - distributedReward = distributedReward.Add(rewardCoins...) + probonoRate := receiverVal.GetProbonoRate() + probonoContribution := sdk.NewDecCoinsFromCoins(rewardCoins...).MulDecTruncate(probonoRate) + totalContribution = totalContribution.Add(probonoContribution...) + + finalReward := sdk.NewDecCoinsFromCoins(rewardCoins...).Sub(probonoContribution) + finalRewardCoins, _ := finalReward.TruncateDecimal() + + k.DistributionKeeper.AllocateTokensToValidator(ctx, receiverVal, finalReward) + distributedReward = distributedReward.Add(finalRewardCoins...) } else { logger.Debug(fmt.Sprintf("no reward %s(%s)", receiverVal.GetMoniker(), @@ -329,11 +332,13 @@ func (k Keeper) RewardBallotWinners(ctx sdk.Context, validatorClaimMap map[strin } feePool := k.DistributionKeeper.GetFeePool(ctx) - feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(probonoReward...)...) + feePool.CommunityPool = feePool.CommunityPool.Add(totalContribution...) k.DistributionKeeper.SetFeePool(ctx, feePool) + + totalContributionCoins, _ := totalContribution.TruncateDecimal() - // Move both distributed reward and probono reward to distribution module - if err := k.BankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.distributionName, distributedReward.Add(probonoReward...)); err != nil { + // Move both distributed reward and contribution reward to distribution module + if err := k.BankKeeper.SendCoinsFromModuleToModule(ctx, types.ModuleName, k.distributionName, distributedReward.Add(totalContributionCoins...)); err != nil { return fmt.Errorf("failed to move distributed reward to distribution module: %w", err) } diff --git a/x/oracle/keeper/keeper_test.go b/x/oracle/keeper/keeper_test.go index 0fa18b0..7b43ae1 100644 --- a/x/oracle/keeper/keeper_test.go +++ b/x/oracle/keeper/keeper_test.go @@ -407,10 +407,10 @@ func (suite *OracleTestSuite) TestKeeper_RewardBallotWinners_WithProbono() { vcm map[string]types.Claim totalCoin sdk.Coins rewardMap map[string]sdk.DecCoins - probonoIndex []int + probonoMap map[int]sdk.Dec }{ { - name: "Probono validators send rewards to community pool, normal validators get rewards as usual", + name: "Probono validators send rewards to community pool with their probono rate, normal validators get rewards", vcm: map[string]types.Claim{ s.validators[0].GetOperator().String(): { Weight: 100, @@ -440,7 +440,10 @@ func (suite *OracleTestSuite) TestKeeper_RewardBallotWinners_WithProbono() { s.validators[2].GetOperator().String(): sdk.NewDecCoins(sdk.NewInt64DecCoin("asetl", 1000000)), s.validators[3].GetOperator().String(): sdk.NewDecCoins(sdk.NewInt64DecCoin("asetl", 1000000)), }, - probonoIndex: []int{0, 1}, + probonoMap: map[int]sdk.Dec{ + 0: sdk.NewDecFromIntWithPrec(sdk.NewInt(2), 1), // 20% probono + 1: sdk.NewDecFromIntWithPrec(sdk.NewInt(1), 0), // 100% probono + }, }, { name: "All probono validator, there's no normal validator", @@ -473,7 +476,12 @@ func (suite *OracleTestSuite) TestKeeper_RewardBallotWinners_WithProbono() { s.validators[2].GetOperator().String(): sdk.NewDecCoins(sdk.NewInt64DecCoin("asetl", 1000000)), s.validators[3].GetOperator().String(): sdk.NewDecCoins(sdk.NewInt64DecCoin("asetl", 1000000)), }, - probonoIndex: []int{0, 1, 2, 3}, + probonoMap: map[int]sdk.Dec{ + 0: sdk.NewDecFromIntWithPrec(sdk.NewInt(1), 0), // 100% probono + 1: sdk.NewDecFromIntWithPrec(sdk.NewInt(1), 0), // 100% probono + 2: sdk.NewDecFromIntWithPrec(sdk.NewInt(1), 0), // 100% probono + 3: sdk.NewDecFromIntWithPrec(sdk.NewInt(1), 0), // 100% probono + }, }, } @@ -488,30 +496,35 @@ func (suite *OracleTestSuite) TestKeeper_RewardBallotWinners_WithProbono() { s.app.DistrKeeper.SetFeePool(s.ctx, disttypes.InitialFeePool()) s.Equal(s.app.DistrKeeper.GetFeePoolCommunityCoins(s.ctx).AmountOf("asetl"), sdk.ZeroDec()) - for _, idx := range tt.probonoIndex { + for idx, rate := range tt.probonoMap { s.validators[idx].Probono = true + s.validators[idx].Commission = stakingtypes.NewCommission( + rate, + sdk.NewDecWithPrec(1, 0), + sdk.NewDecWithPrec(0, 0), + ) s.validators[idx] = stakingkeeper.TestingUpdateValidator(s.app.StakingKeeper, s.ctx, s.validators[idx], true) } - probonoRewards := sdk.ZeroDec() + var probonoRewards sdk.DecCoins err = s.app.OracleKeeper.RewardBallotWinners(s.ctx, tt.vcm) s.NoError(err) for _, validator := range s.validators { - if validator.IsProbono() { - probonoRewards = probonoRewards.Add(tt.rewardMap[validator.GetOperator().String()].AmountOf("asetl")) - validator.Probono = false - continue - } + contribution := tt.rewardMap[validator.GetOperator().String()].MulDec(validator.GetProbonoRate()) + + probonoRewards = probonoRewards.Add(contribution...) + rewards := s.app.DistrKeeper.GetValidatorCurrentRewards(s.ctx, validator.GetOperator()) - s.Equal(tt.rewardMap[validator.GetOperator().String()].AmountOf("asetl"), rewards.Rewards.AmountOf("asetl")) + s.Equal(tt.rewardMap[validator.GetOperator().String()].Sub(contribution).AmountOf("asetl"), rewards.Rewards.AmountOf("asetl")) + s.app.DistrKeeper.DeleteValidatorCurrentRewards(s.ctx, validator.GetOperator()) } // check community pool actualCommunityAmount := s.app.DistrKeeper.GetFeePoolCommunityCoins(s.ctx).AmountOf("asetl") - s.Equal(probonoRewards, actualCommunityAmount) + s.Equal(probonoRewards.AmountOf("asetl"), actualCommunityAmount) s.app.DistrKeeper.SetFeePool(s.ctx, disttypes.InitialFeePool()) }) }