mukan-sdk/x/distribution/keeper/keeper.go
Mukan Erkin Törük abb1ff956e
Some checks are pending
Build SimApp / build (amd64) (push) Waiting to run
Build SimApp / build (arm64) (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Build & Push / build (push) Waiting to run
Run Gosec / Gosec (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Checks dependencies and mocks generation / Check go mod tidy (push) Waiting to run
Checks dependencies and mocks generation / Check up to date mocks (push) Waiting to run
System Tests / setup (push) Waiting to run
System Tests / test-system (push) Blocked by required conditions
System Tests / test-system-legacy (push) Blocked by required conditions
Tests / Code Coverage / split-test-files (push) Waiting to run
Tests / Code Coverage / tests (00) (push) Blocked by required conditions
Tests / Code Coverage / tests (01) (push) Blocked by required conditions
Tests / Code Coverage / tests (02) (push) Blocked by required conditions
Tests / Code Coverage / tests (03) (push) Blocked by required conditions
Tests / Code Coverage / test-integration (push) Waiting to run
Tests / Code Coverage / test-e2e (push) Waiting to run
Tests / Code Coverage / repo-analysis (push) Blocked by required conditions
Tests / Code Coverage / test-sim-nondeterminism (push) Waiting to run
Tests / Code Coverage / test-clientv2 (push) Waiting to run
Tests / Code Coverage / test-core (push) Waiting to run
Tests / Code Coverage / test-depinject (push) Waiting to run
Tests / Code Coverage / test-errors (push) Waiting to run
Tests / Code Coverage / test-math (push) Waiting to run
Tests / Code Coverage / test-schema (push) Waiting to run
Tests / Code Coverage / test-collections (push) Waiting to run
Tests / Code Coverage / test-cosmovisor (push) Waiting to run
Tests / Code Coverage / test-confix (push) Waiting to run
Tests / Code Coverage / test-store (push) Waiting to run
Tests / Code Coverage / test-log (push) Waiting to run
Tests / Code Coverage / test-x-tx (push) Waiting to run
Tests / Code Coverage / test-x-nft (push) Waiting to run
Tests / Code Coverage / test-x-circuit (push) Waiting to run
Tests / Code Coverage / test-x-feegrant (push) Waiting to run
Tests / Code Coverage / test-x-evidence (push) Waiting to run
Tests / Code Coverage / test-x-upgrade (push) Waiting to run
Tests / Code Coverage / test-tools-benchmark (push) Waiting to run
refactor: complete sovereign stack cleanup — all github.com upstream refs purged
2026-05-11 03:46:06 +03:00

261 lines
7.7 KiB
Go

package keeper
import (
"context"
"fmt"
"cosmossdk.io/collections"
"cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"
"git.cw.tr/mukan-network/mukan-sdk/codec"
sdk "git.cw.tr/mukan-network/mukan-sdk/types"
sdkerrors "git.cw.tr/mukan-network/mukan-sdk/types/errors"
"git.cw.tr/mukan-network/mukan-sdk/x/distribution/types"
)
// Keeper of the distribution store
type Keeper struct {
storeService store.KVStoreService
cdc codec.BinaryCodec
authKeeper types.AccountKeeper
bankKeeper types.BankKeeper
stakingKeeper types.StakingKeeper
// the address capable of executing a MsgUpdateParams message. Typically, this
// should be the x/gov module account.
authority string
Schema collections.Schema
Params collections.Item[types.Params]
FeePool collections.Item[types.FeePool]
feeCollectorName string // name of the FeeCollector ModuleAccount
externalCommunityPool types.ExternalCommunityPoolKeeper
}
type InitOption func(*Keeper)
// WithExternalCommunityPool will enable the external pool functionality in x/distribution, directing
// community pool funds to the provided keeper.
//
// WARNING: using an external community pool will cause the following handlers to error when called:
// - FundCommunityPool tx
// - CommunityPoolSpend tx
// - CommunityPool query
func WithExternalCommunityPool(poolKeeper types.ExternalCommunityPoolKeeper) InitOption {
return func(k *Keeper) {
k.externalCommunityPool = poolKeeper
}
}
// NewKeeper creates a new distribution Keeper instance
func NewKeeper(
cdc codec.BinaryCodec,
storeService store.KVStoreService,
ak types.AccountKeeper,
bk types.BankKeeper,
sk types.StakingKeeper,
feeCollectorName, authority string,
opts ...InitOption,
) Keeper {
// ensure distribution module account is set
if addr := ak.GetModuleAddress(types.ModuleName); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", types.ModuleName))
}
sb := collections.NewSchemaBuilder(storeService)
k := Keeper{
storeService: storeService,
cdc: cdc,
authKeeper: ak,
bankKeeper: bk,
stakingKeeper: sk,
feeCollectorName: feeCollectorName,
authority: authority,
Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)),
FeePool: collections.NewItem(sb, types.FeePoolKey, "fee_pool", codec.CollValue[types.FeePool](cdc)),
externalCommunityPool: nil,
}
schema, err := sb.Build()
if err != nil {
panic(err)
}
k.Schema = schema
for _, opt := range opts {
opt(&k)
}
if k.HasExternalCommunityPool() {
// ensure external module account is set if we are enabling it
// this will ensure that funds can be transferred to it.
if addr := ak.GetModuleAddress(k.externalCommunityPool.GetCommunityPoolModule()); addr == nil {
panic(fmt.Sprintf("%s module account has not been set", k.externalCommunityPool.GetCommunityPoolModule()))
}
}
return k
}
// GetAuthority returns the x/distribution module's authority.
func (k Keeper) GetAuthority() string {
return k.authority
}
// HasExternalCommunityPool is a helper function to denote whether the x/distribution module
// is using its native community pool, or using an external pool.
func (k Keeper) HasExternalCommunityPool() bool {
return k.externalCommunityPool != nil
}
// Logger returns a module-specific logger.
func (k Keeper) Logger(ctx context.Context) log.Logger {
sdkCtx := sdk.UnwrapSDKContext(ctx)
return sdkCtx.Logger().With(log.ModuleKey, "x/"+types.ModuleName)
}
// SetWithdrawAddr sets a new address that will receive the rewards upon withdrawal
func (k Keeper) SetWithdrawAddr(ctx context.Context, delegatorAddr, withdrawAddr sdk.AccAddress) error {
if k.bankKeeper.BlockedAddr(withdrawAddr) {
return errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive external funds", withdrawAddr)
}
withdrawAddrEnabled, err := k.GetWithdrawAddrEnabled(ctx)
if err != nil {
return err
}
if !withdrawAddrEnabled {
return types.ErrSetWithdrawAddrDisabled
}
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeSetWithdrawAddress,
sdk.NewAttribute(types.AttributeKeyWithdrawAddress, withdrawAddr.String()),
),
)
return k.SetDelegatorWithdrawAddr(ctx, delegatorAddr, withdrawAddr)
}
// WithdrawDelegationRewards withdraws rewards from a delegation
func (k Keeper) WithdrawDelegationRewards(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) {
val, err := k.stakingKeeper.Validator(ctx, valAddr)
if err != nil {
return nil, err
}
if val == nil {
return nil, types.ErrNoValidatorDistInfo
}
del, err := k.stakingKeeper.Delegation(ctx, delAddr, valAddr)
if err != nil {
return nil, err
}
if del == nil {
return nil, types.ErrEmptyDelegationDistInfo
}
// withdraw rewards
rewards, err := k.withdrawDelegationRewards(ctx, val, del)
if err != nil {
return nil, err
}
// reinitialize the delegation
err = k.initializeDelegation(ctx, valAddr, delAddr)
if err != nil {
return nil, err
}
return rewards, nil
}
// WithdrawValidatorCommission withdraws validator commission.
func (k Keeper) WithdrawValidatorCommission(ctx context.Context, valAddr sdk.ValAddress) (sdk.Coins, error) {
// fetch validator accumulated commission
accumCommission, err := k.GetValidatorAccumulatedCommission(ctx, valAddr)
if err != nil {
return nil, err
}
if accumCommission.Commission.IsZero() {
return nil, types.ErrNoValidatorCommission
}
commission, remainder := accumCommission.Commission.TruncateDecimal()
err = k.SetValidatorAccumulatedCommission(ctx, valAddr, types.ValidatorAccumulatedCommission{Commission: remainder}) // leave remainder to withdraw later
if err != nil {
return nil, err
}
// update outstanding
outstanding, err := k.GetValidatorOutstandingRewards(ctx, valAddr)
if err != nil {
return nil, err
}
err = k.SetValidatorOutstandingRewards(ctx, valAddr, types.ValidatorOutstandingRewards{Rewards: outstanding.Rewards.Sub(sdk.NewDecCoinsFromCoins(commission...))})
if err != nil {
return nil, err
}
if !commission.IsZero() {
accAddr := sdk.AccAddress(valAddr)
withdrawAddr, err := k.GetDelegatorWithdrawAddr(ctx, accAddr)
if err != nil {
return nil, err
}
err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, withdrawAddr, commission)
if err != nil {
return nil, err
}
}
sdkCtx := sdk.UnwrapSDKContext(ctx)
sdkCtx.EventManager().EmitEvent(
sdk.NewEvent(
types.EventTypeWithdrawCommission,
sdk.NewAttribute(sdk.AttributeKeyAmount, commission.String()),
),
)
return commission, nil
}
// GetTotalRewards returns the total amount of fee distribution rewards held in the store
func (k Keeper) GetTotalRewards(ctx context.Context) (totalRewards sdk.DecCoins) {
k.IterateValidatorOutstandingRewards(ctx,
func(_ sdk.ValAddress, rewards types.ValidatorOutstandingRewards) (stop bool) {
totalRewards = totalRewards.Add(rewards.Rewards...)
return false
},
)
return totalRewards
}
// FundCommunityPool allows an account to directly fund the community fund pool.
// The amount is first added to the distribution module account and then directly
// added to the pool. An error is returned if the amount cannot be sent to the
// module account.
func (k Keeper) FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error {
if err := k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.ModuleName, amount); err != nil {
return err
}
feePool, err := k.FeePool.Get(ctx)
if err != nil {
return err
}
feePool.CommunityPool = feePool.CommunityPool.Add(sdk.NewDecCoinsFromCoins(amount...)...)
return k.FeePool.Set(ctx, feePool)
}