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
This commit is contained in:
Denali Marsh 2022-03-28 13:53:42 +02:00 committed by GitHub
parent 3b187011bb
commit 70d431b5c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 756 additions and 29 deletions

View File

@ -9,8 +9,13 @@ option go_package = "github.com/kava-labs/kava/x/savings/types";
// Msg defines the savings Msg service. // Msg defines the savings Msg service.
service Msg { 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. // MsgDeposit defines the Msg/Deposit request type.
@ -22,3 +27,13 @@ message MsgDeposit {
// MsgDepositResponse defines the Msg/Deposit response type. // MsgDepositResponse defines the Msg/Deposit response type.
message MsgDepositResponse {} 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 {}

View File

@ -26,6 +26,7 @@ func GetTxCmd() *cobra.Command {
cmds := []*cobra.Command{ cmds := []*cobra.Command{
getCmdDeposit(), getCmdDeposit(),
getCmdWithdraw(),
} }
for _, cmd := range cmds { for _, cmd := range cmds {
@ -42,7 +43,7 @@ func getCmdDeposit() *cobra.Command {
Use: "deposit [amount]", Use: "deposit [amount]",
Short: "deposit coins to savings", Short: "deposit coins to savings",
Example: fmt.Sprintf( Example: fmt.Sprintf(
`%s tx %s savings 10000000bnb --from <key>`, version.AppName, types.ModuleName, `%s tx %s deposit 10000000ukava,100000000usdx --from <key>`, version.AppName, types.ModuleName,
), ),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd) 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 <key>`, 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)
},
}
}

View File

@ -12,7 +12,7 @@ type msgServer struct {
keeper Keeper keeper Keeper
} }
// NewMsgServerImpl returns an implementation of the hard MsgServer interface // NewMsgServerImpl returns an implementation of the savings MsgServer interface
// for the provided Keeper. // for the provided Keeper.
func NewMsgServerImpl(keeper Keeper) types.MsgServer { func NewMsgServerImpl(keeper Keeper) types.MsgServer {
return &msgServer{keeper: keeper} return &msgServer{keeper: keeper}
@ -42,3 +42,26 @@ func (k msgServer) Deposit(goCtx context.Context, msg *types.MsgDeposit) (*types
) )
return &types.MsgDepositResponse{}, nil 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
}

View File

@ -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
}

View File

@ -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))
}
})
}
}

View File

@ -18,6 +18,7 @@ func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
func RegisterInterfaces(registry types.InterfaceRegistry) { func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil), registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgDeposit{}, &MsgDeposit{},
&MsgWithdraw{},
) )
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)

View File

@ -9,6 +9,10 @@ import (
var ( var (
// ErrEmptyInput error for empty input // ErrEmptyInput error for empty input
ErrEmptyInput = sdkerrors.Register(ModuleName, 2, "input must not be empty") ErrEmptyInput = sdkerrors.Register(ModuleName, 2, "input must not be empty")
// ErrInvalidDepositDenom error for invalid deposit denoms // ErrNoDepositFound error when no deposit is found for an address
ErrInvalidDepositDenom = sdkerrors.Register(ModuleName, 3, "invalid deposit denom") 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")
) )

View File

@ -1,7 +1,8 @@
package types package types
const ( const (
EventTypeSavingsDeposit = "deposit_savings" EventTypeSavingsDeposit = "deposit_savings"
EventTypeSavingsWithdrawal = "withdraw_savings"
AttributeValueCategory = ModuleName AttributeValueCategory = ModuleName
AttributeKeyAmount = "amount" AttributeKeyAmount = "amount"

View File

@ -8,6 +8,7 @@ import (
// ensure Msg interface compliance at compile time // ensure Msg interface compliance at compile time
var ( var (
_ sdk.Msg = &MsgDeposit{} _ sdk.Msg = &MsgDeposit{}
_ sdk.Msg = &MsgWithdraw{}
) )
// NewMsgDeposit returns a new MsgDeposit // NewMsgDeposit returns a new MsgDeposit
@ -51,3 +52,45 @@ func (msg MsgDeposit) GetSigners() []sdk.AccAddress {
} }
return []sdk.AccAddress{depositor} 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}
}

View File

@ -121,36 +121,130 @@ func (m *MsgDepositResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_MsgDepositResponse proto.InternalMessageInfo 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() { func init() {
proto.RegisterType((*MsgDeposit)(nil), "kava.savings.v1beta1.MsgDeposit") proto.RegisterType((*MsgDeposit)(nil), "kava.savings.v1beta1.MsgDeposit")
proto.RegisterType((*MsgDepositResponse)(nil), "kava.savings.v1beta1.MsgDepositResponse") 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) } func init() { proto.RegisterFile("kava/savings/v1beta1/tx.proto", fileDescriptor_c0bf8679b144267a) }
var fileDescriptor_c0bf8679b144267a = []byte{ var fileDescriptor_c0bf8679b144267a = []byte{
// 324 bytes of a gzipped FileDescriptorProto // 367 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0xc1, 0x4e, 0xf2, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x52, 0xcd, 0x4e, 0xea, 0x40,
0x14, 0x85, 0xdb, 0x9f, 0x84, 0x3f, 0x8c, 0xbb, 0xa6, 0x0b, 0x20, 0x71, 0x20, 0xac, 0xea, 0x82, 0x14, 0xee, 0x5c, 0x12, 0xee, 0x65, 0xd8, 0xf5, 0xd6, 0x04, 0x9a, 0x58, 0x90, 0x55, 0x59, 0x30,
0x19, 0xc1, 0xc4, 0xbd, 0xe0, 0x96, 0x0d, 0xc6, 0x8d, 0x31, 0x31, 0xd3, 0x76, 0x32, 0x36, 0x48, 0x15, 0x4c, 0xdc, 0x0b, 0x6e, 0xd9, 0x60, 0x8c, 0xc6, 0x8d, 0x99, 0xfe, 0x64, 0x98, 0x20, 0x9d,
0x6f, 0xd3, 0x3b, 0x10, 0x7c, 0x0b, 0x5f, 0x43, 0xd7, 0x3e, 0x04, 0x4b, 0xe2, 0xca, 0x95, 0x1a, 0xa6, 0x67, 0x40, 0x7c, 0x0b, 0x5f, 0x43, 0xd6, 0xc6, 0x67, 0x60, 0x49, 0x5c, 0xb9, 0x52, 0x03,
0x78, 0x11, 0xd3, 0xce, 0x40, 0x5d, 0x98, 0xb8, 0xea, 0x6d, 0xbe, 0x73, 0x4e, 0xce, 0xbd, 0x43, 0x2f, 0x62, 0xda, 0x4e, 0x81, 0x85, 0x86, 0xad, 0xab, 0x9e, 0xe6, 0xfb, 0xc9, 0x77, 0xbe, 0x39,
0x8e, 0x67, 0x62, 0x29, 0x38, 0x8a, 0x65, 0x92, 0x2a, 0xe4, 0xcb, 0x41, 0x28, 0xb5, 0x18, 0x70, 0xf8, 0x70, 0x44, 0xa7, 0xd4, 0x01, 0x3a, 0xe5, 0x21, 0x03, 0x67, 0xda, 0x76, 0x03, 0x49, 0xdb,
0xbd, 0x62, 0x59, 0x0e, 0x1a, 0x3c, 0xbf, 0xc0, 0xcc, 0x62, 0x66, 0x71, 0xdb, 0x57, 0xa0, 0xa0, 0x8e, 0x9c, 0x91, 0x28, 0x16, 0x52, 0xe8, 0x46, 0x02, 0x13, 0x05, 0x13, 0x05, 0x9b, 0x06, 0x13,
0x14, 0xf0, 0x62, 0x32, 0xda, 0x36, 0x8d, 0x00, 0xe7, 0x80, 0x3c, 0x14, 0x28, 0x0f, 0x49, 0x11, 0x4c, 0xa4, 0x04, 0x27, 0x99, 0x32, 0xae, 0x69, 0x79, 0x02, 0xc6, 0x02, 0x1c, 0x97, 0x42, 0xb0,
0x24, 0xa9, 0xe5, 0x2d, 0xc3, 0xef, 0x8c, 0xd1, 0xfc, 0x18, 0xd4, 0x7b, 0x76, 0x09, 0x99, 0xa0, 0x71, 0xf2, 0x04, 0x0f, 0x15, 0x5e, 0xcd, 0xf0, 0xdb, 0x4c, 0x98, 0xfd, 0x64, 0x50, 0xe3, 0x09,
0xba, 0x94, 0x19, 0x60, 0xa2, 0xbd, 0x73, 0xd2, 0x88, 0xcd, 0x08, 0x79, 0xd3, 0xed, 0xba, 0x41, 0x61, 0xdc, 0x07, 0x76, 0x1e, 0x44, 0x02, 0xb8, 0xd4, 0x4f, 0x71, 0xc9, 0xcf, 0x46, 0x11, 0x57,
0x63, 0xd4, 0x7c, 0x7b, 0xed, 0xfb, 0xd6, 0x73, 0x11, 0xc7, 0xb9, 0x44, 0xbc, 0xd2, 0x79, 0x92, 0x50, 0x1d, 0xd9, 0xa5, 0x6e, 0xe5, 0xf5, 0xb9, 0x65, 0x28, 0xcd, 0x99, 0xef, 0xc7, 0x01, 0xc0,
0xaa, 0x69, 0x25, 0xf5, 0x22, 0x52, 0x17, 0x73, 0x58, 0xa4, 0xba, 0xf9, 0xaf, 0x5b, 0x0b, 0x8e, 0x85, 0x8c, 0x79, 0xc8, 0x06, 0x5b, 0xaa, 0xee, 0xe1, 0x22, 0x1d, 0x8b, 0x49, 0x28, 0x2b, 0x7f,
0x86, 0x2d, 0x66, 0x1d, 0x45, 0xa5, 0x7d, 0x7b, 0x36, 0x86, 0x24, 0x1d, 0x9d, 0xae, 0x3f, 0x3a, 0xea, 0x05, 0xbb, 0xdc, 0xa9, 0x12, 0xa5, 0x48, 0x22, 0xe5, 0xe9, 0x49, 0x4f, 0xf0, 0xb0, 0x7b,
0xce, 0xcb, 0x67, 0x27, 0x50, 0x89, 0xbe, 0x5f, 0x84, 0x2c, 0x82, 0xb9, 0xad, 0x64, 0x3f, 0x7d, 0xbc, 0x78, 0xaf, 0x69, 0xf3, 0x8f, 0x9a, 0xcd, 0xb8, 0x1c, 0x4e, 0x5c, 0xe2, 0x89, 0xb1, 0x8a,
0x8c, 0x67, 0x5c, 0x3f, 0x66, 0x12, 0x4b, 0x03, 0x4e, 0x6d, 0x74, 0xcf, 0x27, 0x5e, 0x55, 0x75, 0xa4, 0x3e, 0x2d, 0xf0, 0x47, 0x8e, 0x7c, 0x88, 0x02, 0x48, 0x05, 0x30, 0x50, 0xd6, 0x0d, 0x03,
0x2a, 0x31, 0x83, 0x14, 0xe5, 0xf0, 0x96, 0xd4, 0x26, 0xa8, 0xbc, 0x6b, 0xf2, 0x7f, 0xbf, 0x44, 0xeb, 0xdb, 0xa8, 0x83, 0x00, 0x22, 0x11, 0x42, 0xd0, 0x98, 0x23, 0x5c, 0xee, 0x03, 0xbb, 0xe2,
0x97, 0xfd, 0x76, 0x3b, 0x56, 0x79, 0xdb, 0xc1, 0x5f, 0x8a, 0x7d, 0xfa, 0x68, 0xbc, 0xde, 0x52, 0x72, 0xe8, 0xc7, 0xf4, 0xfe, 0x77, 0xaf, 0x70, 0x80, 0xff, 0xef, 0x64, 0xcd, 0x77, 0xe8, 0xbc,
0x77, 0xb3, 0xa5, 0xee, 0xd7, 0x96, 0xba, 0x4f, 0x3b, 0xea, 0x6c, 0x76, 0xd4, 0x79, 0xdf, 0x51, 0x20, 0x5c, 0xe8, 0x03, 0xd3, 0x2f, 0xf1, 0xdf, 0xfc, 0x25, 0xea, 0xe4, 0xbb, 0x03, 0x20, 0xdb,
0xe7, 0xe6, 0xe4, 0x47, 0xff, 0x22, 0xad, 0xff, 0x20, 0x42, 0x2c, 0x27, 0xbe, 0x3a, 0x3c, 0x6b, 0x02, 0x4c, 0x7b, 0x1f, 0x23, 0xb7, 0xd7, 0xaf, 0xf1, 0xbf, 0x4d, 0x3d, 0x47, 0x3f, 0xaa, 0x72,
0xb9, 0x46, 0x58, 0x2f, 0x6f, 0x7d, 0xf6, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x0e, 0x4c, 0xbf, 0xbb, 0x8a, 0xd9, 0xdc, 0x4b, 0xc9, 0x9d, 0xbb, 0xbd, 0xc5, 0xca, 0x42, 0xcb, 0x95, 0x85, 0x3e, 0x57,
0xf3, 0x01, 0x00, 0x00, 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. // Reference imports to suppress errors if they are not otherwise used.
@ -167,6 +261,8 @@ const _ = grpc.SupportPackageIsVersion4
type MsgClient interface { type MsgClient interface {
// Deposit defines a method for depositing funds to the savings module account // Deposit defines a method for depositing funds to the savings module account
Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.CallOption) (*MsgDepositResponse, error) 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 { type msgClient struct {
@ -186,10 +282,21 @@ func (c *msgClient) Deposit(ctx context.Context, in *MsgDeposit, opts ...grpc.Ca
return out, nil 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. // MsgServer is the server API for Msg service.
type MsgServer interface { type MsgServer interface {
// Deposit defines a method for depositing funds to the savings module account // Deposit defines a method for depositing funds to the savings module account
Deposit(context.Context, *MsgDeposit) (*MsgDepositResponse, error) 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. // 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) { func (*UnimplementedMsgServer) Deposit(ctx context.Context, req *MsgDeposit) (*MsgDepositResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Deposit not implemented") 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) { func RegisterMsgServer(s grpc1.Server, srv MsgServer) {
s.RegisterService(&_Msg_serviceDesc, srv) 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) 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{ var _Msg_serviceDesc = grpc.ServiceDesc{
ServiceName: "kava.savings.v1beta1.Msg", ServiceName: "kava.savings.v1beta1.Msg",
HandlerType: (*MsgServer)(nil), HandlerType: (*MsgServer)(nil),
@ -230,6 +358,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{
MethodName: "Deposit", MethodName: "Deposit",
Handler: _Msg_Deposit_Handler, Handler: _Msg_Deposit_Handler,
}, },
{
MethodName: "Withdraw",
Handler: _Msg_Withdraw_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "kava/savings/v1beta1/tx.proto", Metadata: "kava/savings/v1beta1/tx.proto",
@ -302,6 +434,73 @@ func (m *MsgDepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil 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 { func encodeVarintTx(dAtA []byte, offset int, v uint64) int {
offset -= sovTx(v) offset -= sovTx(v)
base := offset base := offset
@ -341,6 +540,34 @@ func (m *MsgDepositResponse) Size() (n int) {
return n 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) { func sovTx(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7 return (math_bits.Len64(x|1) + 6) / 7
} }
@ -513,6 +740,172 @@ func (m *MsgDepositResponse) Unmarshal(dAtA []byte) error {
} }
return nil 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) { func skipTx(dAtA []byte) (n int, err error) {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0