github.com/Finschia/finschia-sdk@v0.48.1/x/distribution/keeper/keeper.go (about)

     1  package keeper
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/Finschia/ostracon/libs/log"
     7  
     8  	"github.com/Finschia/finschia-sdk/codec"
     9  	sdk "github.com/Finschia/finschia-sdk/types"
    10  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    11  	"github.com/Finschia/finschia-sdk/x/distribution/types"
    12  	paramtypes "github.com/Finschia/finschia-sdk/x/params/types"
    13  )
    14  
    15  // Keeper of the distribution store
    16  type Keeper struct {
    17  	storeKey      sdk.StoreKey
    18  	cdc           codec.BinaryCodec
    19  	paramSpace    paramtypes.Subspace
    20  	authKeeper    types.AccountKeeper
    21  	bankKeeper    types.BankKeeper
    22  	stakingKeeper types.StakingKeeper
    23  
    24  	blockedAddrs map[string]bool
    25  
    26  	feeCollectorName string // name of the FeeCollector ModuleAccount
    27  }
    28  
    29  // NewKeeper creates a new distribution Keeper instance
    30  func NewKeeper(
    31  	cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace paramtypes.Subspace,
    32  	ak types.AccountKeeper, bk types.BankKeeper, sk types.StakingKeeper,
    33  	feeCollectorName string, blockedAddrs map[string]bool,
    34  ) Keeper {
    35  	// ensure distribution module account is set
    36  	if addr := ak.GetModuleAddress(types.ModuleName); addr == nil {
    37  		panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
    38  	}
    39  
    40  	// set KeyTable if it has not already been set
    41  	if !paramSpace.HasKeyTable() {
    42  		paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
    43  	}
    44  
    45  	return Keeper{
    46  		storeKey:         key,
    47  		cdc:              cdc,
    48  		paramSpace:       paramSpace,
    49  		authKeeper:       ak,
    50  		bankKeeper:       bk,
    51  		stakingKeeper:    sk,
    52  		feeCollectorName: feeCollectorName,
    53  		blockedAddrs:     blockedAddrs,
    54  	}
    55  }
    56  
    57  // Logger returns a module-specific logger.
    58  func (k Keeper) Logger(ctx sdk.Context) log.Logger {
    59  	return ctx.Logger().With("module", "x/"+types.ModuleName)
    60  }
    61  
    62  // SetWithdrawAddr sets a new address that will receive the rewards upon withdrawal
    63  func (k Keeper) SetWithdrawAddr(ctx sdk.Context, delegatorAddr sdk.AccAddress, withdrawAddr sdk.AccAddress) error {
    64  	if k.blockedAddrs[withdrawAddr.String()] {
    65  		return sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive external funds", withdrawAddr)
    66  	}
    67  
    68  	if !k.GetWithdrawAddrEnabled(ctx) {
    69  		return types.ErrSetWithdrawAddrDisabled
    70  	}
    71  
    72  	ctx.EventManager().EmitEvent(
    73  		sdk.NewEvent(
    74  			types.EventTypeSetWithdrawAddress,
    75  			sdk.NewAttribute(types.AttributeKeyWithdrawAddress, withdrawAddr.String()),
    76  		),
    77  	)
    78  
    79  	k.SetDelegatorWithdrawAddr(ctx, delegatorAddr, withdrawAddr)
    80  	return nil
    81  }
    82  
    83  // withdraw rewards from a delegation
    84  func (k Keeper) WithdrawDelegationRewards(ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) {
    85  	val := k.stakingKeeper.Validator(ctx, valAddr)
    86  	if val == nil {
    87  		return nil, types.ErrNoValidatorDistInfo
    88  	}
    89  
    90  	del := k.stakingKeeper.Delegation(ctx, delAddr, valAddr)
    91  	if del == nil {
    92  		return nil, types.ErrEmptyDelegationDistInfo
    93  	}
    94  
    95  	// withdraw rewards
    96  	rewards, err := k.withdrawDelegationRewards(ctx, val, del)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	// reinitialize the delegation
   102  	k.initializeDelegation(ctx, valAddr, delAddr)
   103  	return rewards, nil
   104  }
   105  
   106  // withdraw validator commission
   107  func (k Keeper) WithdrawValidatorCommission(ctx sdk.Context, valAddr sdk.ValAddress) (sdk.Coins, error) {
   108  	// fetch validator accumulated commission
   109  	accumCommission := k.GetValidatorAccumulatedCommission(ctx, valAddr)
   110  	if accumCommission.Commission.IsZero() {
   111  		return nil, types.ErrNoValidatorCommission
   112  	}
   113  
   114  	commission, remainder := accumCommission.Commission.TruncateDecimal()
   115  	k.SetValidatorAccumulatedCommission(ctx, valAddr, types.ValidatorAccumulatedCommission{Commission: remainder}) // leave remainder to withdraw later
   116  
   117  	// update outstanding
   118  	outstanding := k.GetValidatorOutstandingRewards(ctx, valAddr).Rewards
   119  	k.SetValidatorOutstandingRewards(ctx, valAddr, types.ValidatorOutstandingRewards{Rewards: outstanding.Sub(sdk.NewDecCoinsFromCoins(commission...))})
   120  
   121  	if !commission.IsZero() {
   122  		accAddr := sdk.AccAddress(valAddr)
   123  		withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, accAddr)
   124  		err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, commission)
   125  		if err != nil {
   126  			return nil, err
   127  		}
   128  	}
   129  
   130  	ctx.EventManager().EmitEvent(
   131  		sdk.NewEvent(
   132  			types.EventTypeWithdrawCommission,
   133  			sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()),
   134  		),
   135  	)
   136  
   137  	return commission, nil
   138  }
   139  
   140  // GetTotalRewards returns the total amount of fee distribution rewards held in the store
   141  func (k Keeper) GetTotalRewards(ctx sdk.Context) (totalRewards sdk.DecCoins) {
   142  	k.IterateValidatorOutstandingRewards(ctx,
   143  		func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
   144  			totalRewards = totalRewards.Add(rewards.Rewards...)
   145  			return false
   146  		},
   147  	)
   148  
   149  	return totalRewards
   150  }
   151  
   152  // FundCommunityPool allows an account to directly fund the community fund pool.
   153  // The amount is first added to the distribution module account and then directly
   154  // added to the pool. An error is returned if the amount cannot be sent to the
   155  // module account.
   156  func (k Keeper) FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error {
   157  	if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
   158  		return err
   159  	}
   160  
   161  	feePool := k.GetFeePool(ctx)
   162  	feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...)
   163  	k.SetFeePool(ctx, feePool)
   164  
   165  	return nil
   166  }