Skip to content

Commit

Permalink
feat: support multiple coins as delegation rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
mbreithecker committed Apr 26, 2024
1 parent f6ace3e commit 33238d2
Show file tree
Hide file tree
Showing 20 changed files with 651 additions and 367 deletions.
86 changes: 72 additions & 14 deletions docs/static/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,22 @@ paths:
format: uint64
title: protocol_delegation_unbonding
protocol_rewards:
type: string
format: uint64
type: array
items:
type: object
properties:
denom:
type: string
amount:
type: string
description: >-
Coin defines a token with a denomination and an amount.
NOTE: The amount field is an Int which implements the custom
method
signatures required by gogoproto.
description: protocol_rewards ...
protocol_funding:
type: string
Expand Down Expand Up @@ -3173,10 +3187,24 @@ paths:
delegator:
type: string
description: delegator ...
current_reward:
type: string
format: uint64
description: current_reward ...
current_rewards:
type: array
items:
type: object
properties:
denom:
type: string
amount:
type: string
description: >-
Coin defines a token with a denomination and an amount.
NOTE: The amount field is an Int which implements the
custom method
signatures required by gogoproto.
description: current_rewards ...
delegation_amount:
type: string
format: uint64
Expand Down Expand Up @@ -3413,10 +3441,25 @@ paths:
delegator:
type: string
description: delegator ...
current_reward:
type: string
format: uint64
description: current_reward ...
current_rewards:
type: array
items:
type: object
properties:
denom:
type: string
amount:
type: string
description: >-
Coin defines a token with a denomination and an
amount.
NOTE: The amount field is an Int which implements the
custom method
signatures required by gogoproto.
description: current_rewards ...
delegation_amount:
type: string
format: uint64
Expand Down Expand Up @@ -3966,10 +4009,25 @@ paths:
It contains almost all needed information for a
convenient usage
current_reward:
type: string
format: uint64
description: current_reward ...
current_rewards:
type: array
items:
type: object
properties:
denom:
type: string
amount:
type: string
description: >-
Coin defines a token with a denomination and an
amount.
NOTE: The amount field is an Int which implements the
custom method
signatures required by gogoproto.
description: current_rewards ...
delegation_amount:
type: string
format: uint64
Expand Down
15 changes: 11 additions & 4 deletions proto/kyve/delegation/v1beta1/delegation.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ syntax = "proto3";

package kyve.delegation.v1beta1;

import "cosmos/base/v1beta1/coin.proto";
import "gogoproto/gogo.proto";
import "amino/amino.proto";

option go_package = "github.com/KYVENetwork/chain/x/delegation/types";

Expand Down Expand Up @@ -31,9 +33,10 @@ message DelegationEntry {
uint64 k_index = 2;

// value is the quotient of collected rewards and total stake according to F1-distribution
string value = 3 [
(gogoproto.customtype) = "cosmossdk.io/math.LegacyDec",
(gogoproto.nullable) = false
repeated cosmos.base.v1beta1.DecCoin value = 3 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.DecCoins"
];
}

Expand All @@ -47,7 +50,11 @@ message DelegationData {
// F1Distribution

// current_rewards ...
uint64 current_rewards = 2;
repeated cosmos.base.v1beta1.Coin current_rewards = 2 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// total_delegation ...
uint64 total_delegation = 3;
// latest_index_k ...
Expand Down
8 changes: 7 additions & 1 deletion proto/kyve/delegation/v1beta1/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ syntax = "proto3";

package kyve.delegation.v1beta1;

import "cosmos/base/v1beta1/coin.proto";
import "gogoproto/gogo.proto";
import "amino/amino.proto";
import "kyve/delegation/v1beta1/delegation.proto";
import "kyve/delegation/v1beta1/params.proto";

Expand Down Expand Up @@ -78,7 +80,11 @@ message EventWithdrawRewards {
// staker is the account address of the protocol node the users withdraws from.
string staker = 2;
// amount ...
uint64 amount = 3;
repeated cosmos.base.v1beta1.Coin amount = 3 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
}

// EventSlash is an event emitted when a protocol node is slashed.
Expand Down
8 changes: 7 additions & 1 deletion proto/kyve/query/v1beta1/account.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import "cosmos/base/query/v1beta1/pagination.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "kyve/query/v1beta1/query.proto";
import "cosmos/base/v1beta1/coin.proto";
import "amino/amino.proto";

option go_package = "github.com/KYVENetwork/chain/x/query/types";

Expand Down Expand Up @@ -55,7 +57,11 @@ message QueryAccountAssetsResponse {
// protocol_delegation_unbonding
uint64 protocol_delegation_unbonding = 5;
// protocol_rewards ...
uint64 protocol_rewards = 6;
repeated cosmos.base.v1beta1.Coin protocol_rewards = 6 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// protocol_funding ...
uint64 protocol_funding = 7;
}
Expand Down
18 changes: 14 additions & 4 deletions proto/kyve/query/v1beta1/delegation.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import "cosmos/base/query/v1beta1/pagination.proto";
import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "kyve/query/v1beta1/query.proto";
import "cosmos/base/v1beta1/coin.proto";
import "amino/amino.proto";

option go_package = "github.com/KYVENetwork/chain/x/query/types";

Expand Down Expand Up @@ -51,8 +53,12 @@ message QueryDelegatorResponse {
message StakerDelegatorResponse {
// delegator ...
string delegator = 1;
// current_reward ...
uint64 current_reward = 2;
// current_rewards ...
repeated cosmos.base.v1beta1.Coin current_rewards = 6 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// delegation_amount ...
uint64 delegation_amount = 3;
// staker ...
Expand Down Expand Up @@ -109,8 +115,12 @@ message QueryStakersByDelegatorResponse {
message DelegationForStakerResponse {
// staker ...
FullStaker staker = 1;
// current_reward ...
uint64 current_reward = 2;
// current_rewards ...
repeated cosmos.base.v1beta1.Coin current_rewards = 6 [
(gogoproto.nullable) = false,
(amino.dont_omitempty) = true,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
// delegation_amount ...
uint64 delegation_amount = 3;
}
5 changes: 4 additions & 1 deletion x/bundles/keeper/msg_server_submit_bundle_proposal.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package keeper
import (
"context"

globalTypes "github.com/KYVENetwork/chain/x/global/types"

"github.com/KYVENetwork/chain/util"
"github.com/KYVENetwork/chain/x/bundles/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -92,7 +94,8 @@ func (k msgServer) SubmitBundleProposal(goCtx context.Context, msg *types.MsgSub
}

// payout rewards to delegators through delegation rewards
if err := k.delegationKeeper.PayoutRewards(ctx, bundleProposal.Uploader, bundleReward.Delegation, poolTypes.ModuleName); err != nil {
bundleRewardCoins := sdk.NewCoins(sdk.NewInt64Coin(globalTypes.Denom, int64(bundleReward.Delegation)))
if err := k.delegationKeeper.PayoutRewards(ctx, bundleProposal.Uploader, bundleRewardCoins, poolTypes.ModuleName); err != nil {
return nil, err
}

Expand Down
2 changes: 1 addition & 1 deletion x/bundles/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type DelegationKeeper interface {
GetDelegationAmount(ctx sdk.Context, staker string) uint64
GetDelegationOfPool(ctx sdk.Context, poolId uint64) uint64
GetTotalAndHighestDelegationOfPool(ctx sdk.Context, poolId uint64) (uint64, uint64)
PayoutRewards(ctx sdk.Context, staker string, amount uint64, payerModuleName string) error
PayoutRewards(ctx sdk.Context, staker string, amount sdk.Coins, payerModuleName string) error
SlashDelegators(ctx sdk.Context, poolId uint64, staker string, slashType delegationTypes.SlashType)
}

Expand Down
14 changes: 7 additions & 7 deletions x/delegation/keeper/exported_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ func (k Keeper) GetTotalAndHighestDelegationOfPool(ctx sdk.Context, poolId uint6
return
}

// PayoutRewards transfers `amount` $nKYVE from the `payerModuleName`-module to the delegation module.
// PayoutRewards transfers `amount` from the `payerModuleName`-module to the delegation module.
// It then awards these tokens internally to all delegators of staker `staker`.
// Delegators can then receive these rewards if they call the `withdraw`-transaction.
// If the staker has no delegators or the module to module transfer fails the method fails and
// returns the error.
func (k Keeper) PayoutRewards(ctx sdk.Context, staker string, amount uint64, payerModuleName string) error {
// If the staker has no delegators or the module to module transfer fails, the method fails and
// returns an error.
func (k Keeper) PayoutRewards(ctx sdk.Context, staker string, amount sdk.Coins, payerModuleName string) error {
// Assert there is an amount
if amount == 0 {
if amount.Empty() {
return nil
}

Expand All @@ -78,7 +78,7 @@ func (k Keeper) PayoutRewards(ctx sdk.Context, staker string, amount uint64, pay
k.AddAmountToDelegationRewards(ctx, staker, amount)

// Transfer tokens to the delegation module
if err := util.TransferFromModuleToModule(k.bankKeeper, ctx, payerModuleName, types.ModuleName, amount); err != nil {
if err := k.bankKeeper.SendCoinsFromModuleToModule(ctx, payerModuleName, types.ModuleName, amount); err != nil {
return err
}

Expand Down Expand Up @@ -115,6 +115,6 @@ func (k Keeper) SlashDelegators(ctx sdk.Context, poolId uint64, staker string, s

// GetOutstandingRewards calculates the current rewards a delegator has collected for
// the given staker.
func (k Keeper) GetOutstandingRewards(ctx sdk.Context, staker string, delegator string) uint64 {
func (k Keeper) GetOutstandingRewards(ctx sdk.Context, staker string, delegator string) sdk.Coins {
return k.f1GetOutstandingRewards(ctx, staker, delegator)
}
4 changes: 2 additions & 2 deletions x/delegation/keeper/getters_delegation_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ import (

// AddAmountToDelegationRewards adds the specified amount to the current delegationData object.
// This is needed by the F1-algorithm to calculate to outstanding rewards
func (k Keeper) AddAmountToDelegationRewards(ctx sdk.Context, stakerAddress string, amount uint64) {
func (k Keeper) AddAmountToDelegationRewards(ctx sdk.Context, stakerAddress string, amount sdk.Coins) {
delegationData, found := k.GetDelegationData(ctx, stakerAddress)
if found {
delegationData.CurrentRewards += amount
delegationData.CurrentRewards.Add(amount...)
k.SetDelegationData(ctx, delegationData)
}
}
Expand Down
21 changes: 14 additions & 7 deletions x/delegation/keeper/logic_delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ func (k Keeper) performDelegation(ctx sdk.Context, stakerAddress string, delegat
if k.DoesDelegatorExist(ctx, stakerAddress, delegatorAddress) {
// If the sender is already a delegator, first perform an undelegation, before delegating.
// "perform a withdrawal"
_ = k.performWithdrawal(ctx, stakerAddress, delegatorAddress)
if _, err := k.performWithdrawal(ctx, stakerAddress, delegatorAddress); err != nil {
util.PanicHalt(k.upgradeKeeper, ctx, "no money left in module")
}

// Perform delegation by fully undelegating and then delegating the new amount
unDelegateAmount := k.f1RemoveDelegator(ctx, stakerAddress, delegatorAddress)
Expand All @@ -37,7 +39,9 @@ func (k Keeper) performUndelegation(ctx sdk.Context, stakerAddress string, deleg
defer k.SetStakerIndex(ctx, stakerAddress)

// Withdraw all outstanding rewards
k.performWithdrawal(ctx, stakerAddress, delegatorAddress)
if _, err := k.performWithdrawal(ctx, stakerAddress, delegatorAddress); err != nil {
util.PanicHalt(k.upgradeKeeper, ctx, "no money left in module")
}

// Perform an internal re-delegation.
undelegatedAmount := k.f1RemoveDelegator(ctx, stakerAddress, delegatorAddress)
Expand All @@ -55,11 +59,14 @@ func (k Keeper) performUndelegation(ctx sdk.Context, stakerAddress string, deleg

// performWithdrawal withdraws all pending rewards from a user and transfers it.
// The amount is returned by the function.
func (k Keeper) performWithdrawal(ctx sdk.Context, stakerAddress, delegatorAddress string) uint64 {
func (k Keeper) performWithdrawal(ctx sdk.Context, stakerAddress, delegatorAddress string) (sdk.Coins, error) {
reward := k.f1WithdrawRewards(ctx, stakerAddress, delegatorAddress)
err := util.TransferFromModuleToAddress(k.bankKeeper, ctx, types.ModuleName, delegatorAddress, reward)
if err != nil {
util.PanicHalt(k.upgradeKeeper, ctx, "no money left in module")
recipient, errAddress := sdk.AccAddressFromBech32(delegatorAddress)
if errAddress != nil {
return nil, errAddress
}
if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, recipient, reward); err != nil {
return nil, err
}

// Emit withdraw event.
Expand All @@ -69,5 +76,5 @@ func (k Keeper) performWithdrawal(ctx sdk.Context, stakerAddress, delegatorAddre
Amount: reward,
})

return reward
return reward, nil
}
Loading

0 comments on commit 33238d2

Please sign in to comment.