diff --git a/app/app.go b/app/app.go
index c12a13f5..f0e3af25 100644
--- a/app/app.go
+++ b/app/app.go
@@ -131,6 +131,9 @@ import (
pricefeed "github.com/kava-labs/kava/x/pricefeed"
pricefeedkeeper "github.com/kava-labs/kava/x/pricefeed/keeper"
pricefeedtypes "github.com/kava-labs/kava/x/pricefeed/types"
+ "github.com/kava-labs/kava/x/router"
+ routerkeeper "github.com/kava-labs/kava/x/router/keeper"
+ routertypes "github.com/kava-labs/kava/x/router/types"
savings "github.com/kava-labs/kava/x/savings"
savingskeeper "github.com/kava-labs/kava/x/savings/keeper"
savingstypes "github.com/kava-labs/kava/x/savings/types"
@@ -195,6 +198,7 @@ var (
evmutil.AppModuleBasic{},
liquid.AppModuleBasic{},
earn.AppModuleBasic{},
+ router.AppModuleBasic{},
)
// module account permissions
@@ -291,6 +295,7 @@ type App struct {
savingsKeeper savingskeeper.Keeper
liquidKeeper liquidkeeper.Keeper
earnKeeper earnkeeper.Keeper
+ routerKeeper routerkeeper.Keeper
// make scoped keepers public for test purposes
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
@@ -624,6 +629,11 @@ func NewApp(
nil,
&earnKeeper,
)
+ app.routerKeeper = routerkeeper.NewKeeper(
+ &app.earnKeeper,
+ app.liquidKeeper,
+ &app.stakingKeeper,
+ )
// create committee keeper with router
committeeGovRouter := govtypes.NewRouter()
@@ -718,6 +728,7 @@ func NewApp(
savings.NewAppModule(app.savingsKeeper, app.accountKeeper, app.bankKeeper),
liquid.NewAppModule(app.liquidKeeper),
earn.NewAppModule(app.earnKeeper, app.accountKeeper, app.bankKeeper),
+ router.NewAppModule(app.routerKeeper),
)
// Warning: Some begin blockers must run before others. Ensure the dependencies are understood before modifying this list.
@@ -766,6 +777,7 @@ func NewApp(
savingstypes.ModuleName,
liquidtypes.ModuleName,
earntypes.ModuleName,
+ routertypes.ModuleName,
)
// Warning: Some end blockers must run before others. Ensure the dependencies are understood before modifying this list.
@@ -806,6 +818,7 @@ func NewApp(
savingstypes.ModuleName,
liquidtypes.ModuleName,
earntypes.ModuleName,
+ routertypes.ModuleName,
)
// Warning: Some init genesis methods must run before others. Ensure the dependencies are understood before modifying this list
@@ -845,6 +858,7 @@ func NewApp(
upgradetypes.ModuleName,
validatorvestingtypes.ModuleName,
liquidtypes.ModuleName,
+ routertypes.ModuleName,
)
app.mm.RegisterInvariants(&app.crisisKeeper)
diff --git a/app/test_common.go b/app/test_common.go
index c458f620..957334e3 100644
--- a/app/test_common.go
+++ b/app/test_common.go
@@ -42,6 +42,7 @@ import (
kavadistkeeper "github.com/kava-labs/kava/x/kavadist/keeper"
liquidkeeper "github.com/kava-labs/kava/x/liquid/keeper"
pricefeedkeeper "github.com/kava-labs/kava/x/pricefeed/keeper"
+ routerkeeper "github.com/kava-labs/kava/x/router/keeper"
savingskeeper "github.com/kava-labs/kava/x/savings/keeper"
swapkeeper "github.com/kava-labs/kava/x/swap/keeper"
)
@@ -112,6 +113,7 @@ func (tApp TestApp) GetSavingsKeeper() savingskeeper.Keeper { return tApp.sa
func (tApp TestApp) GetFeeMarketKeeper() feemarketkeeper.Keeper { return tApp.feeMarketKeeper }
func (tApp TestApp) GetLiquidKeeper() liquidkeeper.Keeper { return tApp.liquidKeeper }
func (tApp TestApp) GetEarnKeeper() earnkeeper.Keeper { return tApp.earnKeeper }
+func (tApp TestApp) GetRouterKeeper() routerkeeper.Keeper { return tApp.routerKeeper }
func (tApp TestApp) GetStoreKey(s string) sdk.StoreKey { return tApp.keys[s] }
diff --git a/contrib/devnet/init-new-chain.sh b/contrib/devnet/init-new-chain.sh
index 58e29d20..5bb2715f 100755
--- a/contrib/devnet/init-new-chain.sh
+++ b/contrib/devnet/init-new-chain.sh
@@ -2,7 +2,8 @@
set -e
validatorMnemonic="equip town gesture square tomorrow volume nephew minute witness beef rich gadget actress egg sing secret pole winter alarm law today check violin uncover"
-# kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
+# kava1ffv7nhd3z6sych2qpqkk03ec6hzkmufy0r2s4c
+# kavavaloper1ffv7nhd3z6sych2qpqkk03ec6hzkmufyz4scd0
faucetMnemonic="crash sort dwarf disease change advice attract clump avoid mobile clump right junior axis book fresh mask tube front require until face effort vault"
# kava1adkm6svtzjsxxvg7g6rshg6kj9qwej8gwqadqd
@@ -92,5 +93,14 @@ jq '.app_state.evm.params.chain_config.merge_fork_block = null' $DATA/config/gen
jq '.app_state.earn.params.allowed_vaults = [
{
denom: "usdx",
- vault_strategy: 1,
+ strategies: ["STRATEGY_TYPE_HARD"],
+ },
+ {
+ denom: "bkava",
+ strategies: ["STRATEGY_TYPE_SAVINGS"],
}]' $DATA/config/genesis.json | sponge $DATA/config/genesis.json
+
+jq '.app_state.savings.params.supported_denoms = ["bkava-kavavaloper1ffv7nhd3z6sych2qpqkk03ec6hzkmufyz4scd0"]' $DATA/config/genesis.json | sponge $DATA/config/genesis.json
+
+
+$BINARY config broadcast-mode block
\ No newline at end of file
diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md
index 5d14b711..a43648fb 100644
--- a/docs/core/proto-docs.md
+++ b/docs/core/proto-docs.md
@@ -430,6 +430,18 @@
- [Msg](#kava.pricefeed.v1beta1.Msg)
+- [kava/router/v1beta1/tx.proto](#kava/router/v1beta1/tx.proto)
+ - [MsgDelegateMintDeposit](#kava.router.v1beta1.MsgDelegateMintDeposit)
+ - [MsgDelegateMintDepositResponse](#kava.router.v1beta1.MsgDelegateMintDepositResponse)
+ - [MsgMintDeposit](#kava.router.v1beta1.MsgMintDeposit)
+ - [MsgMintDepositResponse](#kava.router.v1beta1.MsgMintDepositResponse)
+ - [MsgWithdrawBurn](#kava.router.v1beta1.MsgWithdrawBurn)
+ - [MsgWithdrawBurnResponse](#kava.router.v1beta1.MsgWithdrawBurnResponse)
+ - [MsgWithdrawBurnUndelegate](#kava.router.v1beta1.MsgWithdrawBurnUndelegate)
+ - [MsgWithdrawBurnUndelegateResponse](#kava.router.v1beta1.MsgWithdrawBurnUndelegateResponse)
+
+ - [Msg](#kava.router.v1beta1.Msg)
+
- [kava/savings/v1beta1/store.proto](#kava/savings/v1beta1/store.proto)
- [Deposit](#kava.savings.v1beta1.Deposit)
- [Params](#kava.savings.v1beta1.Params)
@@ -5957,6 +5969,145 @@ Msg defines the pricefeed Msg service.
+
+
Top
+
+## kava/router/v1beta1/tx.proto
+
+
+
+
+
+### MsgDelegateMintDeposit
+MsgDelegateMintDeposit delegates tokens to a validator, then converts them into staking derivatives,
+then deposits to an earn vault.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| `depositor` | [string](#string) | | depositor represents the owner of the tokens to delegate |
+| `validator` | [string](#string) | | validator is the address of the validator to delegate to |
+| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | amount is the tokens to delegate |
+
+
+
+
+
+
+
+
+### MsgDelegateMintDepositResponse
+MsgDelegateMintDepositResponse defines the Msg/MsgDelegateMintDeposit response type.
+
+
+
+
+
+
+
+
+### MsgMintDeposit
+MsgMintDeposit converts a delegation into staking derivatives and deposits it all into an earn vault.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| `depositor` | [string](#string) | | depositor represents the owner of the delegation to convert |
+| `validator` | [string](#string) | | validator is the validator for the depositor's delegation |
+| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | amount is the delegation balance to convert |
+
+
+
+
+
+
+
+
+### MsgMintDepositResponse
+MsgMintDepositResponse defines the Msg/MsgMintDeposit response type.
+
+
+
+
+
+
+
+
+### MsgWithdrawBurn
+MsgWithdrawBurn removes staking derivatives from an earn vault and converts them back to a staking delegation.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| `from` | [string](#string) | | from is the owner of the earn vault to withdraw from |
+| `validator` | [string](#string) | | validator is the address to select the derivative denom to withdraw |
+| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | amount is the staked token equivalent to withdraw |
+
+
+
+
+
+
+
+
+### MsgWithdrawBurnResponse
+MsgWithdrawBurnResponse defines the Msg/MsgWithdrawBurn response type.
+
+
+
+
+
+
+
+
+### MsgWithdrawBurnUndelegate
+MsgWithdrawBurnUndelegate removes staking derivatives from an earn vault, converts them to a staking delegation,
+then undelegates them from their validator.
+
+
+| Field | Type | Label | Description |
+| ----- | ---- | ----- | ----------- |
+| `from` | [string](#string) | | from is the owner of the earn vault to withdraw from |
+| `validator` | [string](#string) | | validator is the address to select the derivative denom to withdraw |
+| `amount` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | | amount is the staked token equivalent to withdraw |
+
+
+
+
+
+
+
+
+### MsgWithdrawBurnUndelegateResponse
+MsgWithdrawBurnUndelegateResponse defines the Msg/MsgWithdrawBurnUndelegate response type.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+### Msg
+Msg defines the router Msg service.
+
+| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
+| ----------- | ------------ | ------------- | ------------| ------- | -------- |
+| `MintDeposit` | [MsgMintDeposit](#kava.router.v1beta1.MsgMintDeposit) | [MsgMintDepositResponse](#kava.router.v1beta1.MsgMintDepositResponse) | MintDeposit converts a delegation into staking derivatives and deposits it all into an earn vault. | |
+| `DelegateMintDeposit` | [MsgDelegateMintDeposit](#kava.router.v1beta1.MsgDelegateMintDeposit) | [MsgDelegateMintDepositResponse](#kava.router.v1beta1.MsgDelegateMintDepositResponse) | DelegateMintDeposit delegates tokens to a validator, then converts them into staking derivatives, then deposits to an earn vault. | |
+| `WithdrawBurn` | [MsgWithdrawBurn](#kava.router.v1beta1.MsgWithdrawBurn) | [MsgWithdrawBurnResponse](#kava.router.v1beta1.MsgWithdrawBurnResponse) | WithdrawBurn removes staking derivatives from an earn vault and converts them back to a staking delegation. | |
+| `WithdrawBurnUndelegate` | [MsgWithdrawBurnUndelegate](#kava.router.v1beta1.MsgWithdrawBurnUndelegate) | [MsgWithdrawBurnUndelegateResponse](#kava.router.v1beta1.MsgWithdrawBurnUndelegateResponse) | WithdrawBurnUndelegate removes staking derivatives from an earn vault, converts them to a staking delegation, then undelegates them from their validator. | |
+
+
+
+
+
Top
diff --git a/proto/kava/router/v1beta1/tx.proto b/proto/kava/router/v1beta1/tx.proto
new file mode 100644
index 00000000..5d517b74
--- /dev/null
+++ b/proto/kava/router/v1beta1/tx.proto
@@ -0,0 +1,76 @@
+syntax = "proto3";
+package kava.router.v1beta1;
+
+import "cosmos_proto/cosmos.proto";
+import "cosmos/base/v1beta1/coin.proto";
+import "gogoproto/gogo.proto";
+
+option go_package = "github.com/kava-labs/kava/x/router/types";
+option (gogoproto.goproto_getters_all) = false;
+
+// Msg defines the router Msg service.
+service Msg {
+ // MintDeposit converts a delegation into staking derivatives and deposits it all into an earn vault.
+ rpc MintDeposit(MsgMintDeposit) returns (MsgMintDepositResponse);
+
+ // DelegateMintDeposit delegates tokens to a validator, then converts them into staking derivatives,
+ // then deposits to an earn vault.
+ rpc DelegateMintDeposit(MsgDelegateMintDeposit) returns (MsgDelegateMintDepositResponse);
+
+ // WithdrawBurn removes staking derivatives from an earn vault and converts them back to a staking delegation.
+ rpc WithdrawBurn(MsgWithdrawBurn) returns (MsgWithdrawBurnResponse);
+
+ // WithdrawBurnUndelegate removes staking derivatives from an earn vault, converts them to a staking delegation,
+ // then undelegates them from their validator.
+ rpc WithdrawBurnUndelegate(MsgWithdrawBurnUndelegate) returns (MsgWithdrawBurnUndelegateResponse);
+}
+
+// MsgMintDeposit converts a delegation into staking derivatives and deposits it all into an earn vault.
+message MsgMintDeposit {
+ // depositor represents the owner of the delegation to convert
+ string depositor = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+ // validator is the validator for the depositor's delegation
+ string validator = 2;
+ // amount is the delegation balance to convert
+ cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
+}
+// MsgMintDepositResponse defines the Msg/MsgMintDeposit response type.
+message MsgMintDepositResponse {}
+
+// MsgDelegateMintDeposit delegates tokens to a validator, then converts them into staking derivatives,
+// then deposits to an earn vault.
+message MsgDelegateMintDeposit {
+ // depositor represents the owner of the tokens to delegate
+ string depositor = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+ // validator is the address of the validator to delegate to
+ string validator = 2;
+ // amount is the tokens to delegate
+ cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
+}
+// MsgDelegateMintDepositResponse defines the Msg/MsgDelegateMintDeposit response type.
+message MsgDelegateMintDepositResponse {}
+
+// MsgWithdrawBurn removes staking derivatives from an earn vault and converts them back to a staking delegation.
+message MsgWithdrawBurn {
+ // from is the owner of the earn vault to withdraw from
+ string from = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+ // validator is the address to select the derivative denom to withdraw
+ string validator = 2;
+ // amount is the staked token equivalent to withdraw
+ cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
+}
+// MsgWithdrawBurnResponse defines the Msg/MsgWithdrawBurn response type.
+message MsgWithdrawBurnResponse {}
+
+// MsgWithdrawBurnUndelegate removes staking derivatives from an earn vault, converts them to a staking delegation,
+// then undelegates them from their validator.
+message MsgWithdrawBurnUndelegate {
+ // from is the owner of the earn vault to withdraw from
+ string from = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
+ // validator is the address to select the derivative denom to withdraw
+ string validator = 2;
+ // amount is the staked token equivalent to withdraw
+ cosmos.base.v1beta1.Coin amount = 3 [(gogoproto.nullable) = false];
+}
+// MsgWithdrawBurnUndelegateResponse defines the Msg/MsgWithdrawBurnUndelegate response type.
+message MsgWithdrawBurnUndelegateResponse {}
\ No newline at end of file
diff --git a/x/liquid/keeper/derivative.go b/x/liquid/keeper/derivative.go
index fe9dd4df..de3c3162 100644
--- a/x/liquid/keeper/derivative.go
+++ b/x/liquid/keeper/derivative.go
@@ -155,3 +155,21 @@ func (k Keeper) burnCoins(ctx sdk.Context, sender sdk.AccAddress, amount sdk.Coi
}
return nil
}
+
+// DerivativeFromTokens calculates the approximate amount of derivative coins that would be minted for a given amount of staking tokens.
+func (k Keeper) DerivativeFromTokens(ctx sdk.Context, valAddr sdk.ValAddress, tokens sdk.Coin) (sdk.Coin, error) {
+ bondDenom := k.stakingKeeper.BondDenom(ctx)
+ if tokens.Denom != bondDenom {
+ return sdk.Coin{}, sdkerrors.Wrapf(types.ErrInvalidDenom, "'%s' does not match staking denom '%s'", tokens.Denom, bondDenom)
+ }
+
+ // Use GetModuleAddress instead of GetModuleAccount to avoid creating a module account if it doesn't exist.
+ modAddress := k.accountKeeper.GetModuleAddress(types.ModuleAccountName)
+ derivative, _, err := k.CalculateDerivativeSharesFromTokens(ctx, modAddress, valAddr, tokens.Amount)
+ if err != nil {
+ return sdk.Coin{}, err
+ }
+ liquidTokenDenom := k.GetLiquidStakingTokenDenom(valAddr)
+ liquidToken := sdk.NewCoin(liquidTokenDenom, derivative)
+ return liquidToken, nil
+}
diff --git a/x/liquid/keeper/derivative_test.go b/x/liquid/keeper/derivative_test.go
index b3e1303b..4bb4b241 100644
--- a/x/liquid/keeper/derivative_test.go
+++ b/x/liquid/keeper/derivative_test.go
@@ -469,3 +469,27 @@ func (suite *KeeperTestSuite) TestGetStakedTokensForDerivatives() {
})
}
}
+
+func (suite *KeeperTestSuite) TestDerivativeFromTokens() {
+ _, addrs := app.GeneratePrivKeyAddressPairs(1)
+ valAccAddr := addrs[0]
+ valAddr := sdk.ValAddress(valAccAddr)
+ moduleAccAddress := authtypes.NewModuleAddress(types.ModuleAccountName)
+
+ initialBalance := i(1e9)
+
+ suite.CreateAccountWithAddress(valAccAddr, suite.NewBondCoins(initialBalance))
+ suite.AddCoinsToModule(types.ModuleAccountName, suite.NewBondCoins(initialBalance))
+
+ suite.CreateNewUnbondedValidator(valAddr, initialBalance)
+ suite.CreateDelegation(valAddr, moduleAccAddress, initialBalance)
+ staking.EndBlocker(suite.Ctx, suite.StakingKeeper)
+
+ _, err := suite.Keeper.DerivativeFromTokens(suite.Ctx, valAddr, sdk.NewCoin("invalid", initialBalance))
+ suite.ErrorIs(err, types.ErrInvalidDenom)
+
+ derivatives, err := suite.Keeper.DerivativeFromTokens(suite.Ctx, valAddr, suite.NewBondCoin(initialBalance))
+ suite.NoError(err)
+ expected := sdk.NewCoin(fmt.Sprintf("bkava-%s", valAddr), initialBalance)
+ suite.Equal(expected, derivatives)
+}
diff --git a/x/liquid/types/expected_keepers.go b/x/liquid/types/expected_keepers.go
index c3d9ca8a..5466983a 100644
--- a/x/liquid/types/expected_keepers.go
+++ b/x/liquid/types/expected_keepers.go
@@ -21,6 +21,7 @@ type BankKeeper interface {
// AccountKeeper defines the expected keeper interface for interacting with account
type AccountKeeper interface {
+ GetModuleAddress(moduleName string) sdk.AccAddress
GetModuleAccount(ctx sdk.Context, name string) authtypes.ModuleAccountI
}
diff --git a/x/router/client/cli/tx.go b/x/router/client/cli/tx.go
new file mode 100644
index 00000000..7f9953bf
--- /dev/null
+++ b/x/router/client/cli/tx.go
@@ -0,0 +1,174 @@
+package cli
+
+import (
+ "fmt"
+
+ "github.com/spf13/cobra"
+
+ "github.com/cosmos/cosmos-sdk/client"
+ "github.com/cosmos/cosmos-sdk/client/flags"
+ "github.com/cosmos/cosmos-sdk/client/tx"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ "github.com/cosmos/cosmos-sdk/version"
+
+ "github.com/kava-labs/kava/x/router/types"
+)
+
+// GetTxCmd returns the transaction commands for this module
+func GetTxCmd() *cobra.Command {
+ liquidTxCmd := &cobra.Command{
+ Use: types.ModuleName,
+ Short: "router transactions subcommands",
+ DisableFlagParsing: true,
+ SuggestionsMinimumDistance: 2,
+ RunE: client.ValidateCmd,
+ }
+
+ cmds := []*cobra.Command{
+ getCmdMintDeposit(),
+ getCmdDelegateMintDeposit(),
+ getCmdWithdrawBurn(),
+ getCmdWithdrawBurnUndelegate(),
+ }
+
+ for _, cmd := range cmds {
+ flags.AddTxFlagsToCmd(cmd)
+ }
+
+ liquidTxCmd.AddCommand(cmds...)
+
+ return liquidTxCmd
+}
+
+func getCmdMintDeposit() *cobra.Command {
+ return &cobra.Command{
+ Use: "mint-deposit [validator-addr] [amount]",
+ Short: "mints staking derivative from a delegation and deposits them to earn",
+ Args: cobra.ExactArgs(2),
+ Example: fmt.Sprintf(
+ `%s tx %s mint-deposit kavavaloper16lnfpgn6llvn4fstg5nfrljj6aaxyee9z59jqd 10000000ukava --from `, version.AppName, types.ModuleName,
+ ),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ valAddr, err := sdk.ValAddressFromBech32(args[0])
+ if err != nil {
+ return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
+ }
+
+ coin, err := sdk.ParseCoinNormalized(args[1])
+ if err != nil {
+ return err
+ }
+
+ msg := types.NewMsgMintDeposit(clientCtx.GetFromAddress(), valAddr, coin)
+ if err := msg.ValidateBasic(); err != nil {
+ return err
+ }
+ return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+ },
+ }
+}
+
+func getCmdDelegateMintDeposit() *cobra.Command {
+ return &cobra.Command{
+ Use: "delegate-mint-deposit [validator-addr] [amount]",
+ Short: "delegates tokens, mints staking derivative from a them, and deposits them to earn",
+ Args: cobra.ExactArgs(2),
+ Example: fmt.Sprintf(
+ `%s tx %s delegate-mint-deposit kavavaloper16lnfpgn6llvn4fstg5nfrljj6aaxyee9z59jqd 10000000ukava --from `, version.AppName, types.ModuleName,
+ ),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ valAddr, err := sdk.ValAddressFromBech32(args[0])
+ if err != nil {
+ return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
+ }
+
+ coin, err := sdk.ParseCoinNormalized(args[1])
+ if err != nil {
+ return err
+ }
+
+ msg := types.NewMsgDelegateMintDeposit(clientCtx.GetFromAddress(), valAddr, coin)
+ if err := msg.ValidateBasic(); err != nil {
+ return err
+ }
+ return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+ },
+ }
+}
+
+func getCmdWithdrawBurn() *cobra.Command {
+ return &cobra.Command{
+ Use: "withdraw-burn [validator-addr] [amount]",
+ Short: "withdraws staking derivatives from earn and burns them to redeem a delegation",
+ Example: fmt.Sprintf(
+ `%s tx %s withdraw-burn kavavaloper16lnfpgn6llvn4fstg5nfrljj6aaxyee9z59jqd 10000000ukava --from `, version.AppName, types.ModuleName,
+ ),
+ Args: cobra.ExactArgs(2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ valAddr, err := sdk.ValAddressFromBech32(args[0])
+ if err != nil {
+ return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
+ }
+
+ amount, err := sdk.ParseCoinNormalized(args[1])
+ if err != nil {
+ return err
+ }
+
+ msg := types.NewMsgWithdrawBurn(clientCtx.GetFromAddress(), valAddr, amount)
+ if err := msg.ValidateBasic(); err != nil {
+ return err
+ }
+ return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+ },
+ }
+}
+
+func getCmdWithdrawBurnUndelegate() *cobra.Command {
+ return &cobra.Command{
+ Use: "withdraw-burn-undelegate [validator-addr] [amount]",
+ Short: "withdraws staking derivatives from earn, burns them to redeem a delegation, then unstakes the delegation",
+ Example: fmt.Sprintf(
+ `%s tx %s withdraw-burn-undelegate kavavaloper16lnfpgn6llvn4fstg5nfrljj6aaxyee9z59jqd 10000000ukava --from `, version.AppName, types.ModuleName,
+ ),
+ Args: cobra.ExactArgs(2),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ clientCtx, err := client.GetClientTxContext(cmd)
+ if err != nil {
+ return err
+ }
+
+ valAddr, err := sdk.ValAddressFromBech32(args[0])
+ if err != nil {
+ return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, err.Error())
+ }
+
+ amount, err := sdk.ParseCoinNormalized(args[1])
+ if err != nil {
+ return err
+ }
+
+ msg := types.NewMsgWithdrawBurnUndelegate(clientCtx.GetFromAddress(), valAddr, amount)
+ if err := msg.ValidateBasic(); err != nil {
+ return err
+ }
+ return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
+ },
+ }
+}
diff --git a/x/router/keeper/keeper.go b/x/router/keeper/keeper.go
new file mode 100644
index 00000000..a509ad97
--- /dev/null
+++ b/x/router/keeper/keeper.go
@@ -0,0 +1,26 @@
+package keeper
+
+import (
+ "github.com/kava-labs/kava/x/router/types"
+)
+
+// Keeper is the keeper for the module
+type Keeper struct {
+ earnKeeper types.EarnKeeper
+ liquidKeeper types.LiquidKeeper
+ stakingKeeper types.StakingKeeper
+}
+
+// NewKeeper creates a new keeper
+func NewKeeper(
+ earnKeeper types.EarnKeeper,
+ liquidKeeper types.LiquidKeeper,
+ stakingKeeper types.StakingKeeper,
+) Keeper {
+
+ return Keeper{
+ earnKeeper: earnKeeper,
+ liquidKeeper: liquidKeeper,
+ stakingKeeper: stakingKeeper,
+ }
+}
diff --git a/x/router/keeper/msg_server.go b/x/router/keeper/msg_server.go
new file mode 100644
index 00000000..72015426
--- /dev/null
+++ b/x/router/keeper/msg_server.go
@@ -0,0 +1,201 @@
+package keeper
+
+import (
+ "context"
+ "time"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+
+ earntypes "github.com/kava-labs/kava/x/earn/types"
+ "github.com/kava-labs/kava/x/router/types"
+)
+
+type msgServer struct {
+ keeper Keeper
+}
+
+// NewMsgServerImpl returns an implementation of the module's MsgServer interface
+// for the provided Keeper.
+func NewMsgServerImpl(keeper Keeper) types.MsgServer {
+ return &msgServer{keeper: keeper}
+}
+
+var _ types.MsgServer = msgServer{}
+
+// MintDeposit converts a delegation into staking derivatives and deposits it all into an earn vault
+func (m msgServer) MintDeposit(goCtx context.Context, msg *types.MsgMintDeposit) (*types.MsgMintDepositResponse, error) {
+ ctx := sdk.UnwrapSDKContext(goCtx)
+
+ depositor, err := sdk.AccAddressFromBech32(msg.Depositor)
+ if err != nil {
+ return nil, err
+ }
+ val, err := sdk.ValAddressFromBech32(msg.Validator)
+ if err != nil {
+ return nil, err
+ }
+
+ derivative, err := m.keeper.liquidKeeper.MintDerivative(ctx, depositor, val, msg.Amount)
+ if err != nil {
+ return nil, err
+ }
+ err = m.keeper.earnKeeper.Deposit(ctx, depositor, derivative, earntypes.STRATEGY_TYPE_SAVINGS)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx.EventManager().EmitEvent(
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
+ sdk.NewAttribute(sdk.AttributeKeySender, depositor.String()),
+ ),
+ )
+ return &types.MsgMintDepositResponse{}, nil
+}
+
+// DelegateMintDeposit delegates tokens to a validator, then converts them into staking derivatives,
+// then deposits to an earn vault.
+func (m msgServer) DelegateMintDeposit(goCtx context.Context, msg *types.MsgDelegateMintDeposit) (*types.MsgDelegateMintDepositResponse, error) {
+ ctx := sdk.UnwrapSDKContext(goCtx)
+
+ depositor, err := sdk.AccAddressFromBech32(msg.Depositor)
+ if err != nil {
+ return nil, err
+ }
+ valAddr, err := sdk.ValAddressFromBech32(msg.Validator)
+ if err != nil {
+ return nil, err
+ }
+ validator, found := m.keeper.stakingKeeper.GetValidator(ctx, valAddr)
+ if !found {
+ return nil, stakingtypes.ErrNoValidatorFound
+ }
+ bondDenom := m.keeper.stakingKeeper.BondDenom(ctx)
+ if msg.Amount.Denom != bondDenom {
+ return nil, sdkerrors.Wrapf(
+ sdkerrors.ErrInvalidRequest, "invalid coin denomination: got %s, expected %s", msg.Amount.Denom, bondDenom,
+ )
+ }
+ newShares, err := m.keeper.stakingKeeper.Delegate(ctx, depositor, msg.Amount.Amount, stakingtypes.Unbonded, validator, true)
+ if err != nil {
+ return nil, err
+ }
+
+ derivativeMinted, err := m.keeper.liquidKeeper.MintDerivative(ctx, depositor, valAddr, msg.Amount)
+ if err != nil {
+ return nil, err
+ }
+
+ err = m.keeper.earnKeeper.Deposit(ctx, depositor, derivativeMinted, earntypes.STRATEGY_TYPE_SAVINGS)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx.EventManager().EmitEvents(sdk.Events{
+ sdk.NewEvent(
+ stakingtypes.EventTypeDelegate,
+ sdk.NewAttribute(stakingtypes.AttributeKeyValidator, valAddr.String()),
+ sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
+ sdk.NewAttribute(stakingtypes.AttributeKeyNewShares, newShares.String()),
+ ),
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
+ sdk.NewAttribute(sdk.AttributeKeySender, depositor.String()),
+ ),
+ })
+
+ return &types.MsgDelegateMintDepositResponse{}, nil
+}
+
+// WithdrawBurn removes staking derivatives from an earn vault and converts them back to a staking delegation.
+func (m msgServer) WithdrawBurn(goCtx context.Context, msg *types.MsgWithdrawBurn) (*types.MsgWithdrawBurnResponse, error) {
+ ctx := sdk.UnwrapSDKContext(goCtx)
+
+ depositor, err := sdk.AccAddressFromBech32(msg.From)
+ if err != nil {
+ return nil, err
+ }
+ val, err := sdk.ValAddressFromBech32(msg.Validator)
+ if err != nil {
+ return nil, err
+ }
+
+ tokenAmount, err := m.keeper.liquidKeeper.DerivativeFromTokens(ctx, val, msg.Amount)
+ if err != nil {
+ return nil, err
+ }
+
+ err = m.keeper.earnKeeper.Withdraw(ctx, depositor, tokenAmount, earntypes.STRATEGY_TYPE_SAVINGS)
+ if err != nil {
+ return nil, err
+ }
+
+ _, err = m.keeper.liquidKeeper.BurnDerivative(ctx, depositor, val, tokenAmount)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx.EventManager().EmitEvent(
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
+ sdk.NewAttribute(sdk.AttributeKeySender, depositor.String()),
+ ),
+ )
+
+ return &types.MsgWithdrawBurnResponse{}, nil
+}
+
+// WithdrawBurnUndelegate removes staking derivatives from an earn vault, converts them to a staking delegation,
+// then undelegates them from their validator.
+func (m msgServer) WithdrawBurnUndelegate(goCtx context.Context, msg *types.MsgWithdrawBurnUndelegate) (*types.MsgWithdrawBurnUndelegateResponse, error) {
+ ctx := sdk.UnwrapSDKContext(goCtx)
+
+ depositor, err := sdk.AccAddressFromBech32(msg.From)
+ if err != nil {
+ return nil, err
+ }
+ val, err := sdk.ValAddressFromBech32(msg.Validator)
+ if err != nil {
+ return nil, err
+ }
+
+ tokenAmount, err := m.keeper.liquidKeeper.DerivativeFromTokens(ctx, val, msg.Amount)
+ if err != nil {
+ return nil, err
+ }
+
+ err = m.keeper.earnKeeper.Withdraw(ctx, depositor, tokenAmount, earntypes.STRATEGY_TYPE_SAVINGS)
+ if err != nil {
+ return nil, err
+ }
+
+ sharesReturned, err := m.keeper.liquidKeeper.BurnDerivative(ctx, depositor, val, tokenAmount)
+ if err != nil {
+ return nil, err
+ }
+
+ completionTime, err := m.keeper.stakingKeeper.Undelegate(ctx, depositor, val, sharesReturned)
+ if err != nil {
+ return nil, err
+ }
+
+ ctx.EventManager().EmitEvents(sdk.Events{
+ sdk.NewEvent(
+ stakingtypes.EventTypeUnbond,
+ sdk.NewAttribute(stakingtypes.AttributeKeyValidator, val.String()),
+ sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
+ sdk.NewAttribute(stakingtypes.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)),
+ ),
+ sdk.NewEvent(
+ sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
+ sdk.NewAttribute(sdk.AttributeKeySender, depositor.String()),
+ ),
+ })
+ return &types.MsgWithdrawBurnUndelegateResponse{}, nil
+}
diff --git a/x/router/keeper/msg_server_test.go b/x/router/keeper/msg_server_test.go
new file mode 100644
index 00000000..8e3d4950
--- /dev/null
+++ b/x/router/keeper/msg_server_test.go
@@ -0,0 +1,321 @@
+package keeper_test
+
+import (
+ "fmt"
+ "testing"
+ "time"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/cosmos/cosmos-sdk/x/staking"
+ stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+ "github.com/stretchr/testify/suite"
+
+ "github.com/kava-labs/kava/app"
+ earntypes "github.com/kava-labs/kava/x/earn/types"
+ "github.com/kava-labs/kava/x/router/keeper"
+ "github.com/kava-labs/kava/x/router/testutil"
+ "github.com/kava-labs/kava/x/router/types"
+)
+
+type msgServerTestSuite struct {
+ testutil.Suite
+
+ msgServer types.MsgServer
+}
+
+func (suite *msgServerTestSuite) SetupTest() {
+ suite.Suite.SetupTest()
+
+ suite.msgServer = keeper.NewMsgServerImpl(suite.Keeper)
+}
+
+func TestMsgServerTestSuite(t *testing.T) {
+ suite.Run(t, new(msgServerTestSuite))
+}
+
+func (suite *msgServerTestSuite) TestMintDeposit_Events() {
+ user, valAddr, delegation := suite.setupValidatorAndDelegation()
+ suite.setupEarnForDeposits(valAddr)
+
+ msg := types.NewMsgMintDeposit(
+ user,
+ valAddr,
+ suite.NewBondCoin(delegation),
+ )
+ _, err := suite.msgServer.MintDeposit(sdk.WrapSDKContext(suite.Ctx), msg)
+ suite.Require().NoError(err)
+
+ suite.EventsContains(suite.Ctx.EventManager().Events(),
+ sdk.NewEvent(sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
+ sdk.NewAttribute(sdk.AttributeKeySender, user.String()),
+ ),
+ )
+}
+
+func (suite *msgServerTestSuite) TestDelegateMintDeposit_Events() {
+ user, valAddr, balance := suite.setupValidator()
+ suite.setupEarnForDeposits(valAddr)
+
+ msg := types.NewMsgDelegateMintDeposit(
+ user,
+ valAddr,
+ suite.NewBondCoin(balance),
+ )
+ _, err := suite.msgServer.DelegateMintDeposit(sdk.WrapSDKContext(suite.Ctx), msg)
+ suite.Require().NoError(err)
+
+ suite.EventsContains(suite.Ctx.EventManager().Events(),
+ sdk.NewEvent(sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
+ sdk.NewAttribute(sdk.AttributeKeySender, user.String()),
+ ),
+ )
+ expectedShares := msg.Amount.Amount.ToDec() // no slashes so shares equal staked tokens
+ suite.EventsContains(suite.Ctx.EventManager().Events(),
+ sdk.NewEvent(
+ stakingtypes.EventTypeDelegate,
+ sdk.NewAttribute(stakingtypes.AttributeKeyValidator, msg.Validator),
+ sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
+ sdk.NewAttribute(stakingtypes.AttributeKeyNewShares, expectedShares.String()),
+ ),
+ )
+}
+
+func (suite *msgServerTestSuite) TestWithdrawBurn_Events() {
+ user, valAddr, delegated := suite.setupDerivatives()
+ // clear events from setup
+ suite.Ctx = suite.Ctx.WithEventManager(sdk.NewEventManager())
+
+ msg := types.NewMsgWithdrawBurn(
+ user,
+ valAddr,
+ // in this setup where the validator is not slashed, the derivative amount is equal to the staked balance
+ suite.NewBondCoin(delegated.Amount),
+ )
+ _, err := suite.msgServer.WithdrawBurn(sdk.WrapSDKContext(suite.Ctx), msg)
+ suite.Require().NoError(err)
+
+ suite.EventsContains(suite.Ctx.EventManager().Events(),
+ sdk.NewEvent(sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
+ sdk.NewAttribute(sdk.AttributeKeySender, user.String()),
+ ),
+ )
+}
+
+func (suite *msgServerTestSuite) TestWithdrawBurnUndelegate_Events() {
+ user, valAddr, delegated := suite.setupDerivatives()
+ // clear events from setup
+ suite.Ctx = suite.Ctx.WithEventManager(sdk.NewEventManager())
+
+ msg := types.NewMsgWithdrawBurnUndelegate(
+ user,
+ valAddr,
+ // in this setup where the validator is not slashed, the derivative amount is equal to the staked balance
+ suite.NewBondCoin(delegated.Amount),
+ )
+ _, err := suite.msgServer.WithdrawBurnUndelegate(sdk.WrapSDKContext(suite.Ctx), msg)
+ suite.Require().NoError(err)
+
+ suite.EventsContains(suite.Ctx.EventManager().Events(),
+ sdk.NewEvent(sdk.EventTypeMessage,
+ sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName),
+ sdk.NewAttribute(sdk.AttributeKeySender, user.String()),
+ ),
+ )
+ unbondingTime := suite.StakingKeeper.UnbondingTime(suite.Ctx)
+ completionTime := suite.Ctx.BlockTime().Add(unbondingTime)
+ suite.EventsContains(suite.Ctx.EventManager().Events(),
+ sdk.NewEvent(
+ stakingtypes.EventTypeUnbond,
+ sdk.NewAttribute(stakingtypes.AttributeKeyValidator, msg.Validator),
+ sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()),
+ sdk.NewAttribute(stakingtypes.AttributeKeyCompletionTime, completionTime.Format(time.RFC3339)),
+ ),
+ )
+}
+
+func (suite *msgServerTestSuite) TestMintDepositAndWithdrawBurn_TransferEntireBalance() {
+ _, addrs := app.GeneratePrivKeyAddressPairs(5)
+ valAccAddr, user := addrs[0], addrs[1]
+ valAddr := sdk.ValAddress(valAccAddr)
+
+ derivativeDenom := suite.setupEarnForDeposits(valAddr)
+
+ suite.CreateAccountWithAddress(valAccAddr, suite.NewBondCoins(sdk.NewInt(1e9)))
+ vesting := sdk.NewInt(1000)
+ suite.CreateVestingAccountWithAddress(user,
+ suite.NewBondCoins(sdk.NewInt(1e9).Add(vesting)),
+ suite.NewBondCoins(vesting),
+ )
+
+ // Create a slashed validator, where the delegator owns fractional tokens.
+ suite.CreateNewUnbondedValidator(valAddr, sdk.NewInt(1e9))
+ suite.CreateDelegation(valAddr, user, sdk.NewInt(1e9))
+ staking.EndBlocker(suite.Ctx, suite.StakingKeeper)
+ suite.SlashValidator(valAddr, sdk.MustNewDecFromStr("0.666666666666666667"))
+
+ // Query the full staked balance and convert it all to derivatives
+ // user technically 333_333_333.333333333333333333 staked tokens without rounding
+ delegation := suite.QueryStaking_Delegation(valAddr, user)
+ suite.Equal(sdk.NewInt(333_333_333), delegation.Balance.Amount)
+
+ msgDeposit := types.NewMsgMintDeposit(
+ user,
+ valAddr,
+ delegation.Balance,
+ )
+ _, err := suite.msgServer.MintDeposit(sdk.WrapSDKContext(suite.Ctx), msgDeposit)
+ suite.Require().NoError(err)
+
+ // There should be no extractable balance left in delegation
+ suite.DelegationBalanceLessThan(valAddr, user, sdk.NewInt(1))
+ // All derivative coins should be deposited to earn
+ suite.AccountBalanceOfEqual(user, derivativeDenom, sdk.ZeroInt())
+ // Earn vault has all minted derivatives
+ suite.VaultAccountValueEqual(user, sdk.NewInt64Coin(derivativeDenom, 999_999_998)) // 2 lost in conversion
+
+ // Query the full kava balance of the earn deposit and convert all to a delegation
+ deposit := suite.QueryEarn_VaultValue(user, "bkava")
+ suite.Equal(suite.NewBondCoins(sdk.NewInt(333_333_332)), deposit.Value) // 1 lost due to lost shares
+
+ msgWithdraw := types.NewMsgWithdrawBurn(
+ user,
+ valAddr,
+ deposit.Value[0],
+ )
+ _, err = suite.msgServer.WithdrawBurn(sdk.WrapSDKContext(suite.Ctx), msgWithdraw)
+ suite.Require().NoError(err)
+
+ // There should be no earn deposit left (earn removes dust amounts)
+ suite.VaultAccountSharesEqual(user, nil)
+ // All derivative coins should be converted to a delegation
+ suite.AccountBalanceOfEqual(user, derivativeDenom, sdk.ZeroInt())
+ // The user should get back most of their original deposited balance
+ suite.DelegationBalanceInDeltaBelow(valAddr, user, sdk.NewInt(333_333_332), sdk.NewInt(2))
+}
+
+func (suite *msgServerTestSuite) TestDelegateMintDepositAndWithdrawBurnUndelegate_TransferEntireBalance() {
+ _, addrs := app.GeneratePrivKeyAddressPairs(5)
+ valAccAddr, user := addrs[0], addrs[1]
+ valAddr := sdk.ValAddress(valAccAddr)
+
+ derivativeDenom := suite.setupEarnForDeposits(valAddr)
+
+ valBalance := sdk.NewInt(1e9)
+ suite.CreateAccountWithAddress(valAccAddr, suite.NewBondCoins(valBalance))
+
+ // Create a slashed validator, where a future delegator will own fractional tokens.
+ suite.CreateNewUnbondedValidator(valAddr, valBalance)
+ staking.EndBlocker(suite.Ctx, suite.StakingKeeper)
+ suite.SlashValidator(valAddr, sdk.MustNewDecFromStr("0.4")) // tokens remaining 600_000_000
+
+ userBalance := sdk.NewInt(100e6)
+ vesting := sdk.NewInt(1000)
+ suite.CreateVestingAccountWithAddress(user,
+ suite.NewBondCoins(userBalance.Add(vesting)),
+ suite.NewBondCoins(vesting),
+ )
+
+ // Query the full vested balance and convert it all to derivatives
+ balance := suite.QueryBank_SpendableBalance(user)
+ suite.Equal(suite.NewBondCoins(userBalance), balance)
+
+ // When delegation is created it will have 166_666_666.666666666666666666 shares
+ // newShares = validatorShares * newTokens/validatorTokens, truncated to 18 decimals
+ msgDeposit := types.NewMsgDelegateMintDeposit(
+ user,
+ valAddr,
+ balance[0],
+ )
+ _, err := suite.msgServer.DelegateMintDeposit(sdk.WrapSDKContext(suite.Ctx), msgDeposit)
+ suite.Require().NoError(err)
+
+ // All spendable balance should be withdrawn
+ suite.AccountSpendableBalanceEqual(user, nil)
+ // Since shares are newly created, the exact amount should be converted to derivatives, leaving none behind
+ suite.DelegationSharesEqual(valAddr, user, sdk.ZeroDec())
+ // All derivative coins should be deposited to earn
+ suite.AccountBalanceOfEqual(user, derivativeDenom, sdk.ZeroInt())
+
+ suite.VaultAccountValueEqual(user, sdk.NewInt64Coin(derivativeDenom, 166_666_666))
+
+ // Query the full kava balance of the earn deposit and convert all to a delegation
+ deposit := suite.QueryEarn_VaultValue(user, "bkava")
+ suite.Equal(suite.NewBondCoins(sdk.NewInt(99_999_999)), deposit.Value) // 1 lost due to truncating shares to derivatives
+
+ msgWithdraw := types.NewMsgWithdrawBurnUndelegate(
+ user,
+ valAddr,
+ deposit.Value[0],
+ )
+ _, err = suite.msgServer.WithdrawBurnUndelegate(sdk.WrapSDKContext(suite.Ctx), msgWithdraw)
+ suite.Require().NoError(err)
+
+ // There should be no earn deposit left (earn removes dust amounts)
+ suite.VaultAccountSharesEqual(user, nil)
+ // All derivative coins should be converted to a delegation
+ suite.AccountBalanceOfEqual(user, derivativeDenom, sdk.ZeroInt())
+ // There should be zero shares left because undelegate removes all created by burn.
+ suite.AccountBalanceOfEqual(user, derivativeDenom, sdk.ZeroInt())
+ // User should have most of their original balance back in an unbonding delegation
+ suite.UnbondingDelegationInDeltaBelow(valAddr, user, userBalance, sdk.NewInt(2))
+}
+
+func (suite *msgServerTestSuite) setupValidator() (sdk.AccAddress, sdk.ValAddress, sdk.Int) {
+ _, addrs := app.GeneratePrivKeyAddressPairs(5)
+ valAccAddr, user := addrs[0], addrs[1]
+ valAddr := sdk.ValAddress(valAccAddr)
+
+ balance := sdk.NewInt(1e9)
+
+ suite.CreateAccountWithAddress(valAccAddr, suite.NewBondCoins(balance))
+ suite.CreateAccountWithAddress(user, suite.NewBondCoins(balance))
+
+ suite.CreateNewUnbondedValidator(valAddr, balance)
+ staking.EndBlocker(suite.Ctx, suite.StakingKeeper)
+ return user, valAddr, balance
+}
+
+func (suite *msgServerTestSuite) setupValidatorAndDelegation() (sdk.AccAddress, sdk.ValAddress, sdk.Int) {
+ _, addrs := app.GeneratePrivKeyAddressPairs(5)
+ valAccAddr, user := addrs[0], addrs[1]
+ valAddr := sdk.ValAddress(valAccAddr)
+
+ balance := sdk.NewInt(1e9)
+
+ suite.CreateAccountWithAddress(valAccAddr, suite.NewBondCoins(balance))
+ suite.CreateAccountWithAddress(user, suite.NewBondCoins(balance))
+
+ suite.CreateNewUnbondedValidator(valAddr, balance)
+ suite.CreateDelegation(valAddr, user, balance)
+ staking.EndBlocker(suite.Ctx, suite.StakingKeeper)
+ return user, valAddr, balance
+}
+
+func (suite *msgServerTestSuite) setupEarnForDeposits(valAddr sdk.ValAddress) string {
+ suite.CreateVault("bkava", earntypes.StrategyTypes{earntypes.STRATEGY_TYPE_SAVINGS}, false, nil)
+ derivativeDenom := fmt.Sprintf("bkava-%s", valAddr)
+ suite.SetSavingsSupportedDenoms([]string{derivativeDenom})
+ return derivativeDenom
+}
+
+func (suite *msgServerTestSuite) setupDerivatives() (sdk.AccAddress, sdk.ValAddress, sdk.Coin) {
+ user, valAddr, delegation := suite.setupValidatorAndDelegation()
+ suite.setupEarnForDeposits(valAddr)
+
+ msg := types.NewMsgMintDeposit(
+ user,
+ valAddr,
+ suite.NewBondCoin(delegation),
+ )
+ _, err := suite.msgServer.MintDeposit(sdk.WrapSDKContext(suite.Ctx), msg)
+ suite.Require().NoError(err)
+
+ derivativeDenom := fmt.Sprintf("bkava-%s", valAddr)
+ derivatives, err := suite.EarnKeeper.GetVaultAccountValue(suite.Ctx, derivativeDenom, user)
+ suite.Require().NoError(err)
+
+ return user, valAddr, derivatives
+}
diff --git a/x/router/module.go b/x/router/module.go
new file mode 100644
index 00000000..500c1d87
--- /dev/null
+++ b/x/router/module.go
@@ -0,0 +1,137 @@
+package router
+
+import (
+ "encoding/json"
+
+ "github.com/cosmos/cosmos-sdk/client"
+ "github.com/cosmos/cosmos-sdk/codec"
+ codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/cosmos/cosmos-sdk/types/module"
+ "github.com/gorilla/mux"
+ "github.com/grpc-ecosystem/grpc-gateway/runtime"
+ "github.com/spf13/cobra"
+ abci "github.com/tendermint/tendermint/abci/types"
+
+ "github.com/kava-labs/kava/x/router/client/cli"
+ "github.com/kava-labs/kava/x/router/keeper"
+ "github.com/kava-labs/kava/x/router/types"
+)
+
+var (
+ _ module.AppModule = AppModule{}
+ _ module.AppModuleBasic = AppModuleBasic{}
+)
+
+// AppModuleBasic app module basics object
+type AppModuleBasic struct{}
+
+// Name get module name
+func (AppModuleBasic) Name() string {
+ return types.ModuleName
+}
+
+// RegisterLegacyAminoCodec register module codec
+func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+ types.RegisterLegacyAminoCodec(cdc)
+}
+
+// DefaultGenesis default genesis state
+func (AppModuleBasic) DefaultGenesis(_ codec.JSONCodec) json.RawMessage {
+ return []byte("{}")
+}
+
+// ValidateGenesis module validate genesis
+func (AppModuleBasic) ValidateGenesis(_ codec.JSONCodec, _ client.TxEncodingConfig, _ json.RawMessage) error {
+ return nil
+}
+
+// RegisterInterfaces implements InterfaceModule.RegisterInterfaces
+func (a AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
+ types.RegisterInterfaces(registry)
+}
+
+// RegisterRESTRoutes registers REST routes for the module.
+func (a AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {}
+
+// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module.
+func (a AppModuleBasic) RegisterGRPCGatewayRoutes(_ client.Context, _ *runtime.ServeMux) {
+}
+
+// GetTxCmd returns the root tx command for the module.
+func (AppModuleBasic) GetTxCmd() *cobra.Command {
+ return cli.GetTxCmd()
+}
+
+// GetQueryCmd returns no root query command for the module.
+func (AppModuleBasic) GetQueryCmd() *cobra.Command {
+ return nil
+}
+
+//____________________________________________________________________________
+
+// AppModule app module type
+type AppModule struct {
+ AppModuleBasic
+
+ keeper keeper.Keeper
+}
+
+// NewAppModule creates a new AppModule object
+func NewAppModule(keeper keeper.Keeper) AppModule {
+ return AppModule{
+ AppModuleBasic: AppModuleBasic{},
+ keeper: keeper,
+ }
+}
+
+// Name module name
+func (am AppModule) Name() string {
+ return am.AppModuleBasic.Name()
+}
+
+// RegisterInvariants register module invariants
+func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
+
+// Route module message route name
+func (am AppModule) Route() sdk.Route {
+ return sdk.Route{}
+}
+
+// QuerierRoute module querier route name
+func (AppModule) QuerierRoute() string {
+ return ""
+}
+
+// LegacyQuerierHandler returns no sdk.Querier.
+func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier {
+ return nil
+}
+
+// ConsensusVersion implements AppModule/ConsensusVersion.
+func (AppModule) ConsensusVersion() uint64 {
+ return 1
+}
+
+// RegisterServices registers module services.
+func (am AppModule) RegisterServices(cfg module.Configurator) {
+ types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
+}
+
+// InitGenesis module init-genesis
+func (am AppModule) InitGenesis(_ sdk.Context, _ codec.JSONCodec, _ json.RawMessage) []abci.ValidatorUpdate {
+ return []abci.ValidatorUpdate{}
+}
+
+// ExportGenesis module export genesis
+func (am AppModule) ExportGenesis(_ sdk.Context, cdc codec.JSONCodec) json.RawMessage {
+ return am.DefaultGenesis(cdc)
+}
+
+// BeginBlock module begin-block
+func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
+
+// EndBlock module end-block
+func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
+ return []abci.ValidatorUpdate{}
+}
diff --git a/x/router/testutil/suite.go b/x/router/testutil/suite.go
new file mode 100644
index 00000000..c5517904
--- /dev/null
+++ b/x/router/testutil/suite.go
@@ -0,0 +1,361 @@
+package testutil
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
+ "github.com/cosmos/cosmos-sdk/simapp"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+ vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
+ bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+ banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+ stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
+ stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+ "github.com/stretchr/testify/suite"
+ abci "github.com/tendermint/tendermint/abci/types"
+ tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+ tmtime "github.com/tendermint/tendermint/types/time"
+
+ "github.com/kava-labs/kava/app"
+ earnkeeper "github.com/kava-labs/kava/x/earn/keeper"
+ earntypes "github.com/kava-labs/kava/x/earn/types"
+ "github.com/kava-labs/kava/x/router/keeper"
+ savingstypes "github.com/kava-labs/kava/x/savings/types"
+)
+
+// Test suite used for all keeper tests
+type Suite struct {
+ suite.Suite
+ App app.TestApp
+ Ctx sdk.Context
+ Keeper keeper.Keeper
+ BankKeeper bankkeeper.Keeper
+ StakingKeeper stakingkeeper.Keeper
+ EarnKeeper earnkeeper.Keeper
+}
+
+// The default state used by each test
+func (suite *Suite) SetupTest() {
+ tApp := app.NewTestApp()
+ ctx := tApp.NewContext(true, tmproto.Header{Height: 1, Time: tmtime.Now()})
+
+ tApp.InitializeFromGenesisStates()
+
+ suite.App = tApp
+ suite.Ctx = ctx
+ suite.Keeper = tApp.GetRouterKeeper()
+ suite.StakingKeeper = tApp.GetStakingKeeper()
+ suite.BankKeeper = tApp.GetBankKeeper()
+ suite.EarnKeeper = tApp.GetEarnKeeper()
+}
+
+// CreateAccount creates a new account from the provided balance and address
+func (suite *Suite) CreateAccountWithAddress(addr sdk.AccAddress, initialBalance sdk.Coins) authtypes.AccountI {
+ ak := suite.App.GetAccountKeeper()
+
+ acc := ak.NewAccountWithAddress(suite.Ctx, addr)
+ ak.SetAccount(suite.Ctx, acc)
+
+ err := simapp.FundAccount(suite.BankKeeper, suite.Ctx, acc.GetAddress(), initialBalance)
+ suite.Require().NoError(err)
+
+ return acc
+}
+
+// CreateVestingAccount creates a new vesting account. `vestingBalance` should be a fraction of `initialBalance`.
+func (suite *Suite) CreateVestingAccountWithAddress(addr sdk.AccAddress, initialBalance sdk.Coins, vestingBalance sdk.Coins) authtypes.AccountI {
+ if vestingBalance.IsAnyGT(initialBalance) {
+ panic("vesting balance must be less than initial balance")
+ }
+ acc := suite.CreateAccountWithAddress(addr, initialBalance)
+ bacc := acc.(*authtypes.BaseAccount)
+
+ periods := vestingtypes.Periods{
+ vestingtypes.Period{
+ Length: 31556952,
+ Amount: vestingBalance,
+ },
+ }
+ vacc := vestingtypes.NewPeriodicVestingAccount(bacc, vestingBalance, suite.Ctx.BlockTime().Unix(), periods)
+ suite.App.GetAccountKeeper().SetAccount(suite.Ctx, vacc)
+ return vacc
+}
+
+// AddCoinsToModule adds coins to the a module account, creating it if it doesn't exist.
+func (suite *Suite) AddCoinsToModule(module string, amount sdk.Coins) {
+ err := simapp.FundModuleAccount(suite.BankKeeper, suite.Ctx, module, amount)
+ suite.Require().NoError(err)
+}
+
+// AccountBalanceEqual checks if an account has the specified coins.
+func (suite *Suite) AccountBalanceEqual(addr sdk.AccAddress, coins sdk.Coins) {
+ balance := suite.BankKeeper.GetAllBalances(suite.Ctx, addr)
+ suite.Equalf(coins, balance, "expected account balance to equal coins %s, but got %s", coins, balance)
+}
+
+// AccountBalanceOfEqual checks if an account has the specified amount of one denom.
+func (suite *Suite) AccountBalanceOfEqual(addr sdk.AccAddress, denom string, amount sdk.Int) {
+ balance := suite.BankKeeper.GetBalance(suite.Ctx, addr, denom).Amount
+ suite.Equalf(amount, balance, "expected account balance to have %[1]s%[2]s, but got %[3]s%[2]s", amount, denom, balance)
+}
+
+// AccountSpendableBalanceEqual checks if an account has the specified coins unlocked.
+func (suite *Suite) AccountSpendableBalanceEqual(addr sdk.AccAddress, amount sdk.Coins) {
+ balance := suite.BankKeeper.SpendableCoins(suite.Ctx, addr)
+ suite.Equalf(amount, balance, "expected account spendable balance to equal coins %s, but got %s", amount, balance)
+}
+
+func (suite *Suite) QueryBank_SpendableBalance(user sdk.AccAddress) sdk.Coins {
+ res, err := suite.BankKeeper.SpendableBalances(
+ sdk.WrapSDKContext(suite.Ctx),
+ &banktypes.QuerySpendableBalancesRequest{
+ Address: user.String(),
+ },
+ )
+ suite.Require().NoError(err)
+ return *&res.Balances
+}
+
+func (suite *Suite) deliverMsgCreateValidator(ctx sdk.Context, address sdk.ValAddress, selfDelegation sdk.Coin) error {
+ msg, err := stakingtypes.NewMsgCreateValidator(
+ address,
+ ed25519.GenPrivKey().PubKey(),
+ selfDelegation,
+ stakingtypes.Description{},
+ stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
+ sdk.NewInt(1e6),
+ )
+ if err != nil {
+ return err
+ }
+
+ msgServer := stakingkeeper.NewMsgServerImpl(suite.StakingKeeper)
+ _, err = msgServer.CreateValidator(sdk.WrapSDKContext(suite.Ctx), msg)
+ return err
+}
+
+// NewBondCoin creates a Coin with the current staking denom.
+func (suite *Suite) NewBondCoin(amount sdk.Int) sdk.Coin {
+ stakingDenom := suite.StakingKeeper.BondDenom(suite.Ctx)
+ return sdk.NewCoin(stakingDenom, amount)
+}
+
+// NewBondCoins creates Coins with the current staking denom.
+func (suite *Suite) NewBondCoins(amount sdk.Int) sdk.Coins {
+ return sdk.NewCoins(suite.NewBondCoin(amount))
+}
+
+// CreateNewUnbondedValidator creates a new validator in the staking module.
+// New validators are unbonded until the end blocker is run.
+func (suite *Suite) CreateNewUnbondedValidator(addr sdk.ValAddress, selfDelegation sdk.Int) stakingtypes.Validator {
+ // Create a validator
+ err := suite.deliverMsgCreateValidator(suite.Ctx, addr, suite.NewBondCoin(selfDelegation))
+ suite.Require().NoError(err)
+
+ // New validators are created in an unbonded state. Note if the end blocker is run later this validator could become bonded.
+
+ validator, found := suite.StakingKeeper.GetValidator(suite.Ctx, addr)
+ suite.Require().True(found)
+ return validator
+}
+
+// SlashValidator burns tokens staked in a validator. new_tokens = old_tokens * (1-slashFraction)
+func (suite *Suite) SlashValidator(addr sdk.ValAddress, slashFraction sdk.Dec) {
+ validator, found := suite.StakingKeeper.GetValidator(suite.Ctx, addr)
+ suite.Require().True(found)
+ consAddr, err := validator.GetConsAddr()
+ suite.Require().NoError(err)
+
+ // Assume infraction was at current height. Note unbonding delegations and redelegations are only slashed if created after
+ // the infraction height so none will be slashed.
+ infractionHeight := suite.Ctx.BlockHeight()
+
+ power := suite.StakingKeeper.TokensToConsensusPower(suite.Ctx, validator.GetTokens())
+
+ suite.StakingKeeper.Slash(suite.Ctx, consAddr, infractionHeight, power, slashFraction)
+}
+
+// CreateDelegation delegates tokens to a validator.
+func (suite *Suite) CreateDelegation(valAddr sdk.ValAddress, delegator sdk.AccAddress, amount sdk.Int) sdk.Dec {
+ stakingDenom := suite.StakingKeeper.BondDenom(suite.Ctx)
+ msg := stakingtypes.NewMsgDelegate(
+ delegator,
+ valAddr,
+ sdk.NewCoin(stakingDenom, amount),
+ )
+
+ msgServer := stakingkeeper.NewMsgServerImpl(suite.StakingKeeper)
+ _, err := msgServer.Delegate(sdk.WrapSDKContext(suite.Ctx), msg)
+ suite.Require().NoError(err)
+
+ del, found := suite.StakingKeeper.GetDelegation(suite.Ctx, delegator, valAddr)
+ suite.Require().True(found)
+ return del.Shares
+}
+
+// DelegationSharesEqual checks if a delegation has the specified shares.
+// It expects delegations with zero shares to not be stored in state.
+func (suite *Suite) DelegationSharesEqual(valAddr sdk.ValAddress, delegator sdk.AccAddress, shares sdk.Dec) bool {
+ del, found := suite.StakingKeeper.GetDelegation(suite.Ctx, delegator, valAddr)
+
+ if shares.IsZero() {
+ return suite.Falsef(found, "expected delegator to not be found, got %s shares", del.Shares)
+ } else {
+ res := suite.True(found, "expected delegator to be found")
+ return res && suite.Truef(shares.Equal(del.Shares), "expected %s delegator shares but got %s", shares, del.Shares)
+ }
+}
+
+// DelegationBalanceLessThan checks if a delegation's staked token balance is less the specified amount.
+// It treats not found delegations as having zero shares.
+func (suite *Suite) DelegationBalanceLessThan(valAddr sdk.ValAddress, delegator sdk.AccAddress, max sdk.Int) bool {
+ shares := sdk.ZeroDec()
+ del, found := suite.StakingKeeper.GetDelegation(suite.Ctx, delegator, valAddr)
+ if found {
+ shares = del.Shares
+ }
+
+ val, found := suite.StakingKeeper.GetValidator(suite.Ctx, valAddr)
+ suite.Require().Truef(found, "expected validator to be found")
+
+ tokens := val.TokensFromShares(shares).TruncateInt()
+
+ return suite.Truef(tokens.LT(max), "expected delegation balance to be less than %s, got %s", max, tokens)
+}
+
+// DelegationBalanceInDeltaBelow checks if a delegation's staked token balance is between `expected` and `expected - delta` inclusive.
+// It treats not found delegations as having zero shares.
+func (suite *Suite) DelegationBalanceInDeltaBelow(valAddr sdk.ValAddress, delegator sdk.AccAddress, expected, delta sdk.Int) bool {
+ shares := sdk.ZeroDec()
+ del, found := suite.StakingKeeper.GetDelegation(suite.Ctx, delegator, valAddr)
+ if found {
+ shares = del.Shares
+ }
+
+ val, found := suite.StakingKeeper.GetValidator(suite.Ctx, valAddr)
+ suite.Require().Truef(found, "expected validator to be found")
+
+ tokens := val.TokensFromShares(shares).TruncateInt()
+
+ lte := suite.Truef(tokens.LTE(expected), "expected delegation balance to be less than or equal to %s, got %s", expected, tokens)
+ gte := suite.Truef(tokens.GTE(expected.Sub(delta)), "expected delegation balance to be greater than or equal to %s, got %s", expected.Sub(delta), tokens)
+ return lte && gte
+}
+
+// UnbondingDelegationInDeltaBelow checks if the total balance in an unbonding delegation is between `expected` and `expected - delta` inclusive.
+func (suite *Suite) UnbondingDelegationInDeltaBelow(valAddr sdk.ValAddress, delegator sdk.AccAddress, expected, delta sdk.Int) bool {
+ tokens := sdk.ZeroInt()
+ ubd, found := suite.StakingKeeper.GetUnbondingDelegation(suite.Ctx, delegator, valAddr)
+ if found {
+ for _, entry := range ubd.Entries {
+ tokens = tokens.Add(entry.Balance)
+ }
+ }
+
+ lte := suite.Truef(tokens.LTE(expected), "expected unbonding delegation balance to be less than or equal to %s, got %s", expected, tokens)
+ gte := suite.Truef(tokens.GTE(expected.Sub(delta)), "expected unbonding delegation balance to be greater than or equal to %s, got %s", expected.Sub(delta), tokens)
+ return lte && gte
+}
+
+func (suite *Suite) QueryStaking_Delegation(valAddr sdk.ValAddress, delegator sdk.AccAddress) stakingtypes.DelegationResponse {
+ stakingQuery := stakingkeeper.Querier{Keeper: suite.StakingKeeper}
+ res, err := stakingQuery.Delegation(
+ sdk.WrapSDKContext(suite.Ctx),
+ &stakingtypes.QueryDelegationRequest{
+ DelegatorAddr: delegator.String(),
+ ValidatorAddr: valAddr.String(),
+ },
+ )
+ suite.Require().NoError(err)
+ return *res.DelegationResponse
+}
+
+// EventsContains asserts that the expected event is in the provided events
+func (suite *Suite) EventsContains(events sdk.Events, expectedEvent sdk.Event) {
+ foundMatch := false
+ for _, event := range events {
+ if event.Type == expectedEvent.Type {
+ if reflect.DeepEqual(attrsToMap(expectedEvent.Attributes), attrsToMap(event.Attributes)) {
+ foundMatch = true
+ }
+ }
+ }
+
+ suite.True(foundMatch, fmt.Sprintf("event of type %s not found or did not match", expectedEvent.Type))
+}
+
+func attrsToMap(attrs []abci.EventAttribute) []sdk.Attribute {
+ out := []sdk.Attribute{}
+
+ for _, attr := range attrs {
+ out = append(out, sdk.NewAttribute(string(attr.Key), string(attr.Value)))
+ }
+
+ return out
+}
+
+// CreateVault adds a new earn vault to the earn keeper parameters
+func (suite *Suite) CreateVault(
+ vaultDenom string,
+ vaultStrategies earntypes.StrategyTypes,
+ isPrivateVault bool,
+ allowedDepositors []sdk.AccAddress,
+) {
+ vault := earntypes.NewAllowedVault(vaultDenom, vaultStrategies, isPrivateVault, allowedDepositors)
+
+ allowedVaults := suite.EarnKeeper.GetAllowedVaults(suite.Ctx)
+ allowedVaults = append(allowedVaults, vault)
+
+ params := earntypes.NewParams(allowedVaults)
+
+ suite.EarnKeeper.SetParams(
+ suite.Ctx,
+ params,
+ )
+}
+
+// SetSavingsSupportedDenoms overwrites the list of supported denoms in the savings module params.
+func (suite *Suite) SetSavingsSupportedDenoms(denoms []string) {
+ sk := suite.App.GetSavingsKeeper()
+ sk.SetParams(suite.Ctx, savingstypes.NewParams(denoms))
+}
+
+// VaultAccountValueEqual asserts that the vault account value matches the provided coin amount.
+func (suite *Suite) VaultAccountValueEqual(acc sdk.AccAddress, coin sdk.Coin) {
+
+ accVaultBal, err := suite.EarnKeeper.GetVaultAccountValue(suite.Ctx, coin.Denom, acc)
+ suite.Require().NoError(err)
+
+ suite.Require().Truef(
+ coin.Equal(accVaultBal),
+ "expected account vault balance to equal %s, but got %s",
+ coin, accVaultBal,
+ )
+}
+
+// VaultAccountSharesEqual asserts that the vault account shares match the provided values.
+func (suite *Suite) VaultAccountSharesEqual(acc sdk.AccAddress, shares earntypes.VaultShares) { // TODO
+
+ accVaultShares, found := suite.EarnKeeper.GetVaultAccountShares(suite.Ctx, acc)
+ if !found {
+ suite.Empty(shares)
+ } else {
+ suite.Equal(shares, accVaultShares)
+ }
+}
+
+func (suite *Suite) QueryEarn_VaultValue(depositor sdk.AccAddress, vaultDenom string) earntypes.DepositResponse {
+ earnQuery := earnkeeper.NewQueryServerImpl(suite.EarnKeeper)
+ res, err := earnQuery.Deposits(
+ sdk.WrapSDKContext(suite.Ctx),
+ &earntypes.QueryDepositsRequest{
+ Depositor: depositor.String(),
+ Denom: vaultDenom,
+ },
+ )
+ suite.Require().NoError(err)
+ suite.Require().Equalf(1, len(res.Deposits), "while earn supports one vault per denom, deposits response should be length 1")
+ return res.Deposits[0]
+}
diff --git a/x/router/types/codec.go b/x/router/types/codec.go
new file mode 100644
index 00000000..3bee22eb
--- /dev/null
+++ b/x/router/types/codec.go
@@ -0,0 +1,42 @@
+package types
+
+import (
+ "github.com/cosmos/cosmos-sdk/codec"
+ "github.com/cosmos/cosmos-sdk/codec/types"
+
+ cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ "github.com/cosmos/cosmos-sdk/types/msgservice"
+)
+
+// RegisterLegacyAminoCodec registers all the necessary types and interfaces for the module.
+func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+ cdc.RegisterConcrete(&MsgMintDeposit{}, "router/MsgMintDeposit", nil)
+ cdc.RegisterConcrete(&MsgDelegateMintDeposit{}, "router/MsgDelegateMintDeposit", nil)
+ cdc.RegisterConcrete(&MsgWithdrawBurn{}, "router/MsgWithdrawBurn", nil)
+ cdc.RegisterConcrete(&MsgWithdrawBurnUndelegate{}, "router/MsgWithdrawBurnUndelegate", nil)
+}
+
+// RegisterInterfaces registers proto messages under their interfaces for unmarshalling,
+// in addition to registering the msg service for handling tx msgs
+func RegisterInterfaces(registry types.InterfaceRegistry) {
+ registry.RegisterImplementations((*sdk.Msg)(nil),
+ &MsgMintDeposit{},
+ &MsgDelegateMintDeposit{},
+ &MsgWithdrawBurn{},
+ &MsgWithdrawBurnUndelegate{},
+ )
+
+ msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
+}
+
+var (
+ amino = codec.NewLegacyAmino()
+ // ModuleCdc represents the legacy amino codec for the module
+ ModuleCdc = codec.NewAminoCodec(amino)
+)
+
+func init() {
+ RegisterLegacyAminoCodec(amino)
+ cryptocodec.RegisterCrypto(amino)
+}
diff --git a/x/router/types/common_test.go b/x/router/types/common_test.go
new file mode 100644
index 00000000..e88e1d5e
--- /dev/null
+++ b/x/router/types/common_test.go
@@ -0,0 +1,13 @@
+package types_test
+
+import (
+ "os"
+ "testing"
+
+ "github.com/kava-labs/kava/app"
+)
+
+func TestMain(m *testing.M) {
+ app.SetSDKConfig()
+ os.Exit(m.Run())
+}
diff --git a/x/router/types/expected_keepers.go b/x/router/types/expected_keepers.go
new file mode 100644
index 00000000..4a3dd382
--- /dev/null
+++ b/x/router/types/expected_keepers.go
@@ -0,0 +1,34 @@
+package types
+
+import (
+ "time"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+
+ earntypes "github.com/kava-labs/kava/x/earn/types"
+)
+
+type StakingKeeper interface {
+ BondDenom(ctx sdk.Context) (res string)
+ GetValidator(ctx sdk.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, found bool)
+
+ Delegate(
+ ctx sdk.Context, delAddr sdk.AccAddress, bondAmt sdk.Int, tokenSrc stakingtypes.BondStatus,
+ validator stakingtypes.Validator, subtractAccount bool,
+ ) (newShares sdk.Dec, err error)
+ Undelegate(
+ ctx sdk.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress, sharesAmount sdk.Dec,
+ ) (time.Time, error)
+}
+
+type LiquidKeeper interface {
+ DerivativeFromTokens(ctx sdk.Context, valAddr sdk.ValAddress, amount sdk.Coin) (sdk.Coin, error)
+ MintDerivative(ctx sdk.Context, delegatorAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) (sdk.Coin, error)
+ BurnDerivative(ctx sdk.Context, delegatorAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) (sdk.Dec, error)
+}
+
+type EarnKeeper interface {
+ Deposit(ctx sdk.Context, depositor sdk.AccAddress, amount sdk.Coin, depositStrategy earntypes.StrategyType) error
+ Withdraw(ctx sdk.Context, from sdk.AccAddress, wantAmount sdk.Coin, withdrawStrategy earntypes.StrategyType) error
+}
diff --git a/x/router/types/keys.go b/x/router/types/keys.go
new file mode 100644
index 00000000..dbde25a7
--- /dev/null
+++ b/x/router/types/keys.go
@@ -0,0 +1,9 @@
+package types
+
+const (
+ // ModuleName name that will be used throughout the module
+ ModuleName = "router"
+
+ // RouterKey top level router key
+ RouterKey = ModuleName
+)
diff --git a/x/router/types/msg.go b/x/router/types/msg.go
new file mode 100644
index 00000000..e0e7104e
--- /dev/null
+++ b/x/router/types/msg.go
@@ -0,0 +1,201 @@
+package types
+
+import (
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ "github.com/cosmos/cosmos-sdk/x/auth/legacy/legacytx"
+)
+
+const (
+ // TypeMsgMintDeposit defines the type for MsgMintDeposit
+ TypeMsgMintDeposit = "mint_deposit"
+ // TypeMsgDelegateMintDeposit defines the type for MsgDelegateMintDeposit
+ TypeMsgDelegateMintDeposit = "delegate_mint_deposit"
+ // TypeMsgWithdrawBurn defines the type for MsgWithdrawBurn
+ TypeMsgWithdrawBurn = "withdraw_burn"
+ // TypeMsgWithdrawBurnUndelegate defines the type for MsgWithdrawBurnUndelegate
+ TypeMsgWithdrawBurnUndelegate = "withdraw_burn_undelegate"
+)
+
+var (
+ _ sdk.Msg = &MsgMintDeposit{}
+ _ legacytx.LegacyMsg = &MsgMintDeposit{}
+ _ sdk.Msg = &MsgDelegateMintDeposit{}
+ _ legacytx.LegacyMsg = &MsgDelegateMintDeposit{}
+ _ sdk.Msg = &MsgWithdrawBurn{}
+ _ legacytx.LegacyMsg = &MsgWithdrawBurn{}
+ _ sdk.Msg = &MsgWithdrawBurnUndelegate{}
+ _ legacytx.LegacyMsg = &MsgWithdrawBurnUndelegate{}
+)
+
+// NewMsgMintDeposit returns a new MsgMintDeposit.
+func NewMsgMintDeposit(depositor sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) *MsgMintDeposit {
+ return &MsgMintDeposit{
+ Depositor: depositor.String(),
+ Validator: validator.String(),
+ Amount: amount,
+ }
+}
+
+// Route return the message type used for routing the message.
+func (msg MsgMintDeposit) Route() string { return RouterKey }
+
+// Type returns a human-readable string for the message, intended for utilization within tags.
+func (msg MsgMintDeposit) Type() string { return TypeMsgMintDeposit }
+
+// ValidateBasic does a simple validation check that doesn't require access to any other information.
+func (msg MsgMintDeposit) ValidateBasic() error {
+ if _, err := sdk.AccAddressFromBech32(msg.Depositor); err != nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid depositor address: %s", err)
+ }
+
+ if _, err := sdk.ValAddressFromBech32(msg.Validator); err != nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid validator address: %s", err)
+ }
+
+ if msg.Amount.IsNil() || !msg.Amount.IsValid() || msg.Amount.IsZero() {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "'%s'", msg.Amount)
+ }
+ return nil
+}
+
+// GetSignBytes gets the canonical byte representation of the Msg.
+func (msg MsgMintDeposit) GetSignBytes() []byte {
+ bz := ModuleCdc.MustMarshalJSON(&msg)
+ return sdk.MustSortJSON(bz)
+}
+
+// GetSigners returns the addresses of signers that must sign.
+func (msg MsgMintDeposit) GetSigners() []sdk.AccAddress {
+ depositor, _ := sdk.AccAddressFromBech32(msg.Depositor)
+ return []sdk.AccAddress{depositor}
+}
+
+// NewMsgDelegateMintDeposit returns a new MsgDelegateMintDeposit.
+func NewMsgDelegateMintDeposit(depositor sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) *MsgDelegateMintDeposit {
+ return &MsgDelegateMintDeposit{
+ Depositor: depositor.String(),
+ Validator: validator.String(),
+ Amount: amount,
+ }
+}
+
+// Route return the message type used for routing the message.
+func (msg MsgDelegateMintDeposit) Route() string { return RouterKey }
+
+// Type returns a human-readable string for the message, intended for utilization within tags.
+func (msg MsgDelegateMintDeposit) Type() string { return TypeMsgDelegateMintDeposit }
+
+// ValidateBasic does a simple validation check that doesn't require access to any other information.
+func (msg MsgDelegateMintDeposit) ValidateBasic() error {
+ if _, err := sdk.AccAddressFromBech32(msg.Depositor); err != nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid depositor address: %s", err)
+ }
+
+ if _, err := sdk.ValAddressFromBech32(msg.Validator); err != nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid validator address: %s", err)
+ }
+
+ if msg.Amount.IsNil() || !msg.Amount.IsValid() || msg.Amount.IsZero() {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "'%s'", msg.Amount)
+ }
+ return nil
+}
+
+// GetSignBytes gets the canonical byte representation of the Msg.
+func (msg MsgDelegateMintDeposit) GetSignBytes() []byte {
+ bz := ModuleCdc.MustMarshalJSON(&msg)
+ return sdk.MustSortJSON(bz)
+}
+
+// GetSigners returns the addresses of signers that must sign.
+func (msg MsgDelegateMintDeposit) GetSigners() []sdk.AccAddress {
+ depositor, _ := sdk.AccAddressFromBech32(msg.Depositor)
+ return []sdk.AccAddress{depositor}
+}
+
+// NewMsgWithdrawBurn returns a new MsgWithdrawBurn.
+func NewMsgWithdrawBurn(from sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) *MsgWithdrawBurn {
+ return &MsgWithdrawBurn{
+ From: from.String(),
+ Validator: validator.String(),
+ Amount: amount,
+ }
+}
+
+// Route return the message type used for routing the message.
+func (msg MsgWithdrawBurn) Route() string { return RouterKey }
+
+// Type returns a human-readable string for the message, intended for utilization within tags.
+func (msg MsgWithdrawBurn) Type() string { return TypeMsgWithdrawBurn }
+
+// ValidateBasic does a simple validation check that doesn't require access to any other information.
+func (msg MsgWithdrawBurn) ValidateBasic() error {
+ if _, err := sdk.AccAddressFromBech32(msg.From); err != nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid from address: %s", err)
+ }
+
+ if _, err := sdk.ValAddressFromBech32(msg.Validator); err != nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid validator address: %s", err)
+ }
+
+ if msg.Amount.IsNil() || !msg.Amount.IsValid() || msg.Amount.IsZero() {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "'%s'", msg.Amount)
+ }
+ return nil
+}
+
+// GetSignBytes gets the canonical byte representation of the Msg.
+func (msg MsgWithdrawBurn) GetSignBytes() []byte {
+ bz := ModuleCdc.MustMarshalJSON(&msg)
+ return sdk.MustSortJSON(bz)
+}
+
+// GetSigners returns the addresses of signers that must sign.
+func (msg MsgWithdrawBurn) GetSigners() []sdk.AccAddress {
+ from, _ := sdk.AccAddressFromBech32(msg.From)
+ return []sdk.AccAddress{from}
+}
+
+// NewMsgWithdrawBurnUndelegate returns a new MsgWithdrawBurnUndelegate.
+func NewMsgWithdrawBurnUndelegate(from sdk.AccAddress, validator sdk.ValAddress, amount sdk.Coin) *MsgWithdrawBurnUndelegate {
+ return &MsgWithdrawBurnUndelegate{
+ From: from.String(),
+ Validator: validator.String(),
+ Amount: amount,
+ }
+}
+
+// Route return the message type used for routing the message.
+func (msg MsgWithdrawBurnUndelegate) Route() string { return RouterKey }
+
+// Type returns a human-readable string for the message, intended for utilization within tags.
+func (msg MsgWithdrawBurnUndelegate) Type() string { return TypeMsgWithdrawBurnUndelegate }
+
+// ValidateBasic does a simple validation check that doesn't require access to any other information.
+func (msg MsgWithdrawBurnUndelegate) ValidateBasic() error {
+ if _, err := sdk.AccAddressFromBech32(msg.From); err != nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid from address: %s", err)
+ }
+
+ if _, err := sdk.ValAddressFromBech32(msg.Validator); err != nil {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid validator address: %s", err)
+ }
+
+ if msg.Amount.IsNil() || !msg.Amount.IsValid() || msg.Amount.IsZero() {
+ return sdkerrors.Wrapf(sdkerrors.ErrInvalidCoins, "'%s'", msg.Amount)
+ }
+ return nil
+}
+
+// GetSignBytes gets the canonical byte representation of the Msg.
+func (msg MsgWithdrawBurnUndelegate) GetSignBytes() []byte {
+ bz := ModuleCdc.MustMarshalJSON(&msg)
+ return sdk.MustSortJSON(bz)
+}
+
+// GetSigners returns the addresses of signers that must sign.
+func (msg MsgWithdrawBurnUndelegate) GetSigners() []sdk.AccAddress {
+ from, _ := sdk.AccAddressFromBech32(msg.From)
+ return []sdk.AccAddress{from}
+}
diff --git a/x/router/types/msg_test.go b/x/router/types/msg_test.go
new file mode 100644
index 00000000..d0ca3a06
--- /dev/null
+++ b/x/router/types/msg_test.go
@@ -0,0 +1,207 @@
+package types_test
+
+import (
+ fmt "fmt"
+ "testing"
+
+ sdk "github.com/cosmos/cosmos-sdk/types"
+ sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/kava-labs/kava/x/router/types"
+)
+
+func TestMsgMintDeposit_Signing(t *testing.T) {
+ address := mustAccAddressFromBech32("kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d")
+ validatorAddress := mustValAddressFromBech32("kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42")
+
+ msg := types.NewMsgMintDeposit(
+ address,
+ validatorAddress,
+ sdk.NewCoin("ukava", sdk.NewInt(1e9)),
+ )
+
+ // checking for the "type" field ensures the msg is registered on the amino codec
+ signBytes := []byte(
+ `{"type":"router/MsgMintDeposit","value":{"amount":{"amount":"1000000000","denom":"ukava"},"depositor":"kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d","validator":"kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42"}}`,
+ )
+
+ assert.Equal(t, []sdk.AccAddress{address}, msg.GetSigners())
+ assert.Equal(t, signBytes, msg.GetSignBytes())
+}
+
+func TestMsgDelegateMintDeposit_Signing(t *testing.T) {
+ address := mustAccAddressFromBech32("kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d")
+ validatorAddress := mustValAddressFromBech32("kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42")
+
+ msg := types.NewMsgDelegateMintDeposit(
+ address,
+ validatorAddress,
+ sdk.NewCoin("ukava", sdk.NewInt(1e9)),
+ )
+
+ // checking for the "type" field ensures the msg is registered on the amino codec
+ signBytes := []byte(
+ `{"type":"router/MsgDelegateMintDeposit","value":{"amount":{"amount":"1000000000","denom":"ukava"},"depositor":"kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d","validator":"kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42"}}`,
+ )
+
+ assert.Equal(t, []sdk.AccAddress{address}, msg.GetSigners())
+ assert.Equal(t, signBytes, msg.GetSignBytes())
+}
+
+func TestMsgWithdrawBurn_Signing(t *testing.T) {
+ address := mustAccAddressFromBech32("kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d")
+ validatorAddress := mustValAddressFromBech32("kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42")
+
+ msg := types.NewMsgWithdrawBurn(
+ address,
+ validatorAddress,
+ sdk.NewCoin("ukava", sdk.NewInt(1e9)),
+ )
+
+ // checking for the "type" field ensures the msg is registered on the amino codec
+ signBytes := []byte(
+ `{"type":"router/MsgWithdrawBurn","value":{"amount":{"amount":"1000000000","denom":"ukava"},"from":"kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d","validator":"kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42"}}`,
+ )
+
+ assert.Equal(t, []sdk.AccAddress{address}, msg.GetSigners())
+ assert.Equal(t, signBytes, msg.GetSignBytes())
+}
+
+func TestMsgWithdrawBurnUndelegate_Signing(t *testing.T) {
+ address := mustAccAddressFromBech32("kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d")
+ validatorAddress := mustValAddressFromBech32("kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42")
+
+ msg := types.NewMsgWithdrawBurnUndelegate(
+ address,
+ validatorAddress,
+ sdk.NewCoin("ukava", sdk.NewInt(1e9)),
+ )
+
+ // checking for the "type" field ensures the msg is registered on the amino codec
+ signBytes := []byte(
+ `{"type":"router/MsgWithdrawBurnUndelegate","value":{"amount":{"amount":"1000000000","denom":"ukava"},"from":"kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d","validator":"kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42"}}`,
+ )
+
+ assert.Equal(t, []sdk.AccAddress{address}, msg.GetSigners())
+ assert.Equal(t, signBytes, msg.GetSignBytes())
+}
+
+func TestMsg_Validate(t *testing.T) {
+ validAddress := "kava1gepm4nwzz40gtpur93alv9f9wm5ht4l0hzzw9d"
+ validValidatorAddress := "kavavaloper1ypjp0m04pyp73hwgtc0dgkx0e9rrydeckewa42"
+ validCoin := sdk.NewInt64Coin("ukava", 1e9)
+
+ type msgArgs struct {
+ depositor string
+ validator string
+ amount sdk.Coin
+ }
+ tests := []struct {
+ name string
+ msgArgs msgArgs
+ expectedErr error
+ }{
+ {
+ name: "normal multiplier is valid",
+ msgArgs: msgArgs{
+ depositor: validAddress,
+ validator: validValidatorAddress,
+ amount: validCoin,
+ },
+ },
+ {
+ name: "invalid depositor",
+ msgArgs: msgArgs{
+ depositor: "invalid",
+ validator: validValidatorAddress,
+ amount: validCoin,
+ },
+ expectedErr: sdkerrors.ErrInvalidAddress,
+ },
+ {
+ name: "empty depositor",
+ msgArgs: msgArgs{
+ depositor: "",
+ validator: validValidatorAddress,
+ amount: validCoin,
+ },
+ expectedErr: sdkerrors.ErrInvalidAddress,
+ },
+ {
+ name: "invalid validator",
+ msgArgs: msgArgs{
+ depositor: validAddress,
+ validator: "invalid",
+ amount: validCoin,
+ },
+ expectedErr: sdkerrors.ErrInvalidAddress,
+ },
+ {
+ name: "nil coin",
+ msgArgs: msgArgs{
+ depositor: validAddress,
+ validator: validValidatorAddress,
+ amount: sdk.Coin{},
+ },
+ expectedErr: sdkerrors.ErrInvalidCoins,
+ },
+ {
+ name: "zero coin",
+ msgArgs: msgArgs{
+ depositor: validAddress,
+ validator: validValidatorAddress,
+ amount: sdk.NewCoin("ukava", sdk.ZeroInt()),
+ },
+ expectedErr: sdkerrors.ErrInvalidCoins,
+ },
+ {
+ name: "negative coin",
+ msgArgs: msgArgs{
+ depositor: validAddress,
+ validator: validValidatorAddress,
+ amount: sdk.Coin{Denom: "ukava", Amount: sdk.NewInt(-1)},
+ },
+ expectedErr: sdkerrors.ErrInvalidCoins,
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ msgMintDeposit := types.MsgMintDeposit{tc.msgArgs.depositor, tc.msgArgs.validator, tc.msgArgs.amount}
+ msgDelegateMintDeposit := types.MsgDelegateMintDeposit{tc.msgArgs.depositor, tc.msgArgs.validator, tc.msgArgs.amount}
+
+ msgWithdrawBurn := types.MsgWithdrawBurn{tc.msgArgs.depositor, tc.msgArgs.validator, tc.msgArgs.amount}
+ msgWithdrawBurnUndelegate := types.MsgWithdrawBurnUndelegate{tc.msgArgs.depositor, tc.msgArgs.validator, tc.msgArgs.amount}
+
+ msgs := []sdk.Msg{&msgMintDeposit, &msgDelegateMintDeposit, &msgWithdrawBurn, &msgWithdrawBurnUndelegate}
+ for _, msg := range msgs {
+ t.Run(fmt.Sprintf("%T", msg), func(t *testing.T) {
+ err := msg.ValidateBasic()
+ if tc.expectedErr == nil {
+ require.NoError(t, err)
+ } else {
+ require.ErrorIs(t, err, tc.expectedErr, "expected error '%s' not found in actual '%s'", tc.expectedErr, err)
+ }
+ })
+ }
+ })
+ }
+}
+
+func mustAccAddressFromBech32(address string) sdk.AccAddress {
+ addr, err := sdk.AccAddressFromBech32(address)
+ if err != nil {
+ panic(err)
+ }
+ return addr
+}
+
+func mustValAddressFromBech32(address string) sdk.ValAddress {
+ addr, err := sdk.ValAddressFromBech32(address)
+ if err != nil {
+ panic(err)
+ }
+ return addr
+}
diff --git a/x/router/types/tx.pb.go b/x/router/types/tx.pb.go
new file mode 100644
index 00000000..aca80010
--- /dev/null
+++ b/x/router/types/tx.pb.go
@@ -0,0 +1,1882 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: kava/router/v1beta1/tx.proto
+
+package types
+
+import (
+ context "context"
+ fmt "fmt"
+ _ "github.com/cosmos/cosmos-proto"
+ types "github.com/cosmos/cosmos-sdk/types"
+ _ "github.com/gogo/protobuf/gogoproto"
+ grpc1 "github.com/gogo/protobuf/grpc"
+ proto "github.com/gogo/protobuf/proto"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+ io "io"
+ math "math"
+ math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// MsgMintDeposit converts a delegation into staking derivatives and deposits it all into an earn vault.
+type MsgMintDeposit struct {
+ // depositor represents the owner of the delegation to convert
+ Depositor string `protobuf:"bytes,1,opt,name=depositor,proto3" json:"depositor,omitempty"`
+ // validator is the validator for the depositor's delegation
+ Validator string `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator,omitempty"`
+ // amount is the delegation balance to convert
+ Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"`
+}
+
+func (m *MsgMintDeposit) Reset() { *m = MsgMintDeposit{} }
+func (m *MsgMintDeposit) String() string { return proto.CompactTextString(m) }
+func (*MsgMintDeposit) ProtoMessage() {}
+func (*MsgMintDeposit) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63015631bbbf9425, []int{0}
+}
+func (m *MsgMintDeposit) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgMintDeposit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgMintDeposit.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 *MsgMintDeposit) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgMintDeposit.Merge(m, src)
+}
+func (m *MsgMintDeposit) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgMintDeposit) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgMintDeposit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgMintDeposit proto.InternalMessageInfo
+
+// MsgMintDepositResponse defines the Msg/MsgMintDeposit response type.
+type MsgMintDepositResponse struct {
+}
+
+func (m *MsgMintDepositResponse) Reset() { *m = MsgMintDepositResponse{} }
+func (m *MsgMintDepositResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgMintDepositResponse) ProtoMessage() {}
+func (*MsgMintDepositResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63015631bbbf9425, []int{1}
+}
+func (m *MsgMintDepositResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgMintDepositResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgMintDepositResponse.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 *MsgMintDepositResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgMintDepositResponse.Merge(m, src)
+}
+func (m *MsgMintDepositResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgMintDepositResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgMintDepositResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgMintDepositResponse proto.InternalMessageInfo
+
+// MsgDelegateMintDeposit delegates tokens to a validator, then converts them into staking derivatives,
+// then deposits to an earn vault.
+type MsgDelegateMintDeposit struct {
+ // depositor represents the owner of the tokens to delegate
+ Depositor string `protobuf:"bytes,1,opt,name=depositor,proto3" json:"depositor,omitempty"`
+ // validator is the address of the validator to delegate to
+ Validator string `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator,omitempty"`
+ // amount is the tokens to delegate
+ Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"`
+}
+
+func (m *MsgDelegateMintDeposit) Reset() { *m = MsgDelegateMintDeposit{} }
+func (m *MsgDelegateMintDeposit) String() string { return proto.CompactTextString(m) }
+func (*MsgDelegateMintDeposit) ProtoMessage() {}
+func (*MsgDelegateMintDeposit) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63015631bbbf9425, []int{2}
+}
+func (m *MsgDelegateMintDeposit) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgDelegateMintDeposit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgDelegateMintDeposit.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 *MsgDelegateMintDeposit) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgDelegateMintDeposit.Merge(m, src)
+}
+func (m *MsgDelegateMintDeposit) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgDelegateMintDeposit) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgDelegateMintDeposit.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgDelegateMintDeposit proto.InternalMessageInfo
+
+// MsgDelegateMintDepositResponse defines the Msg/MsgDelegateMintDeposit response type.
+type MsgDelegateMintDepositResponse struct {
+}
+
+func (m *MsgDelegateMintDepositResponse) Reset() { *m = MsgDelegateMintDepositResponse{} }
+func (m *MsgDelegateMintDepositResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgDelegateMintDepositResponse) ProtoMessage() {}
+func (*MsgDelegateMintDepositResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63015631bbbf9425, []int{3}
+}
+func (m *MsgDelegateMintDepositResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgDelegateMintDepositResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgDelegateMintDepositResponse.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 *MsgDelegateMintDepositResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgDelegateMintDepositResponse.Merge(m, src)
+}
+func (m *MsgDelegateMintDepositResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgDelegateMintDepositResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgDelegateMintDepositResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgDelegateMintDepositResponse proto.InternalMessageInfo
+
+// MsgWithdrawBurn removes staking derivatives from an earn vault and converts them back to a staking delegation.
+type MsgWithdrawBurn struct {
+ // from is the owner of the earn vault to withdraw from
+ From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"`
+ // validator is the address to select the derivative denom to withdraw
+ Validator string `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator,omitempty"`
+ // amount is the staked token equivalent to withdraw
+ Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"`
+}
+
+func (m *MsgWithdrawBurn) Reset() { *m = MsgWithdrawBurn{} }
+func (m *MsgWithdrawBurn) String() string { return proto.CompactTextString(m) }
+func (*MsgWithdrawBurn) ProtoMessage() {}
+func (*MsgWithdrawBurn) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63015631bbbf9425, []int{4}
+}
+func (m *MsgWithdrawBurn) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgWithdrawBurn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgWithdrawBurn.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 *MsgWithdrawBurn) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgWithdrawBurn.Merge(m, src)
+}
+func (m *MsgWithdrawBurn) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgWithdrawBurn) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgWithdrawBurn.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgWithdrawBurn proto.InternalMessageInfo
+
+// MsgWithdrawBurnResponse defines the Msg/MsgWithdrawBurn response type.
+type MsgWithdrawBurnResponse struct {
+}
+
+func (m *MsgWithdrawBurnResponse) Reset() { *m = MsgWithdrawBurnResponse{} }
+func (m *MsgWithdrawBurnResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgWithdrawBurnResponse) ProtoMessage() {}
+func (*MsgWithdrawBurnResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63015631bbbf9425, []int{5}
+}
+func (m *MsgWithdrawBurnResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgWithdrawBurnResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgWithdrawBurnResponse.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 *MsgWithdrawBurnResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgWithdrawBurnResponse.Merge(m, src)
+}
+func (m *MsgWithdrawBurnResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgWithdrawBurnResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgWithdrawBurnResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgWithdrawBurnResponse proto.InternalMessageInfo
+
+// MsgWithdrawBurnUndelegate removes staking derivatives from an earn vault, converts them to a staking delegation,
+// then undelegates them from their validator.
+type MsgWithdrawBurnUndelegate struct {
+ // from is the owner of the earn vault to withdraw from
+ From string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"`
+ // validator is the address to select the derivative denom to withdraw
+ Validator string `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator,omitempty"`
+ // amount is the staked token equivalent to withdraw
+ Amount types.Coin `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount"`
+}
+
+func (m *MsgWithdrawBurnUndelegate) Reset() { *m = MsgWithdrawBurnUndelegate{} }
+func (m *MsgWithdrawBurnUndelegate) String() string { return proto.CompactTextString(m) }
+func (*MsgWithdrawBurnUndelegate) ProtoMessage() {}
+func (*MsgWithdrawBurnUndelegate) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63015631bbbf9425, []int{6}
+}
+func (m *MsgWithdrawBurnUndelegate) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgWithdrawBurnUndelegate) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgWithdrawBurnUndelegate.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 *MsgWithdrawBurnUndelegate) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgWithdrawBurnUndelegate.Merge(m, src)
+}
+func (m *MsgWithdrawBurnUndelegate) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgWithdrawBurnUndelegate) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgWithdrawBurnUndelegate.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgWithdrawBurnUndelegate proto.InternalMessageInfo
+
+// MsgWithdrawBurnUndelegateResponse defines the Msg/MsgWithdrawBurnUndelegate response type.
+type MsgWithdrawBurnUndelegateResponse struct {
+}
+
+func (m *MsgWithdrawBurnUndelegateResponse) Reset() { *m = MsgWithdrawBurnUndelegateResponse{} }
+func (m *MsgWithdrawBurnUndelegateResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgWithdrawBurnUndelegateResponse) ProtoMessage() {}
+func (*MsgWithdrawBurnUndelegateResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_63015631bbbf9425, []int{7}
+}
+func (m *MsgWithdrawBurnUndelegateResponse) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *MsgWithdrawBurnUndelegateResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ if deterministic {
+ return xxx_messageInfo_MsgWithdrawBurnUndelegateResponse.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 *MsgWithdrawBurnUndelegateResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_MsgWithdrawBurnUndelegateResponse.Merge(m, src)
+}
+func (m *MsgWithdrawBurnUndelegateResponse) XXX_Size() int {
+ return m.Size()
+}
+func (m *MsgWithdrawBurnUndelegateResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_MsgWithdrawBurnUndelegateResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgWithdrawBurnUndelegateResponse proto.InternalMessageInfo
+
+func init() {
+ proto.RegisterType((*MsgMintDeposit)(nil), "kava.router.v1beta1.MsgMintDeposit")
+ proto.RegisterType((*MsgMintDepositResponse)(nil), "kava.router.v1beta1.MsgMintDepositResponse")
+ proto.RegisterType((*MsgDelegateMintDeposit)(nil), "kava.router.v1beta1.MsgDelegateMintDeposit")
+ proto.RegisterType((*MsgDelegateMintDepositResponse)(nil), "kava.router.v1beta1.MsgDelegateMintDepositResponse")
+ proto.RegisterType((*MsgWithdrawBurn)(nil), "kava.router.v1beta1.MsgWithdrawBurn")
+ proto.RegisterType((*MsgWithdrawBurnResponse)(nil), "kava.router.v1beta1.MsgWithdrawBurnResponse")
+ proto.RegisterType((*MsgWithdrawBurnUndelegate)(nil), "kava.router.v1beta1.MsgWithdrawBurnUndelegate")
+ proto.RegisterType((*MsgWithdrawBurnUndelegateResponse)(nil), "kava.router.v1beta1.MsgWithdrawBurnUndelegateResponse")
+}
+
+func init() { proto.RegisterFile("kava/router/v1beta1/tx.proto", fileDescriptor_63015631bbbf9425) }
+
+var fileDescriptor_63015631bbbf9425 = []byte{
+ // 473 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x94, 0x4f, 0x6b, 0x13, 0x41,
+ 0x18, 0xc6, 0x77, 0x4c, 0x29, 0xe4, 0xad, 0x28, 0x6c, 0x4b, 0x4d, 0x96, 0x32, 0xc6, 0xd4, 0x43,
+ 0xc0, 0x76, 0x96, 0xb6, 0x50, 0xcf, 0xc6, 0xe2, 0x2d, 0x97, 0x88, 0x08, 0x5e, 0xca, 0x6c, 0x76,
+ 0x9c, 0x0e, 0x26, 0x33, 0x61, 0x66, 0x36, 0xad, 0x37, 0x3f, 0x82, 0x27, 0xaf, 0x7a, 0xf3, 0x0b,
+ 0xf8, 0x19, 0x24, 0xc7, 0xe2, 0xc9, 0x93, 0x68, 0xf2, 0x45, 0x64, 0xff, 0xa6, 0x29, 0xbb, 0x98,
+ 0x1e, 0x84, 0xde, 0x66, 0xe7, 0xf9, 0xbd, 0xcf, 0xfb, 0xbc, 0xf0, 0xee, 0xc0, 0xce, 0x3b, 0x3a,
+ 0xa1, 0xbe, 0x56, 0x91, 0x65, 0xda, 0x9f, 0x1c, 0x04, 0xcc, 0xd2, 0x03, 0xdf, 0x5e, 0x90, 0xb1,
+ 0x56, 0x56, 0xb9, 0x9b, 0xb1, 0x4a, 0x52, 0x95, 0x64, 0xaa, 0xd7, 0x1c, 0x28, 0x33, 0x52, 0xe6,
+ 0x34, 0x41, 0xfc, 0xf4, 0x23, 0xe5, 0x3d, 0x9c, 0x7e, 0xf9, 0x01, 0x35, 0xac, 0x70, 0x1b, 0x28,
+ 0x21, 0x33, 0x7d, 0x8b, 0x2b, 0xae, 0xd2, 0xba, 0xf8, 0x94, 0xde, 0xb6, 0x3f, 0x23, 0xb8, 0xd7,
+ 0x33, 0xbc, 0x27, 0xa4, 0x3d, 0x61, 0x63, 0x65, 0x84, 0x75, 0x8f, 0xa1, 0x1e, 0xa6, 0x47, 0xa5,
+ 0x1b, 0xa8, 0x85, 0x3a, 0xf5, 0x6e, 0xe3, 0xc7, 0xb7, 0xfd, 0xad, 0xac, 0xdb, 0xb3, 0x30, 0xd4,
+ 0xcc, 0x98, 0x97, 0x56, 0x0b, 0xc9, 0xfb, 0x0b, 0xd4, 0xdd, 0x81, 0xfa, 0x84, 0x0e, 0x45, 0x48,
+ 0xe3, 0xba, 0x3b, 0x71, 0x5d, 0x7f, 0x71, 0xe1, 0x3e, 0x85, 0x75, 0x3a, 0x52, 0x91, 0xb4, 0x8d,
+ 0x5a, 0x0b, 0x75, 0x36, 0x0e, 0x9b, 0x24, 0xf3, 0x8b, 0xf3, 0xe6, 0xf3, 0x91, 0xe7, 0x4a, 0xc8,
+ 0xee, 0xda, 0xf4, 0xd7, 0x43, 0xa7, 0x9f, 0xe1, 0xed, 0x06, 0x6c, 0x2f, 0x07, 0xec, 0x33, 0x33,
+ 0x56, 0xd2, 0xb0, 0xf6, 0x57, 0x94, 0x48, 0x27, 0x6c, 0xc8, 0x38, 0xb5, 0xec, 0x16, 0xcf, 0xd0,
+ 0x02, 0x5c, 0x1e, 0xb4, 0x98, 0xe5, 0x13, 0x82, 0xfb, 0x3d, 0xc3, 0x5f, 0x0b, 0x7b, 0x16, 0x6a,
+ 0x7a, 0xde, 0x8d, 0xb4, 0x74, 0xf7, 0x60, 0xed, 0xad, 0x56, 0xa3, 0x7f, 0xe6, 0x4f, 0xa8, 0xff,
+ 0x15, 0xbd, 0x09, 0x0f, 0xae, 0xe5, 0x2a, 0x32, 0x7f, 0x41, 0xd0, 0xbc, 0xa6, 0xbd, 0x92, 0x61,
+ 0x36, 0xe4, 0xed, 0x48, 0xbf, 0x0b, 0x8f, 0x2a, 0x13, 0xe6, 0x73, 0x1c, 0x7e, 0xaf, 0x41, 0xad,
+ 0x67, 0xb8, 0x7b, 0x0a, 0x1b, 0x57, 0x77, 0x68, 0x97, 0x94, 0xfc, 0x81, 0x64, 0x79, 0x17, 0xbd,
+ 0x27, 0x2b, 0x40, 0x79, 0x23, 0xf7, 0x1c, 0x36, 0xcb, 0x96, 0xb5, 0xd2, 0xa3, 0x04, 0xf6, 0x8e,
+ 0x6e, 0x00, 0x17, 0x8d, 0x03, 0xb8, 0xbb, 0xb4, 0x59, 0x8f, 0xab, 0x4c, 0xae, 0x52, 0xde, 0xde,
+ 0x2a, 0x54, 0xd1, 0xe3, 0x03, 0x82, 0xed, 0x8a, 0x55, 0x20, 0xab, 0x18, 0x2d, 0x78, 0xef, 0xf8,
+ 0x66, 0x7c, 0x1e, 0xa1, 0xfb, 0x62, 0xfa, 0x07, 0x3b, 0xd3, 0x19, 0x46, 0x97, 0x33, 0x8c, 0x7e,
+ 0xcf, 0x30, 0xfa, 0x38, 0xc7, 0xce, 0xe5, 0x1c, 0x3b, 0x3f, 0xe7, 0xd8, 0x79, 0xd3, 0xe1, 0xc2,
+ 0x9e, 0x45, 0x01, 0x19, 0xa8, 0x91, 0x1f, 0xfb, 0xef, 0x0f, 0x69, 0x60, 0x92, 0x93, 0x7f, 0x91,
+ 0xbf, 0xc2, 0xf6, 0xfd, 0x98, 0x99, 0x60, 0x3d, 0x79, 0x1b, 0x8f, 0xfe, 0x06, 0x00, 0x00, 0xff,
+ 0xff, 0xb1, 0xc1, 0x92, 0xc8, 0xa1, 0x05, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// MsgClient is the client API for Msg service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type MsgClient interface {
+ // MintDeposit converts a delegation into staking derivatives and deposits it all into an earn vault.
+ MintDeposit(ctx context.Context, in *MsgMintDeposit, opts ...grpc.CallOption) (*MsgMintDepositResponse, error)
+ // DelegateMintDeposit delegates tokens to a validator, then converts them into staking derivatives,
+ // then deposits to an earn vault.
+ DelegateMintDeposit(ctx context.Context, in *MsgDelegateMintDeposit, opts ...grpc.CallOption) (*MsgDelegateMintDepositResponse, error)
+ // WithdrawBurn removes staking derivatives from an earn vault and converts them back to a staking delegation.
+ WithdrawBurn(ctx context.Context, in *MsgWithdrawBurn, opts ...grpc.CallOption) (*MsgWithdrawBurnResponse, error)
+ // WithdrawBurnUndelegate removes staking derivatives from an earn vault, converts them to a staking delegation,
+ // then undelegates them from their validator.
+ WithdrawBurnUndelegate(ctx context.Context, in *MsgWithdrawBurnUndelegate, opts ...grpc.CallOption) (*MsgWithdrawBurnUndelegateResponse, error)
+}
+
+type msgClient struct {
+ cc grpc1.ClientConn
+}
+
+func NewMsgClient(cc grpc1.ClientConn) MsgClient {
+ return &msgClient{cc}
+}
+
+func (c *msgClient) MintDeposit(ctx context.Context, in *MsgMintDeposit, opts ...grpc.CallOption) (*MsgMintDepositResponse, error) {
+ out := new(MsgMintDepositResponse)
+ err := c.cc.Invoke(ctx, "/kava.router.v1beta1.Msg/MintDeposit", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *msgClient) DelegateMintDeposit(ctx context.Context, in *MsgDelegateMintDeposit, opts ...grpc.CallOption) (*MsgDelegateMintDepositResponse, error) {
+ out := new(MsgDelegateMintDepositResponse)
+ err := c.cc.Invoke(ctx, "/kava.router.v1beta1.Msg/DelegateMintDeposit", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *msgClient) WithdrawBurn(ctx context.Context, in *MsgWithdrawBurn, opts ...grpc.CallOption) (*MsgWithdrawBurnResponse, error) {
+ out := new(MsgWithdrawBurnResponse)
+ err := c.cc.Invoke(ctx, "/kava.router.v1beta1.Msg/WithdrawBurn", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *msgClient) WithdrawBurnUndelegate(ctx context.Context, in *MsgWithdrawBurnUndelegate, opts ...grpc.CallOption) (*MsgWithdrawBurnUndelegateResponse, error) {
+ out := new(MsgWithdrawBurnUndelegateResponse)
+ err := c.cc.Invoke(ctx, "/kava.router.v1beta1.Msg/WithdrawBurnUndelegate", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// MsgServer is the server API for Msg service.
+type MsgServer interface {
+ // MintDeposit converts a delegation into staking derivatives and deposits it all into an earn vault.
+ MintDeposit(context.Context, *MsgMintDeposit) (*MsgMintDepositResponse, error)
+ // DelegateMintDeposit delegates tokens to a validator, then converts them into staking derivatives,
+ // then deposits to an earn vault.
+ DelegateMintDeposit(context.Context, *MsgDelegateMintDeposit) (*MsgDelegateMintDepositResponse, error)
+ // WithdrawBurn removes staking derivatives from an earn vault and converts them back to a staking delegation.
+ WithdrawBurn(context.Context, *MsgWithdrawBurn) (*MsgWithdrawBurnResponse, error)
+ // WithdrawBurnUndelegate removes staking derivatives from an earn vault, converts them to a staking delegation,
+ // then undelegates them from their validator.
+ WithdrawBurnUndelegate(context.Context, *MsgWithdrawBurnUndelegate) (*MsgWithdrawBurnUndelegateResponse, error)
+}
+
+// UnimplementedMsgServer can be embedded to have forward compatible implementations.
+type UnimplementedMsgServer struct {
+}
+
+func (*UnimplementedMsgServer) MintDeposit(ctx context.Context, req *MsgMintDeposit) (*MsgMintDepositResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method MintDeposit not implemented")
+}
+func (*UnimplementedMsgServer) DelegateMintDeposit(ctx context.Context, req *MsgDelegateMintDeposit) (*MsgDelegateMintDepositResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method DelegateMintDeposit not implemented")
+}
+func (*UnimplementedMsgServer) WithdrawBurn(ctx context.Context, req *MsgWithdrawBurn) (*MsgWithdrawBurnResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method WithdrawBurn not implemented")
+}
+func (*UnimplementedMsgServer) WithdrawBurnUndelegate(ctx context.Context, req *MsgWithdrawBurnUndelegate) (*MsgWithdrawBurnUndelegateResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method WithdrawBurnUndelegate not implemented")
+}
+
+func RegisterMsgServer(s grpc1.Server, srv MsgServer) {
+ s.RegisterService(&_Msg_serviceDesc, srv)
+}
+
+func _Msg_MintDeposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgMintDeposit)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).MintDeposit(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/kava.router.v1beta1.Msg/MintDeposit",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).MintDeposit(ctx, req.(*MsgMintDeposit))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_DelegateMintDeposit_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgDelegateMintDeposit)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).DelegateMintDeposit(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/kava.router.v1beta1.Msg/DelegateMintDeposit",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).DelegateMintDeposit(ctx, req.(*MsgDelegateMintDeposit))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_WithdrawBurn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgWithdrawBurn)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).WithdrawBurn(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/kava.router.v1beta1.Msg/WithdrawBurn",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).WithdrawBurn(ctx, req.(*MsgWithdrawBurn))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_WithdrawBurnUndelegate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(MsgWithdrawBurnUndelegate)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(MsgServer).WithdrawBurnUndelegate(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/kava.router.v1beta1.Msg/WithdrawBurnUndelegate",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(MsgServer).WithdrawBurnUndelegate(ctx, req.(*MsgWithdrawBurnUndelegate))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+var _Msg_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "kava.router.v1beta1.Msg",
+ HandlerType: (*MsgServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "MintDeposit",
+ Handler: _Msg_MintDeposit_Handler,
+ },
+ {
+ MethodName: "DelegateMintDeposit",
+ Handler: _Msg_DelegateMintDeposit_Handler,
+ },
+ {
+ MethodName: "WithdrawBurn",
+ Handler: _Msg_WithdrawBurn_Handler,
+ },
+ {
+ MethodName: "WithdrawBurnUndelegate",
+ Handler: _Msg_WithdrawBurnUndelegate_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "kava/router/v1beta1/tx.proto",
+}
+
+func (m *MsgMintDeposit) 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 *MsgMintDeposit) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgMintDeposit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintTx(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1a
+ if len(m.Validator) > 0 {
+ i -= len(m.Validator)
+ copy(dAtA[i:], m.Validator)
+ i = encodeVarintTx(dAtA, i, uint64(len(m.Validator)))
+ 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 *MsgMintDepositResponse) 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 *MsgMintDepositResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgMintDepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgDelegateMintDeposit) 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 *MsgDelegateMintDeposit) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgDelegateMintDeposit) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintTx(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1a
+ if len(m.Validator) > 0 {
+ i -= len(m.Validator)
+ copy(dAtA[i:], m.Validator)
+ i = encodeVarintTx(dAtA, i, uint64(len(m.Validator)))
+ 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 *MsgDelegateMintDepositResponse) 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 *MsgDelegateMintDepositResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgDelegateMintDepositResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgWithdrawBurn) 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 *MsgWithdrawBurn) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgWithdrawBurn) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintTx(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1a
+ if len(m.Validator) > 0 {
+ i -= len(m.Validator)
+ copy(dAtA[i:], m.Validator)
+ i = encodeVarintTx(dAtA, i, uint64(len(m.Validator)))
+ i--
+ dAtA[i] = 0x12
+ }
+ if len(m.From) > 0 {
+ i -= len(m.From)
+ copy(dAtA[i:], m.From)
+ i = encodeVarintTx(dAtA, i, uint64(len(m.From)))
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgWithdrawBurnResponse) 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 *MsgWithdrawBurnResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgWithdrawBurnResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgWithdrawBurnUndelegate) 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 *MsgWithdrawBurnUndelegate) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgWithdrawBurnUndelegate) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ {
+ size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintTx(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x1a
+ if len(m.Validator) > 0 {
+ i -= len(m.Validator)
+ copy(dAtA[i:], m.Validator)
+ i = encodeVarintTx(dAtA, i, uint64(len(m.Validator)))
+ i--
+ dAtA[i] = 0x12
+ }
+ if len(m.From) > 0 {
+ i -= len(m.From)
+ copy(dAtA[i:], m.From)
+ i = encodeVarintTx(dAtA, i, uint64(len(m.From)))
+ i--
+ dAtA[i] = 0xa
+ }
+ return len(dAtA) - i, nil
+}
+
+func (m *MsgWithdrawBurnUndelegateResponse) 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 *MsgWithdrawBurnUndelegateResponse) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgWithdrawBurnUndelegateResponse) 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
+ for v >= 1<<7 {
+ dAtA[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ dAtA[offset] = uint8(v)
+ return base
+}
+func (m *MsgMintDeposit) 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))
+ }
+ l = len(m.Validator)
+ if l > 0 {
+ n += 1 + l + sovTx(uint64(l))
+ }
+ l = m.Amount.Size()
+ n += 1 + l + sovTx(uint64(l))
+ return n
+}
+
+func (m *MsgMintDepositResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func (m *MsgDelegateMintDeposit) 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))
+ }
+ l = len(m.Validator)
+ if l > 0 {
+ n += 1 + l + sovTx(uint64(l))
+ }
+ l = m.Amount.Size()
+ n += 1 + l + sovTx(uint64(l))
+ return n
+}
+
+func (m *MsgDelegateMintDepositResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func (m *MsgWithdrawBurn) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.From)
+ if l > 0 {
+ n += 1 + l + sovTx(uint64(l))
+ }
+ l = len(m.Validator)
+ if l > 0 {
+ n += 1 + l + sovTx(uint64(l))
+ }
+ l = m.Amount.Size()
+ n += 1 + l + sovTx(uint64(l))
+ return n
+}
+
+func (m *MsgWithdrawBurnResponse) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ return n
+}
+
+func (m *MsgWithdrawBurnUndelegate) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.From)
+ if l > 0 {
+ n += 1 + l + sovTx(uint64(l))
+ }
+ l = len(m.Validator)
+ if l > 0 {
+ n += 1 + l + sovTx(uint64(l))
+ }
+ l = m.Amount.Size()
+ n += 1 + l + sovTx(uint64(l))
+ return n
+}
+
+func (m *MsgWithdrawBurnUndelegateResponse) 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
+}
+func sozTx(x uint64) (n int) {
+ return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *MsgMintDeposit) 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: MsgMintDeposit: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgMintDeposit: 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 Validator", 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.Validator = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ 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
+ }
+ if err := m.Amount.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 *MsgMintDepositResponse) 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: MsgMintDepositResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgMintDepositResponse: 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 (m *MsgDelegateMintDeposit) 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: MsgDelegateMintDeposit: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgDelegateMintDeposit: 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 Validator", 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.Validator = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ 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
+ }
+ if err := m.Amount.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 *MsgDelegateMintDepositResponse) 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: MsgDelegateMintDepositResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgDelegateMintDepositResponse: 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 (m *MsgWithdrawBurn) 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: MsgWithdrawBurn: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgWithdrawBurn: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field From", 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.From = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Validator", 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.Validator = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ 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
+ }
+ if err := m.Amount.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 *MsgWithdrawBurnResponse) 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: MsgWithdrawBurnResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgWithdrawBurnResponse: 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 (m *MsgWithdrawBurnUndelegate) 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: MsgWithdrawBurnUndelegate: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgWithdrawBurnUndelegate: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field From", 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.From = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Validator", 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.Validator = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 3:
+ 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
+ }
+ if err := m.Amount.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 *MsgWithdrawBurnUndelegateResponse) 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: MsgWithdrawBurnUndelegateResponse: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: MsgWithdrawBurnUndelegateResponse: 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
+ depth := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if dAtA[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ case 1:
+ iNdEx += 8
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowTx
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if length < 0 {
+ return 0, ErrInvalidLengthTx
+ }
+ iNdEx += length
+ case 3:
+ depth++
+ case 4:
+ if depth == 0 {
+ return 0, ErrUnexpectedEndOfGroupTx
+ }
+ depth--
+ case 5:
+ iNdEx += 4
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ if iNdEx < 0 {
+ return 0, ErrInvalidLengthTx
+ }
+ if depth == 0 {
+ return iNdEx, nil
+ }
+ }
+ return 0, io.ErrUnexpectedEOF
+}
+
+var (
+ ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowTx = fmt.Errorf("proto: integer overflow")
+ ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group")
+)