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 }