package keeper import ( "context" "fmt" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/protocolpool/types" ) type MsgServer struct { Keeper } var _ types.MsgServer = MsgServer{} // NewMsgServerImpl returns an implementation of the protocolpool MsgServer interface // for the provided Keeper. func NewMsgServerImpl(keeper Keeper) types.MsgServer { return &MsgServer{Keeper: keeper} } func (k MsgServer) FundCommunityPool(ctx context.Context, msg *types.MsgFundCommunityPool) (*types.MsgFundCommunityPoolResponse, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) depositor, err := k.authKeeper.AddressCodec().StringToBytes(msg.Depositor) if err != nil { return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid depositor address: %s", err) } if err := validateAmount(msg.Amount); err != nil { return nil, err } // send funds to community pool module account if err := k.Keeper.FundCommunityPool(sdkCtx, msg.Amount, depositor); err != nil { return nil, err } return &types.MsgFundCommunityPoolResponse{}, nil } func (k MsgServer) CommunityPoolSpend(ctx context.Context, msg *types.MsgCommunityPoolSpend) (*types.MsgCommunityPoolSpendResponse, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) if err := k.validateAuthority(msg.Authority); err != nil { return nil, err } if err := validateAmount(msg.Amount); err != nil { return nil, err } recipient, err := k.authKeeper.AddressCodec().StringToBytes(msg.Recipient) if err != nil { return nil, err } // distribute funds from community pool module account if err := k.DistributeFromCommunityPool(sdkCtx, msg.Amount, recipient); err != nil { return nil, err } sdkCtx.Logger().Debug("transferred from the community pool", "amount", msg.Amount.String(), "recipient", msg.Recipient) return &types.MsgCommunityPoolSpendResponse{}, nil } func (k MsgServer) CreateContinuousFund(ctx context.Context, msg *types.MsgCreateContinuousFund) (*types.MsgCreateContinuousFundResponse, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) if err := k.validateAuthority(msg.Authority); err != nil { return nil, err } recipient, err := k.Keeper.authKeeper.AddressCodec().StringToBytes(msg.Recipient) if err != nil { return nil, err } // deny creation if we know this address is blocked from receiving funds if k.bankKeeper.BlockedAddr(recipient) { return nil, fmt.Errorf("recipient is blocked in the bank keeper: %s", msg.Recipient) } has, err := k.ContinuousFunds.Has(sdkCtx, recipient) if err != nil { return nil, err } if has { return nil, fmt.Errorf("continuous fund already exists for recipient %s", msg.Recipient) } // Validate the message fields err = validateContinuousFund(sdkCtx, *msg) if err != nil { return nil, err } // Check if total funds percentage exceeds 100% // If exceeds, we should not setup continuous fund proposal. totalStreamFundsPercentage := math.LegacyZeroDec() err = k.ContinuousFunds.Walk(sdkCtx, nil, func(key sdk.AccAddress, value types.ContinuousFund) (stop bool, err error) { totalStreamFundsPercentage = totalStreamFundsPercentage.Add(value.Percentage) return false, nil }) if err != nil { return nil, err } totalStreamFundsPercentage = totalStreamFundsPercentage.Add(msg.Percentage) if totalStreamFundsPercentage.GT(math.LegacyOneDec()) { return nil, fmt.Errorf("cannot set continuous fund proposal\ntotal funds percentage exceeds 100\ncurrent total percentage: %s", totalStreamFundsPercentage.Sub(msg.Percentage).MulInt64(100).TruncateInt().String()) } // Create continuous fund proposal cf := types.ContinuousFund{ Recipient: msg.Recipient, Percentage: msg.Percentage, Expiry: msg.Expiry, } // Set continuous fund to the state err = k.ContinuousFunds.Set(sdkCtx, recipient, cf) if err != nil { return nil, err } return &types.MsgCreateContinuousFundResponse{}, nil } func (k MsgServer) CancelContinuousFund(ctx context.Context, msg *types.MsgCancelContinuousFund) (*types.MsgCancelContinuousFundResponse, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) if err := k.validateAuthority(msg.Authority); err != nil { return nil, err } recipient, err := k.Keeper.authKeeper.AddressCodec().StringToBytes(msg.Recipient) if err != nil { return nil, err } canceledHeight := sdkCtx.BlockHeight() canceledTime := sdkCtx.BlockTime() has, err := k.ContinuousFunds.Has(sdkCtx, recipient) if err != nil { return nil, fmt.Errorf("cannot get continuous fund for recipient %w", err) } if !has { return nil, fmt.Errorf("cannot cancel continuous fund for recipient %s - does not exist", msg.Recipient) } if err := k.ContinuousFunds.Remove(sdkCtx, recipient); err != nil { return nil, fmt.Errorf("failed to remove continuous fund for recipient %s: %w", msg.Recipient, err) } return &types.MsgCancelContinuousFundResponse{ CanceledTime: canceledTime, CanceledHeight: uint64(canceledHeight), Recipient: msg.Recipient, }, nil } func (k MsgServer) UpdateParams(ctx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { sdkCtx := sdk.UnwrapSDKContext(ctx) if err := k.validateAuthority(msg.GetAuthority()); err != nil { return nil, err } if err := msg.Params.Validate(); err != nil { return nil, fmt.Errorf("invalid params: %w", err) } if err := k.Params.Set(sdkCtx, msg.Params); err != nil { return nil, fmt.Errorf("failed to set params: %w", err) } return &types.MsgUpdateParamsResponse{}, nil }