Skip to content

Commit

Permalink
feat(cliff): register cliff vesting account msg
Browse files Browse the repository at this point in the history
  • Loading branch information
bdeneux committed Oct 4, 2022
1 parent 7493de9 commit 9106919
Show file tree
Hide file tree
Showing 10 changed files with 760 additions and 51 deletions.
24 changes: 23 additions & 1 deletion proto/vesting/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ service Msg {
//
// Since: cosmos-sdk 0.46
rpc CreatePeriodicVestingAccount(MsgCreatePeriodicVestingAccount) returns (MsgCreatePeriodicVestingAccountResponse);
// CreateCliffVestingAccount defines a method that enables creating a cliff vesting
// account.
rpc CreateCliffVestingAccount(MsgCreateCliffVestingAccount) returns (MsgCreateCliffVestingAccountResponse);
}

// MsgCreateVestingAccount defines a message that enables creating a vesting
Expand All @@ -40,7 +43,7 @@ message MsgCreateVestingAccount {
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];

int64 end_time = 4;
bool delayed = 5;
bool delayed = 6;
}

// MsgCreateVestingAccountResponse defines the Msg/CreateVestingAccount response type.
Expand Down Expand Up @@ -84,3 +87,22 @@ message MsgCreatePeriodicVestingAccount {
//
// Since: cosmos-sdk 0.46
message MsgCreatePeriodicVestingAccountResponse {}

// MsgCreateCliffVestingAccount defines a message that enables creating a cliff vesting
// account.
message MsgCreateCliffVestingAccount {
option (cosmos.msg.v1.signer) = "from_address";

option (gogoproto.equal) = true;

string from_address = 1;
string to_address = 2;
repeated cosmos.base.v1beta1.Coin amount = 3
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];

int64 end_time = 4;
int64 cliff_time = 5;
}

// MsgCreateCliffVestingAccountResponse defines the Msg/CreateVestingAccount response type.
message MsgCreateCliffVestingAccountResponse {}
2 changes: 1 addition & 1 deletion x/vesting/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
"github.com/okp4/okp4d/x/vesting/types"
)

// Transaction command flags
Expand Down
2 changes: 1 addition & 1 deletion x/vesting/client/testutil/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/network"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/vesting/client/cli"
"github.com/okp4/okp4d/x/vesting/client/cli"
)

type IntegrationTestSuite struct {
Expand Down
4 changes: 2 additions & 2 deletions x/vesting/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth/keeper"
"github.com/cosmos/cosmos-sdk/x/auth/vesting/client/cli"
"github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
"github.com/okp4/okp4d/x/vesting/client/cli"
"github.com/okp4/okp4d/x/vesting/types"
)

var (
Expand Down
65 changes: 64 additions & 1 deletion x/vesting/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
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"
"github.com/okp4/okp4d/x/vesting/types"
)

type msgServer struct {
Expand Down Expand Up @@ -213,3 +213,66 @@ func (s msgServer) CreatePeriodicVestingAccount(goCtx context.Context, msg *type
)
return &types.MsgCreatePeriodicVestingAccountResponse{}, nil
}

func (s msgServer) CreateCliffVestingAccount(goCtx context.Context, msg *types.MsgCreateCliffVestingAccount) (*types.MsgCreateCliffVestingAccountResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
ak := s.AccountKeeper
bk := s.BankKeeper

if err := bk.IsSendEnabledCoins(ctx, msg.Amount...); err != nil {
return nil, err
}

from, err := sdk.AccAddressFromBech32(msg.FromAddress)
if err != nil {
return nil, err
}
to, err := sdk.AccAddressFromBech32(msg.ToAddress)
if err != nil {
return nil, err
}

if bk.BlockedAddr(to) {
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", msg.ToAddress)
}

if acc := ak.GetAccount(ctx, to); acc != nil {
return nil, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "account %s already exists", msg.ToAddress)
}

baseAccount := authtypes.NewBaseAccountWithAddress(to)
baseAccount = ak.NewAccount(ctx, baseAccount).(*authtypes.BaseAccount)
baseVestingAccount := types.NewBaseVestingAccount(baseAccount, msg.Amount.Sort(), msg.EndTime)

vestingAccount := types.NewCliffVestingAccountRaw(baseVestingAccount, ctx.BlockTime().Unix(), msg.CliffTime)

ak.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)},
)
}
}
}()

err = bk.SendCoins(ctx, from, to, msg.Amount)
if err != nil {
return nil, err
}

ctx.EventManager().EmitEvent(
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
),
)

return &types.MsgCreateCliffVestingAccountResponse{}, nil
}
5 changes: 3 additions & 2 deletions x/vesting/types/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/msgservice"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/auth/vesting/exported"
"github.com/okp4/okp4d/x/vesting/exported"
)

// RegisterLegacyAminoCodec registers the vesting interfaces and concrete types on the
// provided LegacyAmino codec. These types are used for Amino JSON serialization
func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
cdc.RegisterInterface((*exported.VestingAccount)(nil), nil)
cdc.RegisterConcrete(&BaseVestingAccount{}, "cosmos-sdk/BaseVestingAccount", nil)
cdc.RegisterConcrete(&CliffVestingAccount{}, "cosmos-sdk/CliffVestingAccount", nil)
cdc.RegisterConcrete(&ContinuousVestingAccount{}, "cosmos-sdk/ContinuousVestingAccount", nil)
cdc.RegisterConcrete(&DelayedVestingAccount{}, "cosmos-sdk/DelayedVestingAccount", nil)
cdc.RegisterConcrete(&PeriodicVestingAccount{}, "cosmos-sdk/PeriodicVestingAccount", nil)
Expand All @@ -28,7 +29,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
// Interfaces and creates a registry of it's concrete implementations
func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterInterface(
"cosmos.vesting.v1beta1.VestingAccount",
"vesting.v1beta1.VestingAccount",
(*exported.VestingAccount)(nil),
&ContinuousVestingAccount{},
&DelayedVestingAccount{},
Expand Down
63 changes: 63 additions & 0 deletions x/vesting/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ const TypeMsgCreatePermanentLockedAccount = "msg_create_permanent_locked_account
// TypeMsgCreatePeriodicVestingAccount defines the type value for a MsgCreateVestingAccount.
const TypeMsgCreatePeriodicVestingAccount = "msg_create_periodic_vesting_account"

// TypeMsgCreateCliffVestingAccount defines the type value for a MsgCreateCliffVestingAccount.
const TypeMsgCreateCliffVestingAccount = "msg_create_cliff_vesting_account"

var _ sdk.Msg = &MsgCreateVestingAccount{}

var _ sdk.Msg = &MsgCreatePermanentLockedAccount{}

var _ sdk.Msg = &MsgCreatePeriodicVestingAccount{}

var _ sdk.Msg = &MsgCreateCliffVestingAccount{}

// NewMsgCreateVestingAccount returns a reference to a new MsgCreateVestingAccount.
//
//nolint:interfacer
func NewMsgCreateVestingAccount(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins, endTime int64, delayed bool) *MsgCreateVestingAccount {
return &MsgCreateVestingAccount{
Expand Down Expand Up @@ -77,6 +83,7 @@ func (msg MsgCreateVestingAccount) GetSigners() []sdk.AccAddress {
}

// NewMsgCreatePermanentLockedAccount returns a reference to a new MsgCreatePermanentLockedAccount.
//
//nolint:interfacer
func NewMsgCreatePermanentLockedAccount(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins) *MsgCreatePermanentLockedAccount {
return &MsgCreatePermanentLockedAccount{
Expand Down Expand Up @@ -125,6 +132,7 @@ func (msg MsgCreatePermanentLockedAccount) GetSigners() []sdk.AccAddress {
}

// NewMsgCreatePeriodicVestingAccount returns a reference to a new MsgCreatePeriodicVestingAccount.
//
//nolint:interfacer
func NewMsgCreatePeriodicVestingAccount(fromAddr, toAddr sdk.AccAddress, startTime int64, periods []Period) *MsgCreatePeriodicVestingAccount {
return &MsgCreatePeriodicVestingAccount{
Expand Down Expand Up @@ -186,3 +194,58 @@ func (msg MsgCreatePeriodicVestingAccount) ValidateBasic() error {

return nil
}

// NewMsgCreateCliffVestingAccount returns a reference to a new MsgCreateCliffVestingAccount.
//
//nolint:interfacer
func NewMsgCreateCliffVestingAccount(fromAddr, toAddr sdk.AccAddress, amount sdk.Coins, endTime, cliffTime int64) *MsgCreateCliffVestingAccount {
return &MsgCreateCliffVestingAccount{
FromAddress: fromAddr.String(),
ToAddress: toAddr.String(),
Amount: amount,
EndTime: endTime,
CliffTime: cliffTime,
}
}

// Route returns the message route for a MsgCreateVestingAccount.
func (msg MsgCreateCliffVestingAccount) Route() string { return RouterKey }

// Type returns the message type for a MsgCreateVestingAccount.
func (msg MsgCreateCliffVestingAccount) Type() string { return TypeMsgCreateVestingAccount }

// ValidateBasic Implements Msg.
func (msg MsgCreateCliffVestingAccount) ValidateBasic() error {
if _, err := sdk.AccAddressFromBech32(msg.FromAddress); err != nil {
return sdkerrors.ErrInvalidAddress.Wrapf("invalid 'from' address: %s", err)
}
if _, err := sdk.AccAddressFromBech32(msg.ToAddress); err != nil {
return sdkerrors.ErrInvalidAddress.Wrapf("invalid 'to' address: %s", err)
}

if !msg.Amount.IsValid() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String())
}

if !msg.Amount.IsAllPositive() {
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String())
}

if msg.EndTime <= 0 {
return sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, "invalid end time")
}

return nil
}

// GetSignBytes returns the bytes all expected signers must sign over for a
// MsgCreateVestingAccount.
func (msg MsgCreateCliffVestingAccount) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg))
}

// GetSigners returns the expected signers for a MsgCreateVestingAccount.
func (msg MsgCreateCliffVestingAccount) GetSigners() []sdk.AccAddress {
addr, _ := sdk.AccAddressFromBech32(msg.FromAddress)
return []sdk.AccAddress{addr}
}
Loading

0 comments on commit 9106919

Please sign in to comment.