github.com/cosmos/cosmos-sdk@v0.50.10/x/distribution/keeper/keeper.go (about)

     1  package keeper
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"cosmossdk.io/collections"
     8  	"cosmossdk.io/core/store"
     9  	errorsmod "cosmossdk.io/errors"
    10  	"cosmossdk.io/log"
    11  
    12  	"github.com/cosmos/cosmos-sdk/codec"
    13  	sdk "github.com/cosmos/cosmos-sdk/types"
    14  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    15  	"github.com/cosmos/cosmos-sdk/x/distribution/types"
    16  )
    17  
    18  // Keeper of the distribution store
    19  type Keeper struct {
    20  	storeService  store.KVStoreService
    21  	cdc           codec.BinaryCodec
    22  	authKeeper    types.AccountKeeper
    23  	bankKeeper    types.BankKeeper
    24  	stakingKeeper types.StakingKeeper
    25  	// the address capable of executing a MsgUpdateParams message. Typically, this
    26  	// should be the x/gov module account.
    27  	authority string
    28  
    29  	Schema  collections.Schema
    30  	Params  collections.Item[types.Params]
    31  	FeePool collections.Item[types.FeePool]
    32  
    33  	feeCollectorName string // name of the FeeCollector ModuleAccount
    34  }
    35  
    36  // NewKeeper creates a new distribution Keeper instance
    37  func NewKeeper(
    38  	cdc codec.BinaryCodec, storeService store.KVStoreService,
    39  	ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper,
    40  	feeCollectorName, authority string,
    41  ) Keeper {
    42  	// ensure distribution module account is set
    43  	if addr := ak.GetModuleAddress(types.ModuleName); addr == nil {
    44  		panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
    45  	}
    46  
    47  	sb := collections.NewSchemaBuilder(storeService)
    48  	k := Keeper{
    49  		storeService:     storeService,
    50  		cdc:              cdc,
    51  		authKeeper:       ak,
    52  		bankKeeper:       bk,
    53  		stakingKeeper:    sk,
    54  		feeCollectorName: feeCollectorName,
    55  		authority:        authority,
    56  		Params:           collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)),
    57  		FeePool:          collections.NewItem(sb, types.FeePoolKey, "fee_pool", codec.CollValue[types.FeePool](cdc)),
    58  	}
    59  
    60  	schema, err := sb.Build()
    61  	if err != nil {
    62  		panic(err)
    63  	}
    64  	k.Schema = schema
    65  	return k
    66  }
    67  
    68  // GetAuthority returns the x/distribution module's authority.
    69  func (k Keeper) GetAuthority() string {
    70  	return k.authority
    71  }
    72  
    73  // Logger returns a module-specific logger.
    74  func (k Keeper) Logger(ctx context.Context) log.Logger {
    75  	sdkCtx := sdk.UnwrapSDKContext(ctx)
    76  	return sdkCtx.Logger().With(log.ModuleKey, "x/"+types.ModuleName)
    77  }
    78  
    79  // SetWithdrawAddr sets a new address that will receive the rewards upon withdrawal
    80  func (k Keeper) SetWithdrawAddr(ctx context.Context, delegatorAddr, withdrawAddr sdk.AccAddress) error {
    81  	if k.bankKeeper.BlockedAddr(withdrawAddr) {
    82  		return errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive external funds", withdrawAddr)
    83  	}
    84  
    85  	withdrawAddrEnabled, err := k.GetWithdrawAddrEnabled(ctx)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	if !withdrawAddrEnabled {
    91  		return types.ErrSetWithdrawAddrDisabled
    92  	}
    93  
    94  	sdkCtx := sdk.UnwrapSDKContext(ctx)
    95  	sdkCtx.EventManager().EmitEvent(
    96  		sdk.NewEvent(
    97  			types.EventTypeSetWithdrawAddress,
    98  			sdk.NewAttribute(types.AttributeKeyWithdrawAddress, withdrawAddr.String()),
    99  		),
   100  	)
   101  
   102  	k.SetDelegatorWithdrawAddr(ctx, delegatorAddr, withdrawAddr)
   103  	return nil
   104  }
   105  
   106  // withdraw rewards from a delegation
   107  func (k Keeper) WithdrawDelegationRewards(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) {
   108  	val, err := k.stakingKeeper.Validator(ctx, valAddr)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	if val == nil {
   114  		return nil, types.ErrNoValidatorDistInfo
   115  	}
   116  
   117  	del, err := k.stakingKeeper.Delegation(ctx, delAddr, valAddr)
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  
   122  	if del == nil {
   123  		return nil, types.ErrEmptyDelegationDistInfo
   124  	}
   125  
   126  	// withdraw rewards
   127  	rewards, err := k.withdrawDelegationRewards(ctx, val, del)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	// reinitialize the delegation
   133  	err = k.initializeDelegation(ctx, valAddr, delAddr)
   134  	if err != nil {
   135  		return nil, err
   136  	}
   137  	return rewards, nil
   138  }
   139  
   140  // withdraw validator commission
   141  func (k Keeper) WithdrawValidatorCommission(ctx context.Context, valAddr sdk.ValAddress) (sdk.Coins, error) {
   142  	// fetch validator accumulated commission
   143  	accumCommission, err := k.GetValidatorAccumulatedCommission(ctx, valAddr)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	if accumCommission.Commission.IsZero() {
   149  		return nil, types.ErrNoValidatorCommission
   150  	}
   151  
   152  	commission, remainder := accumCommission.Commission.TruncateDecimal()
   153  	k.SetValidatorAccumulatedCommission(ctx, valAddr, types.ValidatorAccumulatedCommission{Commission: remainder}) // leave remainder to withdraw later
   154  
   155  	// update outstanding
   156  	outstanding, err := k.GetValidatorOutstandingRewards(ctx, valAddr)
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	err = k.SetValidatorOutstandingRewards(ctx, valAddr, types.ValidatorOutstandingRewards{Rewards: outstanding.Rewards.Sub(sdk.NewDecCoinsFromCoins(commission...))})
   162  	if err != nil {
   163  		return nil, err
   164  	}
   165  
   166  	if !commission.IsZero() {
   167  		accAddr := sdk.AccAddress(valAddr)
   168  		withdrawAddr, err := k.GetDelegatorWithdrawAddr(ctx, accAddr)
   169  		if err != nil {
   170  			return nil, err
   171  		}
   172  
   173  		err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, commission)
   174  		if err != nil {
   175  			return nil, err
   176  		}
   177  	}
   178  
   179  	sdkCtx := sdk.UnwrapSDKContext(ctx)
   180  	sdkCtx.EventManager().EmitEvent(
   181  		sdk.NewEvent(
   182  			types.EventTypeWithdrawCommission,
   183  			sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()),
   184  		),
   185  	)
   186  
   187  	return commission, nil
   188  }
   189  
   190  // GetTotalRewards returns the total amount of fee distribution rewards held in the store
   191  func (k Keeper) GetTotalRewards(ctx context.Context) (totalRewards sdk.DecCoins) {
   192  	k.IterateValidatorOutstandingRewards(ctx,
   193  		func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
   194  			totalRewards = totalRewards.Add(rewards.Rewards...)
   195  			return false
   196  		},
   197  	)
   198  
   199  	return totalRewards
   200  }
   201  
   202  // FundCommunityPool allows an account to directly fund the community fund pool.
   203  // The amount is first added to the distribution module account and then directly
   204  // added to the pool. An error is returned if the amount cannot be sent to the
   205  // module account.
   206  func (k Keeper) FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error {
   207  	if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
   208  		return err
   209  	}
   210  
   211  	feePool, err := k.FeePool.Get(ctx)
   212  	if err != nil {
   213  		return err
   214  	}
   215  
   216  	feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...)
   217  	return k.FeePool.Set(ctx, feePool)
   218  }