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 }