223 lines
6.8 KiB
Go
223 lines
6.8 KiB
Go
package vesting
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/armon/go-metrics"
|
|
|
|
errorsmod "cosmossdk.io/errors"
|
|
|
|
"github.com/cosmos/cosmos-sdk/telemetry"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/keeper"
|
|
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
|
"github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
|
|
)
|
|
|
|
type msgServer struct {
|
|
keeper.AccountKeeper
|
|
types.BankKeeper
|
|
}
|
|
|
|
// NewMsgServerImpl returns an implementation of the vesting MsgServer interface,
|
|
// wrapping the corresponding AccountKeeper and BankKeeper.
|
|
func NewMsgServerImpl(k keeper.AccountKeeper, bk types.BankKeeper) types.MsgServer {
|
|
return &msgServer{AccountKeeper: k, BankKeeper: bk}
|
|
}
|
|
|
|
var _ types.MsgServer = msgServer{}
|
|
|
|
func (s msgServer) CreateVestingAccount(goCtx context.Context, msg *types.MsgCreateVestingAccount) (*types.MsgCreateVestingAccountResponse, error) {
|
|
from, err := s.AccountKeeper.StringToBytes(msg.FromAddress)
|
|
if err != nil {
|
|
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
|
|
}
|
|
|
|
to, err := s.AccountKeeper.StringToBytes(msg.ToAddress)
|
|
if err != nil {
|
|
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
|
|
}
|
|
|
|
if err := validateAmount(msg.Amount); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if msg.EndTime <= 0 {
|
|
return nil, errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid end time")
|
|
}
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
if err := s.BankKeeper.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if s.BankKeeper.BlockedAddr(to) {
|
|
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
|
|
}
|
|
|
|
if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
|
|
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
|
|
}
|
|
|
|
baseAccount := authtypes.NewBaseAccountWithAddress(to)
|
|
baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
|
|
baseVestingAccount := types.NewBaseVestingAccount(baseAccount, msg.Amount.Sort(), msg.EndTime)
|
|
|
|
var vestingAccount sdk.AccountI
|
|
if msg.Delayed {
|
|
vestingAccount = types.NewDelayedVestingAccountRaw(baseVestingAccount)
|
|
} else {
|
|
vestingAccount = types.NewContinuousVestingAccountRaw(baseVestingAccount, ctx.BlockTime().Unix())
|
|
}
|
|
|
|
s.AccountKeeper.SetAccount(ctx, vestingAccount)
|
|
|
|
defer func() {
|
|
telemetry.IncrCounter(1, "new", "account")
|
|
|
|
for _, a := range msg.Amount {
|
|
if a.Amount.IsInt64() {
|
|
telemetry.SetGaugeWithLabels(
|
|
[]string{"tx", "msg", "create_vesting_account"},
|
|
float32(a.Amount.Int64()),
|
|
[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
|
|
)
|
|
}
|
|
}
|
|
}()
|
|
|
|
if err = s.BankKeeper.SendCoins(ctx, from, to, msg.Amount); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.MsgCreateVestingAccountResponse{}, nil
|
|
}
|
|
|
|
func (s msgServer) CreatePermanentLockedAccount(goCtx context.Context, msg *types.MsgCreatePermanentLockedAccount) (*types.MsgCreatePermanentLockedAccountResponse, error) {
|
|
from, err := s.AccountKeeper.StringToBytes(msg.FromAddress)
|
|
if err != nil {
|
|
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
|
|
}
|
|
|
|
to, err := s.AccountKeeper.StringToBytes(msg.ToAddress)
|
|
if err != nil {
|
|
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
|
|
}
|
|
|
|
if err := validateAmount(msg.Amount); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
if err := s.BankKeeper.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if s.BankKeeper.BlockedAddr(to) {
|
|
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
|
|
}
|
|
|
|
if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
|
|
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
|
|
}
|
|
|
|
baseAccount := authtypes.NewBaseAccountWithAddress(to)
|
|
baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
|
|
vestingAccount := types.NewPermanentLockedAccount(baseAccount, msg.Amount)
|
|
|
|
s.AccountKeeper.SetAccount(ctx, vestingAccount)
|
|
|
|
defer func() {
|
|
telemetry.IncrCounter(1, "new", "account")
|
|
|
|
for _, a := range msg.Amount {
|
|
if a.Amount.IsInt64() {
|
|
telemetry.SetGaugeWithLabels(
|
|
[]string{"tx", "msg", "create_permanent_locked_account"},
|
|
float32(a.Amount.Int64()),
|
|
[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
|
|
)
|
|
}
|
|
}
|
|
}()
|
|
|
|
if err = s.BankKeeper.SendCoins(ctx, from, to, msg.Amount); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.MsgCreatePermanentLockedAccountResponse{}, nil
|
|
}
|
|
|
|
func (s msgServer) CreatePeriodicVestingAccount(goCtx context.Context, msg *types.MsgCreatePeriodicVestingAccount) (*types.MsgCreatePeriodicVestingAccountResponse, error) {
|
|
from, err := s.AccountKeeper.StringToBytes(msg.FromAddress)
|
|
if err != nil {
|
|
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
|
|
}
|
|
|
|
to, err := s.AccountKeeper.StringToBytes(msg.ToAddress)
|
|
if err != nil {
|
|
return nil, sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
|
|
}
|
|
|
|
if msg.StartTime < 1 {
|
|
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid start time of %d, length must be greater than 0", msg.StartTime)
|
|
}
|
|
|
|
var totalCoins sdk.Coins
|
|
for i, period := range msg.VestingPeriods {
|
|
if period.Length < 1 {
|
|
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "invalid period length of %d in period %d, length must be greater than 0", period.Length, i)
|
|
}
|
|
|
|
totalCoins = totalCoins.Add(period.Amount...)
|
|
}
|
|
|
|
ctx := sdk.UnwrapSDKContext(goCtx)
|
|
if acc := s.AccountKeeper.GetAccount(ctx, to); acc != nil {
|
|
return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
|
|
}
|
|
|
|
if err := s.BankKeeper.IsSendEnabledCoins(ctx, totalCoins...); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
baseAccount := authtypes.NewBaseAccountWithAddress(to)
|
|
baseAccount = s.AccountKeeper.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
|
|
vestingAccount := types.NewPeriodicVestingAccount(baseAccount, totalCoins.Sort(), msg.StartTime, msg.VestingPeriods)
|
|
|
|
s.AccountKeeper.SetAccount(ctx, vestingAccount)
|
|
|
|
defer func() {
|
|
telemetry.IncrCounter(1, "new", "account")
|
|
|
|
for _, a := range totalCoins {
|
|
if a.Amount.IsInt64() {
|
|
telemetry.SetGaugeWithLabels(
|
|
[]string{"tx", "msg", "create_periodic_vesting_account"},
|
|
float32(a.Amount.Int64()),
|
|
[]metrics.Label{telemetry.NewLabel("denom", a.Denom)},
|
|
)
|
|
}
|
|
}
|
|
}()
|
|
|
|
if err = s.BankKeeper.SendCoins(ctx, from, to, totalCoins); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &types.MsgCreatePeriodicVestingAccountResponse{}, nil
|
|
}
|
|
|
|
func validateAmount(amount sdk.Coins) error {
|
|
if !amount.IsValid() {
|
|
return sdkerrors.ErrInvalidCoins.Wrap(amount.String())
|
|
}
|
|
|
|
if !amount.IsAllPositive() {
|
|
return sdkerrors.ErrInvalidCoins.Wrap(amount.String())
|
|
}
|
|
|
|
return nil
|
|
}
|