github.com/cosmos/cosmos-sdk@v0.50.10/x/auth/vesting/msg_server.go (about)

     1  package vesting
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/hashicorp/go-metrics"
     7  
     8  	errorsmod "cosmossdk.io/errors"
     9  
    10  	"github.com/cosmos/cosmos-sdk/telemetry"
    11  	sdk "github.com/cosmos/cosmos-sdk/types"
    12  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    13  	"github.com/cosmos/cosmos-sdk/x/auth/keeper"
    14  	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
    15  	"github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
    16  )
    17  
    18  type msgServer struct {
    19  	keeper.AccountKeeper
    20  	types.BankKeeper
    21  }
    22  
    23  // NewMsgServerImpl returns an implementation of the vesting MsgServer interface,
    24  // wrapping the corresponding AccountKeeper and BankKeeper.
    25  func NewMsgServerImpl(k keeper.AccountKeeper, bk types.BankKeeper) types.MsgServer {
    26  	return &msgServer{AccountKeeper: k, BankKeeper: bk}
    27  }
    28  
    29  var _ types.MsgServer = msgServer{}
    30  
    31  func (s msgServer) CreateVestingAccount(goCtx context.Context, msg *types.MsgCreateVestingAccount) (*types.MsgCreateVestingAccountResponse, error) {
    32  	from, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.FromAddress)
    33  	if err != nil {
    34  		return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
    35  	}
    36  
    37  	to, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.ToAddress)
    38  	if err != nil {
    39  		return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
    40  	}
    41  
    42  	if err := validateAmount(msg.Amount); err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	if msg.EndTime <= 0 {
    47  		return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid end time")
    48  	}
    49  
    50  	ctx := sdk.UnwrapSDKContext(goCtx)
    51  	if err := s.BankKeeper.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
    52  		return nil, err
    53  	}
    54  
    55  	if s.BankKeeper.BlockedAddr(to) {
    56  		return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
    57  	}
    58  
    59  	if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
    60  		return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
    61  	}
    62  
    63  	baseAccount := authtypes.NewBaseAccountWithAddress(to)
    64  	baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
    65  	baseVestingAccount, err := types.NewBaseVestingAccount(baseAccount, msg.Amount.Sort(), msg.EndTime)
    66  	if err != nil {
    67  		return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
    68  	}
    69  
    70  	var vestingAccount sdk.AccountI
    71  	if msg.Delayed {
    72  		vestingAccount = types.NewDelayedVestingAccountRaw(baseVestingAccount)
    73  	} else {
    74  		vestingAccount = types.NewContinuousVestingAccountRaw(baseVestingAccount, ctx.BlockTime().Unix())
    75  	}
    76  
    77  	s.AccountKeeper.SetAccount(ctx, vestingAccount)
    78  
    79  	defer func() {
    80  		telemetry.IncrCounter(1, "new", "account")
    81  
    82  		for _, a := range msg.Amount {
    83  			if a.Amount.IsInt64() {
    84  				telemetry.SetGaugeWithLabels(
    85  					[]string{"tx", "msg", "create_vesting_account"},
    86  					float32(a.Amount.Int64()),
    87  					[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
    88  				)
    89  			}
    90  		}
    91  	}()
    92  
    93  	if err = s.BankKeeper.SendCoins(ctx, from, to, msg.Amount); err != nil {
    94  		return nil, err
    95  	}
    96  
    97  	return &types.MsgCreateVestingAccountResponse{}, nil
    98  }
    99  
   100  func (s msgServer) CreatePermanentLockedAccount(goCtx context.Context, msg *types.MsgCreatePermanentLockedAccount) (*types.MsgCreatePermanentLockedAccountResponse, error) {
   101  	from, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.FromAddress)
   102  	if err != nil {
   103  		return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
   104  	}
   105  
   106  	to, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.ToAddress)
   107  	if err != nil {
   108  		return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
   109  	}
   110  
   111  	if err := validateAmount(msg.Amount); err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	ctx := sdk.UnwrapSDKContext(goCtx)
   116  	if err := s.BankKeeper.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	if s.BankKeeper.BlockedAddr(to) {
   121  		return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
   122  	}
   123  
   124  	if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
   125  		return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
   126  	}
   127  
   128  	baseAccount := authtypes.NewBaseAccountWithAddress(to)
   129  	baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
   130  	vestingAccount, err := types.NewPermanentLockedAccount(baseAccount, msg.Amount)
   131  	if err != nil {
   132  		return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
   133  	}
   134  
   135  	s.AccountKeeper.SetAccount(ctx, vestingAccount)
   136  
   137  	defer func() {
   138  		telemetry.IncrCounter(1, "new", "account")
   139  
   140  		for _, a := range msg.Amount {
   141  			if a.Amount.IsInt64() {
   142  				telemetry.SetGaugeWithLabels(
   143  					[]string{"tx", "msg", "create_permanent_locked_account"},
   144  					float32(a.Amount.Int64()),
   145  					[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
   146  				)
   147  			}
   148  		}
   149  	}()
   150  
   151  	if err = s.BankKeeper.SendCoins(ctx, from, to, msg.Amount); err != nil {
   152  		return nil, err
   153  	}
   154  
   155  	return &types.MsgCreatePermanentLockedAccountResponse{}, nil
   156  }
   157  
   158  func (s msgServer) CreatePeriodicVestingAccount(goCtx context.Context, msg *types.MsgCreatePeriodicVestingAccount) (*types.MsgCreatePeriodicVestingAccountResponse, error) {
   159  	from, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.FromAddress)
   160  	if err != nil {
   161  		return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
   162  	}
   163  
   164  	to, err := s.AccountKeeper.AddressCodec().StringToBytes(msg.ToAddress)
   165  	if err != nil {
   166  		return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
   167  	}
   168  
   169  	if msg.StartTime < 1 {
   170  		return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid start time of %d, length must be greater than 0", msg.StartTime)
   171  	}
   172  
   173  	var totalCoins sdk.Coins
   174  	for i, period := range msg.VestingPeriods {
   175  		if period.Length < 1 {
   176  			return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid period length of %d in period %d, length must be greater than 0", period.Length, i)
   177  		}
   178  
   179  		if err := validateAmount(period.Amount); err != nil {
   180  			return nil, err
   181  		}
   182  
   183  		totalCoins = totalCoins.Add(period.Amount...)
   184  	}
   185  
   186  	if s.BankKeeper.BlockedAddr(to) {
   187  		return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
   188  	}
   189  
   190  	ctx := sdk.UnwrapSDKContext(goCtx)
   191  	if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
   192  		return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
   193  	}
   194  
   195  	if err := s.BankKeeper.IsSendEnabledCoins(ctx, totalCoins...); err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	baseAccount := authtypes.NewBaseAccountWithAddress(to)
   200  	baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
   201  	vestingAccount, err := types.NewPeriodicVestingAccount(baseAccount, totalCoins.Sort(), msg.StartTime, msg.VestingPeriods)
   202  	if err != nil {
   203  		return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
   204  	}
   205  
   206  	s.AccountKeeper.SetAccount(ctx, vestingAccount)
   207  
   208  	defer func() {
   209  		telemetry.IncrCounter(1, "new", "account")
   210  
   211  		for _, a := range totalCoins {
   212  			if a.Amount.IsInt64() {
   213  				telemetry.SetGaugeWithLabels(
   214  					[]string{"tx", "msg", "create_periodic_vesting_account"},
   215  					float32(a.Amount.Int64()),
   216  					[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
   217  				)
   218  			}
   219  		}
   220  	}()
   221  
   222  	if err = s.BankKeeper.SendCoins(ctx, from, to, totalCoins); err != nil {
   223  		return nil, err
   224  	}
   225  
   226  	return &types.MsgCreatePeriodicVestingAccountResponse{}, nil
   227  }
   228  
   229  func validateAmount(amount sdk.Coins) error {
   230  	if !amount.IsValid() {
   231  		return sdkerrors.ErrInvalidCoins.Wrap(amount.String())
   232  	}
   233  
   234  	if !amount.IsAllPositive() {
   235  		return sdkerrors.ErrInvalidCoins.Wrap(amount.String())
   236  	}
   237  
   238  	return nil
   239  }