Skip to content

Commit

Permalink
feat: claimable protocol commission (backport #46) (#72)
Browse files Browse the repository at this point in the history
Co-authored-by: Troy Kessler <troy@kyve.network>
  • Loading branch information
mergify[bot] and troykessler authored May 30, 2023
1 parent 68eb43f commit b553d9d
Show file tree
Hide file tree
Showing 24 changed files with 1,351 additions and 162 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,19 @@

## [Unreleased]

### Improvements

- (`x/stakers`) [#46](https://github.com/KYVENetwork/chain/pull/46) Allow protocol validator commission rewards to be claimed.

### Client Breaking

- (`x/stakers`) [#46](https://github.com/KYVENetwork/chain/pull/46) Include `MsgClaimCommissionRewards` for claiming commission rewards.

### API Breaking

<!-- TODO: Switch this link to the release tag. -->
- (`x/stakers`) [#46](https://github.com/KYVENetwork/chain/pull/46) Emit an [event](https://github.com/KYVENetwork/chain/blob/main/x/stakers/spec/05_events.md#eventclaimcommissionrewards) when claiming protocol validator commission rewards.

## [v1.2.1](https://github.com/KYVENetwork/chain/releases/tag/v1.2.1) - 2023-05-25

### Bug Fixes
Expand Down
9 changes: 9 additions & 0 deletions proto/kyve/stakers/v1beta1/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ message EventUpdateCommission {
];
}

// EventClaimCommissionRewards ...
// emitted_by: MsgClaimCommissionRewards
message EventClaimCommissionRewards {
// staker is the account address of the protocol node.
string staker = 1;
// amount ...
uint64 amount = 2;
}

// EventJoinPool ...
// emitted_by: MsgJoinPool
message EventJoinPool {
Expand Down
2 changes: 2 additions & 0 deletions proto/kyve/stakers/v1beta1/stakers.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ message Staker {
string security_contact = 6;
// details are some additional notes the staker finds important
string details = 7;
// commission_rewards are the rewards in $KYVE earned through commission
uint64 commission_rewards = 8;
}

// Valaccount gets authorized by a staker to
Expand Down
13 changes: 13 additions & 0 deletions proto/kyve/stakers/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ service Msg {
rpc UpdateMetadata(MsgUpdateMetadata) returns (MsgUpdateMetadataResponse);
// UpdateCommission ...
rpc UpdateCommission(MsgUpdateCommission) returns (MsgUpdateCommissionResponse);
// ClaimCommissionRewards ...
rpc ClaimCommissionRewards(MsgClaimCommissionRewards) returns (MsgClaimCommissionRewardsResponse);
// JoinPool ...
rpc JoinPool(MsgJoinPool) returns (MsgJoinPoolResponse);
// LeavePool ...
Expand Down Expand Up @@ -75,6 +77,17 @@ message MsgUpdateCommission {
// MsgUpdateCommissionResponse ...
message MsgUpdateCommissionResponse {}

// MsgClaimCommissionRewards ...
message MsgClaimCommissionRewards {
// creator ...
string creator = 1;
// amount ...
uint64 amount = 2;
}

// MsgClaimCommissionRewardsResponse ...
message MsgClaimCommissionRewardsResponse {}

// MsgJoinPool ...
message MsgJoinPool {
// creator ...
Expand Down
24 changes: 13 additions & 11 deletions testutil/integration/checks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/KYVENetwork/chain/x/pool"
querytypes "github.com/KYVENetwork/chain/x/query/types"
"github.com/KYVENetwork/chain/x/stakers"
stakertypes "github.com/KYVENetwork/chain/x/stakers/types"
"github.com/KYVENetwork/chain/x/team"
"github.com/cosmos/cosmos-sdk/types/query"
. "github.com/onsi/gomega"
Expand Down Expand Up @@ -56,6 +57,7 @@ func (suite *KeeperTestSuite) VerifyPoolModuleAssetsIntegrity() {
actualBalance := uint64(0)

for _, pool := range suite.App().PoolKeeper.GetAllPools(suite.Ctx()) {
// pool funds should be in pool module
for _, funder := range pool.Funders {
expectedBalance += funder.Amount
}
Expand Down Expand Up @@ -166,17 +168,17 @@ func (suite *KeeperTestSuite) VerifyPoolGenesisImportExport() {
// =====================

func (suite *KeeperTestSuite) VerifyStakersModuleAssetsIntegrity() {
//expectedBalance := uint64(0)
//actualBalance := uint64(0)
//
//for _, staker := range suite.App().StakersKeeper.GetAllStakers(suite.Ctx()) {
// expectedBalance += suite.App().DelegationKeeper
//}
//
//moduleAcc := suite.App().AccountKeeper.GetModuleAccount(suite.Ctx(), stakerstypes.ModuleName).GetAddress()
//actualBalance = suite.App().BankKeeper.GetBalance(suite.Ctx(), moduleAcc, globalTypes.Denom).Amount.Uint64()
//
//Expect(actualBalance).To(Equal(expectedBalance))
expectedBalance := uint64(0)
actualBalance := uint64(0)

for _, staker := range suite.App().StakersKeeper.GetAllStakers(suite.Ctx()) {
expectedBalance += staker.CommissionRewards
}

moduleAcc := suite.App().AccountKeeper.GetModuleAccount(suite.Ctx(), stakertypes.ModuleName).GetAddress()
actualBalance = suite.App().BankKeeper.GetBalance(suite.Ctx(), moduleAcc, globalTypes.Denom).Amount.Uint64()

Expect(actualBalance).To(Equal(expectedBalance))
}

func (suite *KeeperTestSuite) VerifyPoolTotalStake() {
Expand Down
4 changes: 3 additions & 1 deletion x/bundles/keeper/keeper_suite_stakers_leave_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,9 @@ var _ = Describe("stakers leave", Ordered, func() {
uploaderDelegationReward := totalUploaderReward - uploaderPayoutReward

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))
})
Expand Down
47 changes: 24 additions & 23 deletions x/bundles/keeper/keeper_suite_valid_bundles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,9 @@ var _ = Describe("valid bundles", Ordered, func() {
uploaderDelegationReward := totalUploaderReward - uploaderPayoutReward

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))

Expand Down Expand Up @@ -307,7 +309,9 @@ var _ = Describe("valid bundles", Ordered, func() {
delegatorDelegationReward := uint64(sdk.NewDec(int64(totalDelegationReward)).Quo(sdk.NewDec(4)).Mul(sdk.NewDec(3)).TruncateInt64())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))
// assert delegator delegation rewards
Expand Down Expand Up @@ -451,7 +455,9 @@ var _ = Describe("valid bundles", Ordered, func() {
uploaderDelegationReward := totalUploaderReward - uploaderPayoutReward

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))

Expand Down Expand Up @@ -609,7 +615,9 @@ var _ = Describe("valid bundles", Ordered, func() {
delegatorDelegationReward := uint64(sdk.NewDec(int64(totalDelegationReward)).Quo(sdk.NewDec(3)).Mul(sdk.NewDec(2)).TruncateInt64())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))
// assert delegator delegation rewards
Expand All @@ -619,7 +627,9 @@ var _ = Describe("valid bundles", Ordered, func() {
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_1, i.BOB)).To(BeZero())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))

Expand Down Expand Up @@ -768,7 +778,9 @@ var _ = Describe("valid bundles", Ordered, func() {
delegatorDelegationReward := uint64(sdk.NewDec(int64(totalDelegationReward)).Quo(sdk.NewDec(4)).Mul(sdk.NewDec(3)).TruncateInt64())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))
// assert delegator delegation rewards
Expand All @@ -777,11 +789,6 @@ var _ = Describe("valid bundles", Ordered, func() {
// check voter rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_1, i.BOB)).To(BeZero())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))

// check pool funds
pool, _ = s.App().PoolKeeper.GetPool(s.Ctx(), 0)

Expand Down Expand Up @@ -935,7 +942,9 @@ var _ = Describe("valid bundles", Ordered, func() {
delegatorDelegationReward := uint64(sdk.NewDec(int64(totalDelegationReward)).Quo(sdk.NewDec(4)).Mul(sdk.NewDec(3)).TruncateInt64())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))
// assert delegator delegation rewards
Expand All @@ -944,11 +953,6 @@ var _ = Describe("valid bundles", Ordered, func() {
// check voter rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_1, i.BOB)).To(BeZero())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))

// check pool funds
pool, _ = s.App().PoolKeeper.GetPool(s.Ctx(), 0)

Expand Down Expand Up @@ -1112,7 +1116,9 @@ var _ = Describe("valid bundles", Ordered, func() {
delegatorDelegationReward := uint64(sdk.NewDec(int64(totalDelegationReward)).Quo(sdk.NewDec(4)).Mul(sdk.NewDec(3)).TruncateInt64())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))
// assert delegator delegation rewards
Expand All @@ -1121,11 +1127,6 @@ var _ = Describe("valid bundles", Ordered, func() {
// check voter rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_1, i.BOB)).To(BeZero())

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + uploaderPayoutReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(Equal(uploaderDelegationReward))

// check pool funds
pool, _ = s.App().PoolKeeper.GetPool(s.Ctx(), 0)

Expand Down
6 changes: 5 additions & 1 deletion x/bundles/keeper/keeper_suite_zero_delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -390,8 +390,12 @@ var _ = Describe("valid bundles", Ordered, func() {
treasuryReward := uint64(sdk.NewDec(int64(totalReward)).Mul(networkFee).TruncateInt64())
totalUploaderReward := totalReward - treasuryReward

uploader, _ := s.App().StakersKeeper.GetStaker(s.Ctx(), i.STAKER_0)

// assert payout transfer
Expect(balanceUploader).To(Equal(initialBalanceStaker0 + totalUploaderReward))
Expect(balanceUploader).To(Equal(initialBalanceStaker0))
// assert commission rewards
Expect(uploader.CommissionRewards).To(Equal(totalUploaderReward))
// assert uploader self delegation rewards
Expect(s.App().DelegationKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0)).To(BeZero())

Expand Down
23 changes: 15 additions & 8 deletions x/bundles/keeper/msg_server_submit_bundle_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ package keeper
import (
"context"

delegationTypes "github.com/KYVENetwork/chain/x/delegation/types"

"github.com/KYVENetwork/chain/util"
"github.com/KYVENetwork/chain/x/bundles/types"
pooltypes "github.com/KYVENetwork/chain/x/pool/types"
sdk "github.com/cosmos/cosmos-sdk/types"

// Delegation
delegationTypes "github.com/KYVENetwork/chain/x/delegation/types"
// Pool
poolTypes "github.com/KYVENetwork/chain/x/pool/types"
// Stakers
stakersTypes "github.com/KYVENetwork/chain/x/stakers/types"
)

// SubmitBundleProposal handles the logic of an SDK message that allows protocol nodes to submit a new bundle proposal.
Expand Down Expand Up @@ -73,7 +77,7 @@ func (k msgServer) SubmitBundleProposal(
k.SetBundleProposal(ctx, bundleProposal)

// emit event which indicates that pool has run out of funds
_ = ctx.EventManager().EmitTypedEvent(&pooltypes.EventPoolOutOfFunds{
_ = ctx.EventManager().EmitTypedEvent(&poolTypes.EventPoolOutOfFunds{
PoolId: msg.PoolId,
})

Expand All @@ -85,19 +89,22 @@ func (k msgServer) SubmitBundleProposal(

uploaderPayout := bundleReward.Uploader

delegationPayoutSuccessful := k.delegationKeeper.PayoutRewards(ctx, bundleProposal.Uploader, bundleReward.Delegation, pooltypes.ModuleName)
delegationPayoutSuccessful := k.delegationKeeper.PayoutRewards(ctx, bundleProposal.Uploader, bundleReward.Delegation, poolTypes.ModuleName)
// If staker has no delegators add all delegation rewards to the staker rewards
if !delegationPayoutSuccessful {
uploaderPayout += bundleReward.Delegation
}

// send commission to uploader
if err := util.TransferFromModuleToAddress(k.bankKeeper, ctx, pooltypes.ModuleName, bundleProposal.Uploader, uploaderPayout); err != nil {
// transfer funds from pool to stakers module
if err := util.TransferFromModuleToModule(k.bankKeeper, ctx, poolTypes.ModuleName, stakersTypes.ModuleName, uploaderPayout); err != nil {
return nil, err
}

// increase commission rewards of uploader
k.stakerKeeper.IncreaseStakerCommissionRewards(ctx, bundleProposal.Uploader, uploaderPayout)

// send network fee to treasury
if err := util.TransferFromModuleToTreasury(k.accountKeeper, k.distrkeeper, ctx, pooltypes.ModuleName, bundleReward.Treasury); err != nil {
if err := util.TransferFromModuleToTreasury(k.accountKeeper, k.distrkeeper, ctx, poolTypes.ModuleName, bundleReward.Treasury); err != nil {
return nil, err
}

Expand Down
1 change: 1 addition & 0 deletions x/bundles/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type PoolKeeper interface {
type StakerKeeper interface {
GetAllStakerAddressesOfPool(ctx sdk.Context, poolId uint64) (stakers []string)
GetCommission(ctx sdk.Context, stakerAddress string) sdk.Dec
IncreaseStakerCommissionRewards(ctx sdk.Context, address string, amount uint64)
AssertValaccountAuthorized(ctx sdk.Context, poolId uint64, stakerAddress string, valaddress string) error

DoesStakerExist(ctx sdk.Context, staker string) bool
Expand Down
1 change: 1 addition & 0 deletions x/stakers/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ func GetTxCmd() *cobra.Command {
cmd.AddCommand(CmdJoinPool())
cmd.AddCommand(CmdLeavePool())
cmd.AddCommand(CmdUpdateCommission())
cmd.AddCommand(CmdClaimCommissionRewards())
cmd.AddCommand(CmdUpdateMetadata())

return cmd
Expand Down
44 changes: 44 additions & 0 deletions x/stakers/client/cli/tx_claim_commission_rewards.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package cli

import (
"github.com/KYVENetwork/chain/x/stakers/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/spf13/cast"
"github.com/spf13/cobra"
)

func CmdClaimCommissionRewards() *cobra.Command {
cmd := &cobra.Command{
Use: "claim-commission-rewards [amount]",
Short: "Broadcast message claim-commission-rewards",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

argAmount, err := cast.ToUint64E(args[0])
if err != nil {
return err
}

msg := types.MsgClaimCommissionRewards{
Creator: clientCtx.GetFromAddress().String(),
Amount: argAmount,
}

if err := msg.ValidateBasic(); err != nil {
return err
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
9 changes: 9 additions & 0 deletions x/stakers/keeper/getters_staker.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ func (k Keeper) UpdateStakerCommission(ctx sdk.Context, address string, commissi
}
}

// IncreaseStakerCommissionRewards ...
func (k Keeper) IncreaseStakerCommissionRewards(ctx sdk.Context, address string, amount uint64) {
staker, found := k.GetStaker(ctx, address)
if found {
staker.CommissionRewards += amount
k.setStaker(ctx, staker)
}
}

// AddValaccountToPool adds a valaccount to a pool.
// If valaccount already belongs to pool, nothing happens.
func (k Keeper) AddValaccountToPool(ctx sdk.Context, poolId uint64, stakerAddress string, valaddress string) {
Expand Down
Loading

0 comments on commit b553d9d

Please sign in to comment.