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 }