From 70d431b5c81c4f9e4892d3159ea777181d19c0fc Mon Sep 17 00:00:00 2001 From: Denali Marsh Date: Mon, 28 Mar 2022 13:53:42 +0200 Subject: [PATCH] Savings module withdrawals (#1197) * proto types * updated types, generated protobuf types * add to client * add withdraw keeper methods + test * more withdraw keeper test cases * revisions --- proto/kava/savings/v1beta1/tx.proto | 19 +- x/savings/client/cli/tx.go | 29 +- x/savings/keeper/msg_server.go | 25 +- x/savings/keeper/withdraw.go | 63 ++++ x/savings/keeper/withdraw_test.go | 157 ++++++++++ x/savings/types/codec.go | 1 + x/savings/types/errors.go | 8 +- x/savings/types/events.go | 3 +- x/savings/types/msg.go | 43 +++ x/savings/types/tx.pb.go | 437 ++++++++++++++++++++++++++-- 10 files changed, 756 insertions(+), 29 deletions(-) create mode 100644 x/savings/keeper/withdraw.go create mode 100644 x/savings/keeper/withdraw_test.go diff --git a/proto/kava/savings/v1beta1/tx.proto b/proto/kava/savings/v1beta1/tx.proto index c12a5624..ac0102ce 100644 --- a/proto/kava/savings/v1beta1/tx.proto +++ b/proto/kava/savings/v1beta1/tx.proto @@ -9,8 +9,13 @@ option go_package = "github.com/kava-labs/kava/x/savings/types"; // Msg defines the savings Msg service. service Msg { - // Deposit defines a method for depositing funds to the savings module account - rpc Deposit(MsgDeposit) returns (MsgDepositResponse); + + // Deposit defines a method for depositing funds to the savings module account + rpc Deposit(MsgDeposit) returns (MsgDepositResponse); + + // Withdraw defines a method for withdrawing funds to the savings module account + rpc Withdraw(MsgWithdraw) returns (MsgWithdrawResponse); + } // MsgDeposit defines the Msg/Deposit request type. @@ -22,3 +27,13 @@ message MsgDeposit { // MsgDepositResponse defines the Msg/Deposit response type. message MsgDepositResponse {} + +// MsgWithdraw defines the Msg/Withdraw request type. +message MsgWithdraw { + string depositor = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + repeated cosmos.base.v1beta1.Coin amount = 2 + [(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", (gogoproto.nullable) = false]; + } + + // MsgWithdrawResponse defines the Msg/Withdraw response type. + message MsgWithdrawResponse {} diff --git a/x/savings/client/cli/tx.go b/x/savings/client/cli/tx.go index c7a62380..469a9936 100644 --- a/x/savings/client/cli/tx.go +++ b/x/savings/client/cli/tx.go @@ -26,6 +26,7 @@ func GetTxCmd() *cobra.Command { cmds := []*cobra.Command{ getCmdDeposit(), + getCmdWithdraw(), } for _, cmd := range cmds { @@ -42,7 +43,7 @@ func getCmdDeposit() *cobra.Command { Use: "deposit [amount]", Short: "deposit coins to savings", Example: fmt.Sprintf( - `%s tx %s savings 10000000bnb --from `, version.AppName, types.ModuleName, + `%s tx %s deposit 10000000ukava,100000000usdx --from `, version.AppName, types.ModuleName, ), RunE: func(cmd *cobra.Command, args []string) error { clientCtx, err := client.GetClientTxContext(cmd) @@ -62,3 +63,29 @@ func getCmdDeposit() *cobra.Command { }, } } + +func getCmdWithdraw() *cobra.Command { + return &cobra.Command{ + Use: "withdraw [amount]", + Short: "withdraw coins from savings", + Example: fmt.Sprintf( + `%s tx %s withdraw 10000000ukava,100000000usdx --from `, version.AppName, types.ModuleName, + ), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + amount, err := sdk.ParseCoinsNormalized(args[0]) + if err != nil { + return err + } + msg := types.NewMsgWithdraw(clientCtx.GetFromAddress(), amount) + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), &msg) + }, + } +} diff --git a/x/savings/keeper/msg_server.go b/x/savings/keeper/msg_server.go index 0e88700d..1f081a66 100644 --- a/x/savings/keeper/msg_server.go +++ b/x/savings/keeper/msg_server.go @@ -12,7 +12,7 @@ type msgServer struct { keeper Keeper } -// NewMsgServerImpl returns an implementation of the hard MsgServer interface +// NewMsgServerImpl returns an implementation of the savings MsgServer interface // for the provided Keeper. func NewMsgServerImpl(keeper Keeper) types.MsgServer { return &msgServer{keeper: keeper} @@ -42,3 +42,26 @@ func (k msgServer) Deposit(goCtx context.Context, msg *types.MsgDeposit) (*types ) return &types.MsgDepositResponse{}, nil } + +func (k msgServer) Withdraw(goCtx context.Context, msg *types.MsgWithdraw) (*types.MsgWithdrawResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + depositor, err := sdk.AccAddressFromBech32(msg.Depositor) + if err != nil { + return nil, err + } + + err = k.keeper.Withdraw(ctx, depositor, msg.Amount) + if err != nil { + return nil, err + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, msg.Depositor), + ), + ) + return &types.MsgWithdrawResponse{}, nil +} diff --git a/x/savings/keeper/withdraw.go b/x/savings/keeper/withdraw.go new file mode 100644 index 00000000..311ba84a --- /dev/null +++ b/x/savings/keeper/withdraw.go @@ -0,0 +1,63 @@ +package keeper + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "github.com/kava-labs/kava/x/savings/types" +) + +// Withdraw returns some or all of a deposit back to original depositor +func (k Keeper) Withdraw(ctx sdk.Context, depositor sdk.AccAddress, coins sdk.Coins) error { + deposit, found := k.GetDeposit(ctx, depositor) + if !found { + return sdkerrors.Wrap(types.ErrNoDepositFound, fmt.Sprintf(" for address: %s", depositor.String())) + } + + amount, err := k.CalculateWithdrawAmount(deposit.Amount, coins) + if err != nil { + return err + } + + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleAccountName, depositor, amount) + if err != nil { + return err + } + + deposit.Amount = deposit.Amount.Sub(amount) + if deposit.Amount.Empty() { + k.DeleteDeposit(ctx, deposit) + } else { + k.SetDeposit(ctx, deposit) + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeSavingsWithdrawal, + sdk.NewAttribute(sdk.AttributeKeyAmount, amount.String()), + sdk.NewAttribute(types.AttributeKeyDepositor, depositor.String()), + ), + ) + return nil +} + +// CalculateWithdrawAmount enables full withdraw of deposited coins by adjusting withdraw amount +// to equal total deposit amount if the requested withdraw amount > current deposit amount +func (k Keeper) CalculateWithdrawAmount(available sdk.Coins, request sdk.Coins) (sdk.Coins, error) { + result := sdk.Coins{} + + if !request.DenomsSubsetOf(available) { + return result, types.ErrInvalidWithdrawDenom + } + + for _, coin := range request { + if coin.Amount.GT(available.AmountOf(coin.Denom)) { + result = append(result, sdk.NewCoin(coin.Denom, available.AmountOf(coin.Denom))) + } else { + result = append(result, coin) + } + } + return result, nil +} diff --git a/x/savings/keeper/withdraw_test.go b/x/savings/keeper/withdraw_test.go new file mode 100644 index 00000000..fd9250f4 --- /dev/null +++ b/x/savings/keeper/withdraw_test.go @@ -0,0 +1,157 @@ +package keeper_test + +import ( + "strings" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/tendermint/tendermint/crypto" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" + + "github.com/kava-labs/kava/app" + "github.com/kava-labs/kava/x/savings/types" +) + +func (suite *KeeperTestSuite) TestWithdraw() { + type args struct { + allowedDenoms []string + depositor sdk.AccAddress + initialDepositorBalance sdk.Coins + depositAmount sdk.Coins + withdrawAmount sdk.Coins + expectedAccountBalance sdk.Coins + expectedModAccountBalance sdk.Coins + expectedDepositCoins sdk.Coins + } + type errArgs struct { + expectPass bool + expectDelete bool + contains string + } + type withdrawTest struct { + name string + args args + errArgs errArgs + } + testCases := []withdrawTest{ + { + "valid: partial withdraw", + args{ + allowedDenoms: []string{"bnb", "btcb", "ukava"}, + depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))), + initialDepositorBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))), + depositAmount: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(200))), + withdrawAmount: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(100))), + expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(900)), sdk.NewCoin("btcb", sdk.NewInt(1000))), + expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(100))), + expectedDepositCoins: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(100))), + }, + errArgs{ + expectPass: true, + expectDelete: false, + contains: "", + }, + }, + { + "valid: full withdraw", + args{ + allowedDenoms: []string{"bnb", "btcb", "ukava"}, + initialDepositorBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))), + depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))), + depositAmount: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(200))), + withdrawAmount: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(200))), + expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))), + expectedModAccountBalance: sdk.Coins{}, + expectedDepositCoins: sdk.Coins{}, + }, + errArgs{ + expectPass: true, + expectDelete: true, + contains: "", + }, + }, + { + "valid: withdraw exceeds deposit but is adjusted to match max deposit", + args{ + allowedDenoms: []string{"bnb", "btcb", "ukava"}, + initialDepositorBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))), + depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))), + depositAmount: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(200))), + withdrawAmount: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(300))), + expectedAccountBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))), + expectedModAccountBalance: sdk.Coins{}, + expectedDepositCoins: sdk.Coins{}, + }, + errArgs{ + expectPass: true, + expectDelete: true, + contains: "", + }, + }, + { + "invalid: withdraw non-supplied coin type", + args{ + allowedDenoms: []string{"bnb", "btcb", "ukava"}, + depositor: sdk.AccAddress(crypto.AddressHash([]byte("test"))), + initialDepositorBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(1000)), sdk.NewCoin("btcb", sdk.NewInt(1000))), + depositAmount: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(200))), + withdrawAmount: sdk.NewCoins(sdk.NewCoin("btcb", sdk.NewInt(200))), + expectedAccountBalance: sdk.Coins{}, + expectedModAccountBalance: sdk.NewCoins(sdk.NewCoin("bnb", sdk.NewInt(200))), + expectedDepositCoins: sdk.Coins{}, + }, + errArgs{ + expectPass: false, + expectDelete: false, + contains: "invalid withdraw denom", + }, + }, + } + for _, tc := range testCases { + suite.Run(tc.name, func() { + // Initialize test app and set context + tApp := app.NewTestApp() + ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()}) + authGS := app.NewFundedGenStateWithCoins( + tApp.AppCodec(), + []sdk.Coins{tc.args.initialDepositorBalance}, + []sdk.AccAddress{tc.args.depositor}, + ) + savingsGS := types.NewGenesisState(types.NewParams(tc.args.allowedDenoms)) + + tApp.InitializeFromGenesisStates(authGS, + app.GenesisState{types.ModuleName: tApp.AppCodec().MustMarshalJSON(&savingsGS)}, + ) + keeper := tApp.GetSavingsKeeper() + suite.app = tApp + suite.ctx = ctx + suite.keeper = keeper + bankKeeper := tApp.GetBankKeeper() + + err := suite.keeper.Deposit(suite.ctx, tc.args.depositor, tc.args.depositAmount) + suite.Require().NoError(err) + + err = suite.keeper.Withdraw(suite.ctx, tc.args.depositor, tc.args.withdrawAmount) + if tc.errArgs.expectPass { + suite.Require().NoError(err) + // Check depositor's account balance + acc := suite.getAccount(tc.args.depositor) + suite.Require().Equal(tc.args.expectedAccountBalance, bankKeeper.GetAllBalances(ctx, acc.GetAddress())) + // Check savings module account balance + mAcc := suite.getModuleAccount(types.ModuleAccountName) + suite.Require().True(tc.args.expectedModAccountBalance.IsEqual(bankKeeper.GetAllBalances(ctx, mAcc.GetAddress()))) + // Check deposit + testDeposit, f := suite.keeper.GetDeposit(suite.ctx, tc.args.depositor) + if tc.errArgs.expectDelete { + suite.Require().False(f) + } else { + suite.Require().True(f) + suite.Require().Equal(tc.args.expectedDepositCoins, testDeposit.Amount) + } + } else { + suite.Require().Error(err) + suite.Require().True(strings.Contains(err.Error(), tc.errArgs.contains)) + } + }) + } +} diff --git a/x/savings/types/codec.go b/x/savings/types/codec.go index 3fa5061f..6daa7252 100644 --- a/x/savings/types/codec.go +++ b/x/savings/types/codec.go @@ -18,6 +18,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { func RegisterInterfaces(registry types.InterfaceRegistry) { registry.RegisterImplementations((*sdk.Msg)(nil), &MsgDeposit{}, + &MsgWithdraw{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) diff --git a/x/savings/types/errors.go b/x/savings/types/errors.go index 84403561..70c75d24 100644 --- a/x/savings/types/errors.go +++ b/x/savings/types/errors.go @@ -9,6 +9,10 @@ import ( var ( // ErrEmptyInput error for empty input ErrEmptyInput = sdkerrors.Register(ModuleName, 2, "input must not be empty") - // ErrInvalidDepositDenom error for invalid deposit denoms - ErrInvalidDepositDenom = sdkerrors.Register(ModuleName, 3, "invalid deposit denom") + // ErrNoDepositFound error when no deposit is found for an address + ErrNoDepositFound = sdkerrors.Register(ModuleName, 3, "no deposit found") + // ErrInvalidDepositDenom error for invalid deposit denom + ErrInvalidDepositDenom = sdkerrors.Register(ModuleName, 4, "invalid deposit denom") + // ErrInvalidWithdrawDenom error for invalid withdraw denoms + ErrInvalidWithdrawDenom = sdkerrors.Register(ModuleName, 5, "invalid withdraw denom") ) diff --git a/x/savings/types/events.go b/x/savings/types/events.go index 69fc5b75..2f30ebe1 100644 --- a/x/savings/types/events.go +++ b/x/savings/types/events.go @@ -1,7 +1,8 @@ package types const ( - EventTypeSavingsDeposit = "deposit_savings" + EventTypeSavingsDeposit = "deposit_savings" + EventTypeSavingsWithdrawal = "withdraw_savings" AttributeValueCategory = ModuleName AttributeKeyAmount = "amount" diff --git a/x/savings/types/msg.go b/x/savings/types/msg.go index d6513905..1cc88895 100644 --- a/x/savings/types/msg.go +++ b/x/savings/types/msg.go @@ -8,6 +8,7 @@ import ( // ensure Msg interface compliance at compile time var ( _ sdk.Msg = &MsgDeposit{} + _ sdk.Msg = &MsgWithdraw{} ) // NewMsgDeposit returns a new MsgDeposit @@ -51,3 +52,45 @@ func (msg MsgDeposit) GetSigners() []sdk.AccAddress { } return []sdk.AccAddress{depositor} } + +// NewMsgWithdraw returns a new MsgWithdraw +func NewMsgWithdraw(depositor sdk.AccAddress, amount sdk.Coins) MsgWithdraw { + return MsgWithdraw{ + Depositor: depositor.String(), + Amount: amount, + } +} + +// Route return the message type used for routing the message. +func (msg MsgWithdraw) Route() string { return RouterKey } + +// Type returns a human-readable string for the message, intended for utilization within tags. +func (msg MsgWithdraw) Type() string { return "savings_withdraw" } + +// ValidateBasic does a simple validation check that doesn't require access to any other information. +func (msg MsgWithdraw) ValidateBasic() error { + _, err := sdk.AccAddressFromBech32(msg.Depositor) + if err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error()) + } + + if !msg.Amount.IsValid() || msg.Amount.IsZero() { + return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "withdraw amount %s", msg.Amount) + } + return nil +} + +// GetSignBytes gets the canonical byte representation of the Msg. +func (msg MsgWithdraw) GetSignBytes() []byte { + bz := ModuleCdc.MustMarshalJSON(&msg) + return sdk.MustSortJSON(bz) +} + +// GetSigners returns the addresses of signers that must sign. +func (msg MsgWithdraw) GetSigners() []sdk.AccAddress { + depositor, err := sdk.AccAddressFromBech32(msg.Depositor) + if err != nil { + panic(err) + } + return []sdk.AccAddress{depositor} +} diff --git a/x/savings/types/tx.pb.go b/x/savings/types/tx.pb.go index 0f4dc36a..2ccba38b 100644 --- a/x/savings/types/tx.pb.go +++ b/x/savings/types/tx.pb.go @@ -121,36 +121,130 @@ func (m *MsgDepositResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgDepositResponse proto.InternalMessageInfo +// MsgWithdraw defines the Msg/Withdraw request type. +type MsgWithdraw struct { + Depositor string `protobuf:"bytes,1,opt,name=depositor,proto3" json:"depositor,omitempty"` + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *MsgWithdraw) Reset() { *m = MsgWithdraw{} } +func (m *MsgWithdraw) String() string { return proto.CompactTextString(m) } +func (*MsgWithdraw) ProtoMessage() {} +func (*MsgWithdraw) Descriptor() ([]byte, []int) { + return fileDescriptor_c0bf8679b144267a, []int{2} +} +func (m *MsgWithdraw) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdraw) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdraw.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgWithdraw) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdraw.Merge(m, src) +} +func (m *MsgWithdraw) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdraw) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdraw.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdraw proto.InternalMessageInfo + +func (m *MsgWithdraw) GetDepositor() string { + if m != nil { + return m.Depositor + } + return "" +} + +func (m *MsgWithdraw) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +// MsgWithdrawResponse defines the Msg/Withdraw response type. +type MsgWithdrawResponse struct { +} + +func (m *MsgWithdrawResponse) Reset() { *m = MsgWithdrawResponse{} } +func (m *MsgWithdrawResponse) String() string { return proto.CompactTextString(m) } +func (*MsgWithdrawResponse) ProtoMessage() {} +func (*MsgWithdrawResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_c0bf8679b144267a, []int{3} +} +func (m *MsgWithdrawResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgWithdrawResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgWithdrawResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgWithdrawResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgWithdrawResponse.Merge(m, src) +} +func (m *MsgWithdrawResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgWithdrawResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgWithdrawResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgWithdrawResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgDeposit)(nil), "kava.savings.v1beta1.MsgDeposit") proto.RegisterType((*MsgDepositResponse)(nil), "kava.savings.v1beta1.MsgDepositResponse") + proto.RegisterType((*MsgWithdraw)(nil), "kava.savings.v1beta1.MsgWithdraw") + proto.RegisterType((*MsgWithdrawResponse)(nil), "kava.savings.v1beta1.MsgWithdrawResponse") } func init() { proto.RegisterFile("kava/savings/v1beta1/tx.proto", fileDescriptor_c0bf8679b144267a) } var fileDescriptor_c0bf8679b144267a = []byte{ - // 324 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0xc1, 0x4e, 0xf2, 0x40, - 0x14, 0x85, 0xdb, 0x9f, 0x84, 0x3f, 0x8c, 0xbb, 0xa6, 0x0b, 0x20, 0x71, 0x20, 0xac, 0xea, 0x82, - 0x19, 0xc1, 0xc4, 0xbd, 0xe0, 0x96, 0x0d, 0xc6, 0x8d, 0x31, 0x31, 0xd3, 0x76, 0x32, 0x36, 0x48, - 0x6f, 0xd3, 0x3b, 0x10, 0x7c, 0x0b, 0x5f, 0x43, 0xd7, 0x3e, 0x04, 0x4b, 0xe2, 0xca, 0x95, 0x1a, - 0x78, 0x11, 0xd3, 0xce, 0x40, 0x5d, 0x98, 0xb8, 0xea, 0x6d, 0xbe, 0x73, 0x4e, 0xce, 0xbd, 0x43, - 0x8e, 0x67, 0x62, 0x29, 0x38, 0x8a, 0x65, 0x92, 0x2a, 0xe4, 0xcb, 0x41, 0x28, 0xb5, 0x18, 0x70, - 0xbd, 0x62, 0x59, 0x0e, 0x1a, 0x3c, 0xbf, 0xc0, 0xcc, 0x62, 0x66, 0x71, 0xdb, 0x57, 0xa0, 0xa0, - 0x14, 0xf0, 0x62, 0x32, 0xda, 0x36, 0x8d, 0x00, 0xe7, 0x80, 0x3c, 0x14, 0x28, 0x0f, 0x49, 0x11, - 0x24, 0xa9, 0xe5, 0x2d, 0xc3, 0xef, 0x8c, 0xd1, 0xfc, 0x18, 0xd4, 0x7b, 0x76, 0x09, 0x99, 0xa0, - 0xba, 0x94, 0x19, 0x60, 0xa2, 0xbd, 0x73, 0xd2, 0x88, 0xcd, 0x08, 0x79, 0xd3, 0xed, 0xba, 0x41, - 0x63, 0xd4, 0x7c, 0x7b, 0xed, 0xfb, 0xd6, 0x73, 0x11, 0xc7, 0xb9, 0x44, 0xbc, 0xd2, 0x79, 0x92, - 0xaa, 0x69, 0x25, 0xf5, 0x22, 0x52, 0x17, 0x73, 0x58, 0xa4, 0xba, 0xf9, 0xaf, 0x5b, 0x0b, 0x8e, - 0x86, 0x2d, 0x66, 0x1d, 0x45, 0xa5, 0x7d, 0x7b, 0x36, 0x86, 0x24, 0x1d, 0x9d, 0xae, 0x3f, 0x3a, - 0xce, 0xcb, 0x67, 0x27, 0x50, 0x89, 0xbe, 0x5f, 0x84, 0x2c, 0x82, 0xb9, 0xad, 0x64, 0x3f, 0x7d, - 0x8c, 0x67, 0x5c, 0x3f, 0x66, 0x12, 0x4b, 0x03, 0x4e, 0x6d, 0x74, 0xcf, 0x27, 0x5e, 0x55, 0x75, - 0x2a, 0x31, 0x83, 0x14, 0xe5, 0xf0, 0x96, 0xd4, 0x26, 0xa8, 0xbc, 0x6b, 0xf2, 0x7f, 0xbf, 0x44, - 0x97, 0xfd, 0x76, 0x3b, 0x56, 0x79, 0xdb, 0xc1, 0x5f, 0x8a, 0x7d, 0xfa, 0x68, 0xbc, 0xde, 0x52, - 0x77, 0xb3, 0xa5, 0xee, 0xd7, 0x96, 0xba, 0x4f, 0x3b, 0xea, 0x6c, 0x76, 0xd4, 0x79, 0xdf, 0x51, - 0xe7, 0xe6, 0xe4, 0x47, 0xff, 0x22, 0xad, 0xff, 0x20, 0x42, 0x2c, 0x27, 0xbe, 0x3a, 0x3c, 0x6b, - 0xb9, 0x46, 0x58, 0x2f, 0x6f, 0x7d, 0xf6, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x0e, 0x4c, 0xbf, 0xbb, - 0xf3, 0x01, 0x00, 0x00, + // 367 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x52, 0xcd, 0x4e, 0xea, 0x40, + 0x14, 0xee, 0x5c, 0x12, 0xee, 0x65, 0xd8, 0xf5, 0xd6, 0x04, 0x9a, 0x58, 0x90, 0x55, 0x59, 0x30, + 0x15, 0x4c, 0xdc, 0x0b, 0x6e, 0xd9, 0x60, 0x8c, 0xc6, 0x8d, 0x99, 0xfe, 0x64, 0x98, 0x20, 0x9d, + 0xa6, 0x67, 0x40, 0x7c, 0x0b, 0x5f, 0x43, 0xd6, 0xc6, 0x67, 0x60, 0x49, 0x5c, 0xb9, 0x52, 0x03, + 0x2f, 0x62, 0xda, 0x4e, 0x81, 0x85, 0x86, 0xad, 0xab, 0x9e, 0xe6, 0xfb, 0xc9, 0x77, 0xbe, 0x39, + 0xf8, 0x70, 0x44, 0xa7, 0xd4, 0x01, 0x3a, 0xe5, 0x21, 0x03, 0x67, 0xda, 0x76, 0x03, 0x49, 0xdb, + 0x8e, 0x9c, 0x91, 0x28, 0x16, 0x52, 0xe8, 0x46, 0x02, 0x13, 0x05, 0x13, 0x05, 0x9b, 0x06, 0x13, + 0x4c, 0xa4, 0x04, 0x27, 0x99, 0x32, 0xae, 0x69, 0x79, 0x02, 0xc6, 0x02, 0x1c, 0x97, 0x42, 0xb0, + 0x71, 0xf2, 0x04, 0x0f, 0x15, 0x5e, 0xcd, 0xf0, 0xdb, 0x4c, 0x98, 0xfd, 0x64, 0x50, 0xe3, 0x09, + 0x61, 0xdc, 0x07, 0x76, 0x1e, 0x44, 0x02, 0xb8, 0xd4, 0x4f, 0x71, 0xc9, 0xcf, 0x46, 0x11, 0x57, + 0x50, 0x1d, 0xd9, 0xa5, 0x6e, 0xe5, 0xf5, 0xb9, 0x65, 0x28, 0xcd, 0x99, 0xef, 0xc7, 0x01, 0xc0, + 0x85, 0x8c, 0x79, 0xc8, 0x06, 0x5b, 0xaa, 0xee, 0xe1, 0x22, 0x1d, 0x8b, 0x49, 0x28, 0x2b, 0x7f, + 0xea, 0x05, 0xbb, 0xdc, 0xa9, 0x12, 0xa5, 0x48, 0x22, 0xe5, 0xe9, 0x49, 0x4f, 0xf0, 0xb0, 0x7b, + 0xbc, 0x78, 0xaf, 0x69, 0xf3, 0x8f, 0x9a, 0xcd, 0xb8, 0x1c, 0x4e, 0x5c, 0xe2, 0x89, 0xb1, 0x8a, + 0xa4, 0x3e, 0x2d, 0xf0, 0x47, 0x8e, 0x7c, 0x88, 0x02, 0x48, 0x05, 0x30, 0x50, 0xd6, 0x0d, 0x03, + 0xeb, 0xdb, 0xa8, 0x83, 0x00, 0x22, 0x11, 0x42, 0xd0, 0x98, 0x23, 0x5c, 0xee, 0x03, 0xbb, 0xe2, + 0x72, 0xe8, 0xc7, 0xf4, 0xfe, 0x77, 0xaf, 0x70, 0x80, 0xff, 0xef, 0x64, 0xcd, 0x77, 0xe8, 0xbc, + 0x20, 0x5c, 0xe8, 0x03, 0xd3, 0x2f, 0xf1, 0xdf, 0xfc, 0x25, 0xea, 0xe4, 0xbb, 0x03, 0x20, 0xdb, + 0x02, 0x4c, 0x7b, 0x1f, 0x23, 0xb7, 0xd7, 0xaf, 0xf1, 0xbf, 0x4d, 0x3d, 0x47, 0x3f, 0xaa, 0x72, + 0x8a, 0xd9, 0xdc, 0x4b, 0xc9, 0x9d, 0xbb, 0xbd, 0xc5, 0xca, 0x42, 0xcb, 0x95, 0x85, 0x3e, 0x57, + 0x16, 0x7a, 0x5c, 0x5b, 0xda, 0x72, 0x6d, 0x69, 0x6f, 0x6b, 0x4b, 0xbb, 0x69, 0xee, 0x74, 0x93, + 0xd8, 0xb5, 0xee, 0xa8, 0x0b, 0xe9, 0xe4, 0xcc, 0x36, 0x57, 0x9f, 0x56, 0xe4, 0x16, 0xd3, 0x53, + 0x3c, 0xf9, 0x0a, 0x00, 0x00, 0xff, 0xff, 0xb7, 0x5e, 0x73, 0x68, 0x12, 0x03, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -167,6 +261,8 @@ const _ = grpc.SupportPackageIsVersion4 type MsgClient interface { // Deposit defines a method for depositing funds to the savings module account Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) + // Withdraw defines a method for withdrawing funds to the savings module account + Withdraw(ctx context.Context, in *MsgWithdraw, opts ...grpc.CallOption) (*MsgWithdrawResponse, error) } type msgClient struct { @@ -186,10 +282,21 @@ func (c *msgClient) Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.Ca return out, nil } +func (c *msgClient) Withdraw(ctx context.Context, in *MsgWithdraw, opts ...grpc.CallOption) (*MsgWithdrawResponse, error) { + out := new(MsgWithdrawResponse) + err := c.cc.Invoke(ctx, "/kava.savings.v1beta1.Msg/Withdraw", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { // Deposit defines a method for depositing funds to the savings module account Deposit(context.Context, *MsgDeposit) (*MsgDepositResponse, error) + // Withdraw defines a method for withdrawing funds to the savings module account + Withdraw(context.Context, *MsgWithdraw) (*MsgWithdrawResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -199,6 +306,9 @@ type UnimplementedMsgServer struct { func (*UnimplementedMsgServer) Deposit(ctx context.Context, req *MsgDeposit) (*MsgDepositResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented") } +func (*UnimplementedMsgServer) Withdraw(ctx context.Context, req *MsgWithdraw) (*MsgWithdrawResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Withdraw not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -222,6 +332,24 @@ func _Msg_Deposit_Handler(srv interface{}, ctx context.Context, dec func(interfa return interceptor(ctx, in, info, handler) } +func _Msg_Withdraw_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgWithdraw) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).Withdraw(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/kava.savings.v1beta1.Msg/Withdraw", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).Withdraw(ctx, req.(*MsgWithdraw)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "kava.savings.v1beta1.Msg", HandlerType: (*MsgServer)(nil), @@ -230,6 +358,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "Deposit", Handler: _Msg_Deposit_Handler, }, + { + MethodName: "Withdraw", + Handler: _Msg_Withdraw_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "kava/savings/v1beta1/tx.proto", @@ -302,6 +434,73 @@ func (m *MsgDepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *MsgWithdraw) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgWithdraw) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdraw) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Depositor) > 0 { + i -= len(m.Depositor) + copy(dAtA[i:], m.Depositor) + i = encodeVarintTx(dAtA, i, uint64(len(m.Depositor))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgWithdrawResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgWithdrawResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgWithdrawResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -341,6 +540,34 @@ func (m *MsgDepositResponse) Size() (n int) { return n } +func (m *MsgWithdraw) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Depositor) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgWithdrawResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -513,6 +740,172 @@ func (m *MsgDepositResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgWithdraw) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgWithdraw: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdraw: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Depositor", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Depositor = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgWithdrawResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgWithdrawResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgWithdrawResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0