diff --git a/CHANGELOG.md b/CHANGELOG.md
index eee050dd5c..8897ccbb1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,7 +22,8 @@
 
 ### Features
 
-*  [#1406](https://github.com/crypto-org-chain/cronos/pull/1406) Add set-encryption-key for encryption module.
+* [#1406](https://github.com/crypto-org-chain/cronos/pull/1406) Add set-encryption-key for encryption module.
+* [#1411](https://github.com/crypto-org-chain/cronos/pull/1411) Add encrypt and decrypt cmds for message.
 
 *April 8, 2024*
 
diff --git a/client/docs/config.json b/client/docs/config.json
index dfa434bdd8..9c76549202 100644
--- a/client/docs/config.json
+++ b/client/docs/config.json
@@ -15,12 +15,7 @@
       }
     },
     {
-      "url": "./tmp-swagger-gen/e2ee/query.swagger.json",
-      "operationIds": {
-        "rename": {
-          "Params": "Keys"
-        }
-      }
+      "url": "./tmp-swagger-gen/e2ee/query.swagger.json"
     },
     {
       "url": "./tmp-swagger-gen/ethermint/evm/v1/query.swagger.json",
diff --git a/cmd/cronosd/cmd/root.go b/cmd/cronosd/cmd/root.go
index 18c64c2392..82beda0f3e 100644
--- a/cmd/cronosd/cmd/root.go
+++ b/cmd/cronosd/cmd/root.go
@@ -48,6 +48,7 @@ import (
 	"github.com/crypto-org-chain/cronos/v2/app"
 	"github.com/crypto-org-chain/cronos/v2/cmd/cronosd/opendb"
 	"github.com/crypto-org-chain/cronos/v2/x/cronos"
+	e2eecli "github.com/crypto-org-chain/cronos/v2/x/e2ee/client/cli"
 	// this line is used by starport scaffolding # stargate/root/import
 )
 
@@ -189,6 +190,7 @@ func initRootCmd(
 		queryCommand(),
 		txCommand(),
 		ethermintclient.KeyCommands(app.DefaultNodeHome),
+		e2eecli.E2EECommand(),
 	)
 
 	// add rosetta
diff --git a/gomod2nix.toml b/gomod2nix.toml
index 47e295a89e..e032605394 100644
--- a/gomod2nix.toml
+++ b/gomod2nix.toml
@@ -64,6 +64,9 @@ schema = 3
   [mod."cosmossdk.io/x/upgrade"]
     version = "v0.1.1"
     hash = "sha256-bM9ybpaibMH7k4M6/QAXCZ3fJcADfJHxvMgp4AVUihs="
+  [mod."filippo.io/age"]
+    version = "v1.1.1"
+    hash = "sha256-LRxxJQLQkzoCNYGS/XBixVmYXoZ1mPHKvFicPGXYLcw="
   [mod."filippo.io/edwards25519"]
     version = "v1.1.0"
     hash = "sha256-9ACANrgWZSd5HYPfDZHY8DVbPSC9LOMgy8deq3rDOoc="
@@ -575,6 +578,9 @@ schema = 3
   [mod."github.com/tendermint/go-amino"]
     version = "v0.16.0"
     hash = "sha256-JW4zO/0vMzf1dXLePOqaMtiLUZgNbuIseh9GV+jQlf0="
+  [mod."github.com/test-go/testify"]
+    version = "v1.1.4"
+    hash = "sha256-8xygO1Rd4eTrmRe/g7zaifpNkeb6EmjNfUvTWbjDtPg="
   [mod."github.com/tidwall/btree"]
     version = "v0.0.0-20240406140148-2687063b042c"
     hash = "sha256-8eDLGHhw4qXG6MEa7w5Q9KLwOobXr8Vn5qqyQhuipQw="
diff --git a/integration_tests/cosmoscli.py b/integration_tests/cosmoscli.py
index 06f459590a..d2b9acaf5d 100644
--- a/integration_tests/cosmoscli.py
+++ b/integration_tests/cosmoscli.py
@@ -1,6 +1,7 @@
 import binascii
 import enum
 import hashlib
+import itertools
 import json
 import os
 import re
@@ -1851,16 +1852,16 @@ def query_e2ee_key(self, address):
                 home=self.data_dir,
                 output="json",
             )
-        )
+        )["key"]
 
-    def set_e2ee_key(self, key, **kwargs):
+    def register_e2ee_key(self, key, **kwargs):
         kwargs.setdefault("gas_prices", DEFAULT_GAS_PRICE)
         kwargs.setdefault("gas", DEFAULT_GAS)
         rsp = json.loads(
             self.raw(
                 "tx",
                 "e2ee",
-                "set-encryption-key",
+                "register-encryption-key",
                 key,
                 "-y",
                 home=self.data_dir,
@@ -1870,3 +1871,34 @@ def set_e2ee_key(self, key, **kwargs):
         if rsp["code"] == 0:
             rsp = self.event_query_tx_for(rsp["txhash"])
         return rsp
+
+    def keygen(self, **kwargs):
+        return self.raw("e2ee", "keygen", home=self.data_dir, **kwargs).strip().decode()
+
+    def encrypt(self, input, *recipients, **kwargs):
+        return (
+            self.raw(
+                "e2ee",
+                "encrypt",
+                input,
+                *itertools.chain.from_iterable(("-r", r) for r in recipients),
+                home=self.data_dir,
+                **kwargs,
+            )
+            .strip()
+            .decode()
+        )
+
+    def decrypt(self, input, identity="e2ee-identity", **kwargs):
+        return (
+            self.raw(
+                "e2ee",
+                "decrypt",
+                input,
+                home=self.data_dir,
+                identity=identity,
+                **kwargs,
+            )
+            .strip()
+            .decode()
+        )
diff --git a/integration_tests/test_e2ee.py b/integration_tests/test_e2ee.py
index 9b20796d56..99c1dc6c48 100644
--- a/integration_tests/test_e2ee.py
+++ b/integration_tests/test_e2ee.py
@@ -1,10 +1,26 @@
-import base64
+def test_encrypt_decrypt(cronos):
+    cli = cronos.cosmos_cli()
 
+    # gen two keys for two accounts
+    pubkey0 = cli.keygen(keyring_name="key0")
+    cli.register_e2ee_key(pubkey0, _from="validator")
+    assert cli.query_e2ee_key(cli.address("validator")) == pubkey0
+    pubkey1 = cli.keygen(keyring_name="key1")
+    cli.register_e2ee_key(pubkey1, _from="community")
+    assert cli.query_e2ee_key(cli.address("community")) == pubkey1
 
-def test_set_key(cronos):
-    cli = cronos.cosmos_cli()
-    key = base64.b64encode(b"new_key").decode("utf-8")
-    cli.set_e2ee_key(key, _from="community")
-    adr = cli.address("community")
-    p = cli.query_e2ee_key(adr)
-    assert p["key"] == key
+    # prepare data file to encrypt
+    content = "Hello World!"
+    plainfile = cli.data_dir / "plaintext"
+    plainfile.write_text(content)
+
+    cipherfile = cli.data_dir / "ciphertext"
+    cli.encrypt(
+        plainfile,
+        cli.address("validator"),
+        cli.address("community"),
+        output=cipherfile,
+    )
+
+    assert cli.decrypt(cipherfile, identity="key0") == content
+    assert cli.decrypt(cipherfile, identity="key1") == content
diff --git a/proto/e2ee/genesis.proto b/proto/e2ee/genesis.proto
index cf69a6b70c..12f4948149 100644
--- a/proto/e2ee/genesis.proto
+++ b/proto/e2ee/genesis.proto
@@ -8,7 +8,7 @@ option go_package = "github.com/crypto-org-chain/cronos/v2/x/e2ee/types";
 // EncryptionKeyEntry is a type that contains the owner and the public key.
 message EncryptionKeyEntry {
   string address = 1;
-  bytes key = 2;
+  string key = 2;
 }
 
 // GenesisState defines the e2ee module's genesis state.
diff --git a/proto/e2ee/query.proto b/proto/e2ee/query.proto
index cccb307a10..b1789a1af8 100644
--- a/proto/e2ee/query.proto
+++ b/proto/e2ee/query.proto
@@ -20,5 +20,5 @@ message KeyRequest {
 
 // KeyResponse is the response type for the Query/Key RPC method.
 message KeyResponse {
-  bytes key = 1;
+  string key = 1;
 }
diff --git a/proto/e2ee/tx.proto b/proto/e2ee/tx.proto
index 611e373472..358a45743a 100644
--- a/proto/e2ee/tx.proto
+++ b/proto/e2ee/tx.proto
@@ -18,7 +18,7 @@ message MsgRegisterEncryptionKey {
   option (cosmos.msg.v1.signer) = "address";
 
   string address = 1;
-  bytes key = 2;
+  string key = 2;
 }
 
 // MsgRegisterEncryptionKeyResponse defines the Msg/RegisterEncryptionKey response type
diff --git a/x/e2ee/autocli.go b/x/e2ee/autocli.go
index f83bedd8d0..9b9bb4904a 100644
--- a/x/e2ee/autocli.go
+++ b/x/e2ee/autocli.go
@@ -23,8 +23,8 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
 			RpcCommandOptions: []*autocliv1.RpcCommandOptions{
 				{
 					RpcMethod: "RegisterEncryptionKey",
-					Use:       "set-encryption-key [key]",
-					Short:     "Set encryption key is stored associated with the user address.",
+					Use:       "register-encryption-key [key]",
+					Short:     "Register encryption key stores an public key for asymmetric encryption with the user address.",
 					PositionalArgs: []*autocliv1.PositionalArgDescriptor{
 						{ProtoField: "key"},
 					},
diff --git a/x/e2ee/client/cli/cmd.go b/x/e2ee/client/cli/cmd.go
new file mode 100644
index 0000000000..e7358085a4
--- /dev/null
+++ b/x/e2ee/client/cli/cmd.go
@@ -0,0 +1,18 @@
+package cli
+
+import "github.com/spf13/cobra"
+
+func E2EECommand() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "e2ee",
+		Short: "End-to-end encryption commands",
+	}
+
+	cmd.AddCommand(
+		KeygenCommand(),
+		EncryptCommand(),
+		DecryptCommand(),
+	)
+
+	return cmd
+}
diff --git a/x/e2ee/client/cli/decrypt.go b/x/e2ee/client/cli/decrypt.go
new file mode 100644
index 0000000000..89f26f3298
--- /dev/null
+++ b/x/e2ee/client/cli/decrypt.go
@@ -0,0 +1,108 @@
+package cli
+
+import (
+	"fmt"
+	"io"
+	"os"
+
+	"filippo.io/age"
+	"github.com/spf13/cobra"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+
+	"github.com/crypto-org-chain/cronos/v2/x/e2ee/keyring"
+	"github.com/crypto-org-chain/cronos/v2/x/e2ee/types"
+)
+
+const FlagIdentity = "identity"
+
+func DecryptCommand() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "decrypt [input-file]",
+		Short: "Decrypt input file to local identity",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			kr, err := keyring.New("cronosd", clientCtx.Keyring.Backend(), clientCtx.HomeDir, os.Stdin)
+			if err != nil {
+				return err
+			}
+
+			outputFile, err := cmd.Flags().GetString(flags.FlagOutput)
+			if err != nil {
+				return err
+			}
+
+			identityNames, err := cmd.Flags().GetStringArray(FlagIdentity)
+			if err != nil {
+				return err
+			}
+
+			if len(identityNames) == 0 {
+				return fmt.Errorf("no identity provided")
+			}
+
+			identities := make([]age.Identity, len(identityNames))
+			for i, name := range identityNames {
+				secret, err := kr.Get(name)
+				if err != nil {
+					return err
+				}
+
+				identity, err := age.ParseX25519Identity(string(secret))
+				if err != nil {
+					return err
+				}
+
+				identities[i] = identity
+			}
+
+			var input io.Reader
+			inputFile := args[0]
+			if inputFile == "-" {
+				input = os.Stdin
+			} else {
+				f, err := os.Open(inputFile)
+				if err != nil {
+					return err
+				}
+				defer f.Close()
+				input = f
+			}
+
+			var output io.Writer
+			if outputFile == "-" {
+				output = os.Stdout
+			} else {
+				f, err := os.Create(outputFile)
+				if err != nil {
+					return err
+				}
+				defer f.Close()
+				output = f
+			}
+			return decrypt(identities, input, output)
+		},
+	}
+
+	cmd.Flags().StringArrayP(FlagIdentity, "i", []string{types.DefaultKeyringName}, "identity (can be repeated)")
+	cmd.Flags().StringP(flags.FlagOutput, "o", "-", "output file (default stdout)")
+
+	return cmd
+}
+
+func decrypt(identities []age.Identity, in io.Reader, out io.Writer) error {
+	r, err := age.Decrypt(in, identities...)
+	if err != nil {
+		return err
+	}
+	if _, err := io.Copy(out, r); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/x/e2ee/client/cli/encrypt.go b/x/e2ee/client/cli/encrypt.go
new file mode 100644
index 0000000000..cd7b071fe2
--- /dev/null
+++ b/x/e2ee/client/cli/encrypt.go
@@ -0,0 +1,104 @@
+package cli
+
+import (
+	"errors"
+	"io"
+	"os"
+
+	"filippo.io/age"
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/spf13/cobra"
+
+	"github.com/crypto-org-chain/cronos/v2/x/e2ee/types"
+)
+
+const (
+	FlagRecipient = "recipient"
+)
+
+func EncryptCommand() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "encrypt [input-file]",
+		Short: "Encrypt input file to one or multiple recipients",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			outputFile, err := cmd.Flags().GetString(flags.FlagOutput)
+			if err != nil {
+				return err
+			}
+
+			recs, err := cmd.Flags().GetStringArray(FlagRecipient)
+			if err != nil {
+				return err
+			}
+
+			// query encryption key from chain state
+			client := types.NewQueryClient(clientCtx)
+			recipients := make([]age.Recipient, len(recs))
+			for i, rec := range recs {
+				rsp, err := client.Key(clientCtx.CmdContext, &types.KeyRequest{
+					Address: rec,
+				})
+				if err != nil {
+					return err
+				}
+				recipient, err := age.ParseX25519Recipient(rsp.Key)
+				if err != nil {
+					return err
+				}
+				recipients[i] = recipient
+			}
+
+			inputFile := args[0]
+			var input io.Reader
+			if inputFile == "-" {
+				input = os.Stdin
+			} else {
+				f, err := os.Open(inputFile)
+				if err != nil {
+					return err
+				}
+				defer f.Close()
+				input = f
+			}
+
+			var output io.Writer
+			if outputFile == "-" {
+				output = os.Stdout
+			} else {
+				fp, err := os.Create(outputFile)
+				if err != nil {
+					return err
+				}
+				defer fp.Close()
+				output = fp
+			}
+			return encrypt(recipients, input, output)
+		},
+	}
+	f := cmd.Flags()
+	f.StringArrayP(FlagRecipient, "r", []string{}, "recipients")
+	f.StringP(flags.FlagOutput, "o", "-", "output file (default stdout)")
+	return cmd
+}
+
+func encrypt(recipients []age.Recipient, in io.Reader, out io.Writer) (err error) {
+	var w io.WriteCloser
+	w, err = age.Encrypt(out, recipients...)
+	if err != nil {
+		return err
+	}
+
+	defer func() {
+		err = errors.Join(err, w.Close())
+	}()
+
+	_, err = io.Copy(w, in)
+	return
+}
diff --git a/x/e2ee/client/cli/generate.go b/x/e2ee/client/cli/generate.go
new file mode 100644
index 0000000000..2d694d7088
--- /dev/null
+++ b/x/e2ee/client/cli/generate.go
@@ -0,0 +1,56 @@
+package cli
+
+import (
+	"fmt"
+	"os"
+
+	"filippo.io/age"
+	"github.com/spf13/cobra"
+
+	"github.com/cosmos/cosmos-sdk/client"
+
+	"github.com/crypto-org-chain/cronos/v2/x/e2ee/keyring"
+	"github.com/crypto-org-chain/cronos/v2/x/e2ee/types"
+)
+
+const FlagKeyringName = "keyring-name"
+
+func KeygenCommand() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "keygen",
+		Short: "Generates a new native X25519 key pair",
+		Args:  cobra.NoArgs,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			krName, err := cmd.Flags().GetString(FlagKeyringName)
+			if err != nil {
+				return err
+			}
+
+			kr, err := keyring.New("cronosd", clientCtx.Keyring.Backend(), clientCtx.HomeDir, os.Stdin)
+			if err != nil {
+				return err
+			}
+
+			k, err := age.GenerateX25519Identity()
+			if err != nil {
+				return err
+			}
+
+			if err := kr.Set(krName, []byte(k.String())); err != nil {
+				return err
+			}
+
+			fmt.Println(k.Recipient())
+			return nil
+		},
+	}
+
+	cmd.Flags().String(FlagKeyringName, types.DefaultKeyringName, "The keyring name to use")
+
+	return cmd
+}
diff --git a/x/e2ee/keeper/keeper.go b/x/e2ee/keeper/keeper.go
index de1c3acab3..84ccd5be5b 100644
--- a/x/e2ee/keeper/keeper.go
+++ b/x/e2ee/keeper/keeper.go
@@ -37,7 +37,7 @@ func (k Keeper) RegisterEncryptionKey(
 		return nil, err
 	}
 	sdkCtx := sdk.UnwrapSDKContext(ctx)
-	sdkCtx.KVStore(k.storeKey).Set(types.KeyPrefix(bz), req.Key)
+	sdkCtx.KVStore(k.storeKey).Set(types.KeyPrefix(bz), []byte(req.Key))
 	return &types.MsgRegisterEncryptionKeyResponse{}, nil
 }
 
@@ -70,7 +70,7 @@ func (k Keeper) ExportGenesis(ctx context.Context) (*types.GenesisState, error)
 		key := iter.Value()
 		keys = append(keys, types.EncryptionKeyEntry{
 			Address: address,
-			Key:     key,
+			Key:     string(key),
 		})
 	}
 	return &types.GenesisState{Keys: keys}, nil
@@ -83,5 +83,5 @@ func (k Keeper) Key(ctx context.Context, req *types.KeyRequest) (*types.KeyRespo
 	}
 	sdkCtx := sdk.UnwrapSDKContext(ctx)
 	value := sdkCtx.KVStore(k.storeKey).Get(types.KeyPrefix(bz))
-	return &types.KeyResponse{Key: value}, nil
+	return &types.KeyResponse{Key: string(value)}, nil
 }
diff --git a/x/e2ee/module.go b/x/e2ee/module.go
index e2e33ccb42..c91f233d6e 100644
--- a/x/e2ee/module.go
+++ b/x/e2ee/module.go
@@ -24,6 +24,7 @@ var (
 	_ module.AppModule        = AppModule{}
 	_ module.AppModuleBasic   = AppModuleBasic{}
 	_ module.HasGenesisBasics = AppModuleBasic{}
+	_ module.HasName          = AppModuleBasic{}
 	// this line is used by starport scaffolding # ibc/module/interface
 )
 
diff --git a/x/e2ee/types/genesis.pb.go b/x/e2ee/types/genesis.pb.go
index 28bd2085ee..69f52bc690 100644
--- a/x/e2ee/types/genesis.pb.go
+++ b/x/e2ee/types/genesis.pb.go
@@ -26,7 +26,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
 // EncryptionKeyEntry is a type that contains the owner and the public key.
 type EncryptionKeyEntry struct {
 	Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
-	Key     []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+	Key     string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
 }
 
 func (m *EncryptionKeyEntry) Reset()         { *m = EncryptionKeyEntry{} }
@@ -69,11 +69,11 @@ func (m *EncryptionKeyEntry) GetAddress() string {
 	return ""
 }
 
-func (m *EncryptionKeyEntry) GetKey() []byte {
+func (m *EncryptionKeyEntry) GetKey() string {
 	if m != nil {
 		return m.Key
 	}
-	return nil
+	return ""
 }
 
 // GenesisState defines the e2ee module's genesis state.
@@ -130,22 +130,22 @@ func init() {
 func init() { proto.RegisterFile("e2ee/genesis.proto", fileDescriptor_e81aee24edfec633) }
 
 var fileDescriptor_e81aee24edfec633 = []byte{
-	// 239 bytes of a gzipped FileDescriptorProto
+	// 236 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4a, 0x35, 0x4a, 0x4d,
 	0xd5, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62,
 	0x01, 0x89, 0x49, 0x89, 0xa4, 0xe7, 0xa7, 0xe7, 0x83, 0x05, 0xf4, 0x41, 0x2c, 0x88, 0x9c, 0x92,
 	0x03, 0x97, 0x90, 0x6b, 0x5e, 0x72, 0x51, 0x65, 0x41, 0x49, 0x66, 0x7e, 0x9e, 0x77, 0x6a, 0xa5,
 	0x6b, 0x5e, 0x49, 0x51, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x62, 0x4a, 0x4a, 0x51, 0x6a, 0x71, 0xb1,
 	0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x8c, 0x2b, 0x24, 0xc0, 0xc5, 0x9c, 0x9d, 0x5a, 0x29,
-	0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0x62, 0x2a, 0x39, 0x71, 0xf1, 0xb8, 0x43, 0xac, 0x0b,
-	0x2e, 0x49, 0x2c, 0x49, 0x15, 0x32, 0xe2, 0x62, 0xc9, 0x4e, 0xad, 0x04, 0x69, 0x64, 0xd6, 0xe0,
-	0x36, 0x92, 0xd0, 0x03, 0x59, 0xae, 0x87, 0x69, 0x87, 0x13, 0xcb, 0x89, 0x7b, 0xf2, 0x0c, 0x41,
-	0x60, 0xb5, 0x4e, 0x3e, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c,
-	0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0x65, 0x94,
-	0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0x36, 0x22, 0x5f, 0x37, 0xbf,
-	0x28, 0x5d, 0x37, 0x39, 0x23, 0x31, 0x33, 0x4f, 0x3f, 0xb9, 0x28, 0x3f, 0x2f, 0xbf, 0x58, 0xbf,
-	0xcc, 0x48, 0xbf, 0x42, 0x1f, 0xec, 0xef, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0xb0, 0xd7,
-	0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x1b, 0xe5, 0x9f, 0x8e, 0x0c, 0x01, 0x00, 0x00,
+	0xc1, 0x04, 0x16, 0x05, 0x31, 0x95, 0x9c, 0xb8, 0x78, 0xdc, 0x21, 0xd6, 0x05, 0x97, 0x24, 0x96,
+	0xa4, 0x0a, 0x19, 0x71, 0xb1, 0x64, 0xa7, 0x56, 0x82, 0x34, 0x32, 0x6b, 0x70, 0x1b, 0x49, 0xe8,
+	0x81, 0x2c, 0xd7, 0xc3, 0xb4, 0xc3, 0x89, 0xe5, 0xc4, 0x3d, 0x79, 0x86, 0x20, 0xb0, 0x5a, 0x27,
+	0x9f, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63,
+	0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4a, 0xcf, 0x2c, 0xc9,
+	0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x07, 0x1b, 0x91, 0xaf, 0x9b, 0x5f, 0x94, 0xae, 0x9b,
+	0x9c, 0x91, 0x98, 0x99, 0xa7, 0x9f, 0x5c, 0x94, 0x9f, 0x97, 0x5f, 0xac, 0x5f, 0x66, 0xa4, 0x5f,
+	0xa1, 0x0f, 0xf6, 0x77, 0x49, 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x6b, 0xc6, 0x80, 0x00,
+	0x00, 0x00, 0xff, 0xff, 0xf9, 0x3d, 0x79, 0x56, 0x0c, 0x01, 0x00, 0x00,
 }
 
 func (m *EncryptionKeyEntry) Marshal() (dAtA []byte, err error) {
@@ -336,7 +336,7 @@ func (m *EncryptionKeyEntry) Unmarshal(dAtA []byte) error {
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
 			}
-			var byteLen int
+			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowGenesis
@@ -346,25 +346,23 @@ func (m *EncryptionKeyEntry) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				byteLen |= int(b&0x7F) << shift
+				stringLen |= uint64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if byteLen < 0 {
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
 				return ErrInvalidLengthGenesis
 			}
-			postIndex := iNdEx + byteLen
+			postIndex := iNdEx + intStringLen
 			if postIndex < 0 {
 				return ErrInvalidLengthGenesis
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
-			if m.Key == nil {
-				m.Key = []byte{}
-			}
+			m.Key = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
diff --git a/x/e2ee/types/keys.go b/x/e2ee/types/keys.go
index c5744d7c7a..5941cbf264 100644
--- a/x/e2ee/types/keys.go
+++ b/x/e2ee/types/keys.go
@@ -17,6 +17,10 @@ const (
 	RouterKey = ModuleName
 )
 
+const (
+	DefaultKeyringName = "e2ee-identity"
+)
+
 const (
 	prefixEncryptionKey = iota + 1
 )
@@ -35,7 +39,7 @@ func (e EncryptionKeyEntry) Validate() error {
 	if _, err := sdk.AccAddressFromBech32(e.Address); err != nil {
 		return err
 	}
-	if e.Key == nil {
+	if len(e.Key) == 0 {
 		return errors.New("key can't be nil")
 	}
 	return nil
diff --git a/x/e2ee/types/msg.go b/x/e2ee/types/msg.go
new file mode 100644
index 0000000000..a18c0b0b4b
--- /dev/null
+++ b/x/e2ee/types/msg.go
@@ -0,0 +1,21 @@
+package types
+
+import (
+	fmt "fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+func (m *MsgRegisterEncryptionKey) ValidateBasic() error {
+	if m.Address == "" {
+		return fmt.Errorf("address cannot be empty")
+	}
+	if len(m.Key) == 0 {
+		return fmt.Errorf("key cannot be nil")
+	}
+	// validate bech32 format of Address
+	if _, err := sdk.AccAddressFromBech32(m.Address); err != nil {
+		return fmt.Errorf("invalid address: %s", err)
+	}
+	return nil
+}
diff --git a/x/e2ee/types/query.pb.go b/x/e2ee/types/query.pb.go
index 0505dcde42..4758e396e5 100644
--- a/x/e2ee/types/query.pb.go
+++ b/x/e2ee/types/query.pb.go
@@ -75,7 +75,7 @@ func (m *KeyRequest) GetAddress() string {
 
 // KeyResponse is the response type for the Query/Key RPC method.
 type KeyResponse struct {
-	Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
+	Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
 }
 
 func (m *KeyResponse) Reset()         { *m = KeyResponse{} }
@@ -111,11 +111,11 @@ func (m *KeyResponse) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_KeyResponse proto.InternalMessageInfo
 
-func (m *KeyResponse) GetKey() []byte {
+func (m *KeyResponse) GetKey() string {
 	if m != nil {
 		return m.Key
 	}
-	return nil
+	return ""
 }
 
 func init() {
@@ -126,24 +126,24 @@ func init() {
 func init() { proto.RegisterFile("e2ee/query.proto", fileDescriptor_1e8b28e605d00558) }
 
 var fileDescriptor_1e8b28e605d00558 = []byte{
-	// 264 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0xbf, 0x4a, 0xc4, 0x40,
-	0x10, 0xc6, 0x13, 0xcf, 0x3f, 0xb8, 0x5a, 0xc4, 0x2d, 0x24, 0x1c, 0xb2, 0x4a, 0x0a, 0xb1, 0xb9,
-	0x0c, 0xc6, 0x37, 0xb0, 0xf4, 0x6c, 0x8c, 0x9d, 0x5d, 0x2e, 0x37, 0xe4, 0xc2, 0xe9, 0xce, 0xde,
-	0xee, 0xe6, 0x70, 0x11, 0x1b, 0x9f, 0x40, 0xf0, 0xa5, 0x2c, 0x0f, 0x6c, 0x2c, 0x25, 0xf1, 0x41,
-	0x24, 0x89, 0xe2, 0x75, 0x33, 0x1f, 0x1f, 0xbf, 0xdf, 0x0c, 0x0b, 0x30, 0x41, 0x84, 0x45, 0x85,
-	0xda, 0xc5, 0x4a, 0x93, 0x25, 0xbe, 0xd9, 0x26, 0xc3, 0xa3, 0x82, 0xa8, 0xb8, 0x47, 0xc8, 0x54,
-	0x09, 0x99, 0x94, 0x64, 0x33, 0x5b, 0x92, 0x34, 0x7d, 0x27, 0x3a, 0x65, 0x6c, 0x8c, 0x2e, 0xc5,
-	0x45, 0x85, 0xc6, 0xf2, 0x90, 0xed, 0x64, 0xd3, 0xa9, 0x46, 0x63, 0x42, 0xff, 0xc4, 0x3f, 0xdb,
-	0x4d, 0xff, 0xd6, 0xe8, 0x98, 0xed, 0x75, 0x3d, 0xa3, 0x48, 0x1a, 0xe4, 0x01, 0x1b, 0xcc, 0xd1,
-	0x75, 0xa5, 0xfd, 0xb4, 0x1d, 0x93, 0x5b, 0xb6, 0x75, 0xd3, 0xba, 0xf9, 0x15, 0x1b, 0x8c, 0xd1,
-	0xf1, 0x20, 0x6e, 0xed, 0xf1, 0x3f, 0x7c, 0x78, 0xb0, 0x96, 0xf4, 0x98, 0x48, 0xbc, 0x7c, 0x7c,
-	0xbf, 0x6d, 0x84, 0xfc, 0x10, 0xba, 0xe3, 0x97, 0xe7, 0x30, 0x47, 0x07, 0x4f, 0xbf, 0xd2, 0xe7,
-	0xcb, 0xeb, 0xf7, 0x5a, 0xf8, 0xab, 0x5a, 0xf8, 0x5f, 0xb5, 0xf0, 0x5f, 0x1b, 0xe1, 0xad, 0x1a,
-	0xe1, 0x7d, 0x36, 0xc2, 0xbb, 0x4b, 0x8a, 0xd2, 0xce, 0xaa, 0x49, 0x9c, 0xd3, 0x03, 0xe4, 0xda,
-	0x29, 0x4b, 0x23, 0xd2, 0xc5, 0x28, 0x9f, 0x65, 0xa5, 0x84, 0x5c, 0x93, 0x24, 0x03, 0xcb, 0x04,
-	0x1e, 0x7b, 0xb0, 0x75, 0x0a, 0xcd, 0x64, 0xbb, 0x7b, 0xf9, 0xe2, 0x27, 0x00, 0x00, 0xff, 0xff,
-	0x41, 0x96, 0x8b, 0x79, 0x2a, 0x01, 0x00, 0x00,
+	// 263 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x48, 0x35, 0x4a, 0x4d,
+	0xd5, 0x2f, 0x2c, 0x4d, 0x2d, 0xaa, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0x89,
+	0x48, 0xc9, 0xa4, 0xe7, 0xe7, 0xa7, 0xe7, 0xa4, 0xea, 0x27, 0x16, 0x64, 0xea, 0x27, 0xe6, 0xe5,
+	0xe5, 0x97, 0x24, 0x96, 0x64, 0xe6, 0xe7, 0x15, 0x43, 0xd4, 0x28, 0xa9, 0x71, 0x71, 0x79, 0xa7,
+	0x56, 0x06, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, 0x08, 0x49, 0x70, 0xb1, 0x27, 0xa6, 0xa4, 0x14,
+	0xa5, 0x16, 0x17, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x4a, 0xf2, 0x5c, 0xdc,
+	0x60, 0x75, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x02, 0x5c, 0xcc, 0xd9, 0xa9, 0x95, 0x50,
+	0x45, 0x20, 0xa6, 0x51, 0x30, 0x17, 0x6b, 0x20, 0xc8, 0x6e, 0x21, 0x2f, 0x2e, 0x66, 0xef, 0xd4,
+	0x4a, 0x21, 0x01, 0x3d, 0x90, 0xed, 0x7a, 0x08, 0xc3, 0xa5, 0x04, 0x91, 0x44, 0x20, 0xc6, 0x28,
+	0xc9, 0x35, 0x5d, 0x7e, 0x32, 0x99, 0x49, 0x42, 0x48, 0x4c, 0x1f, 0xec, 0xf8, 0x32, 0x43, 0xfd,
+	0xec, 0xd4, 0x4a, 0xfd, 0x6a, 0xa8, 0xa5, 0xb5, 0x4e, 0x3e, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78,
+	0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc,
+	0x78, 0x2c, 0xc7, 0x10, 0x65, 0x94, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab,
+	0x9f, 0x5c, 0x54, 0x59, 0x50, 0x92, 0xaf, 0x9b, 0x5f, 0x94, 0xae, 0x9b, 0x9c, 0x91, 0x98, 0x99,
+	0xa7, 0x9f, 0x5c, 0x94, 0x9f, 0x97, 0x5f, 0xac, 0x5f, 0x66, 0xa4, 0x5f, 0x01, 0x31, 0xb8, 0xa4,
+	0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0xec, 0x65, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5c,
+	0x7b, 0x0b, 0x3a, 0x2a, 0x01, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -446,7 +446,7 @@ func (m *KeyResponse) Unmarshal(dAtA []byte) error {
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
 			}
-			var byteLen int
+			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowQuery
@@ -456,25 +456,23 @@ func (m *KeyResponse) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				byteLen |= int(b&0x7F) << shift
+				stringLen |= uint64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if byteLen < 0 {
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
 				return ErrInvalidLengthQuery
 			}
-			postIndex := iNdEx + byteLen
+			postIndex := iNdEx + intStringLen
 			if postIndex < 0 {
 				return ErrInvalidLengthQuery
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
-			if m.Key == nil {
-				m.Key = []byte{}
-			}
+			m.Key = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
diff --git a/x/e2ee/types/tx.pb.go b/x/e2ee/types/tx.pb.go
index 9a7cc7d9ce..938a155bca 100644
--- a/x/e2ee/types/tx.pb.go
+++ b/x/e2ee/types/tx.pb.go
@@ -31,7 +31,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
 // MsgRegisterEncryptionKey defines the Msg/RegisterEncryptionKey request type
 type MsgRegisterEncryptionKey struct {
 	Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
-	Key     []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
+	Key     string `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
 }
 
 func (m *MsgRegisterEncryptionKey) Reset()         { *m = MsgRegisterEncryptionKey{} }
@@ -74,11 +74,11 @@ func (m *MsgRegisterEncryptionKey) GetAddress() string {
 	return ""
 }
 
-func (m *MsgRegisterEncryptionKey) GetKey() []byte {
+func (m *MsgRegisterEncryptionKey) GetKey() string {
 	if m != nil {
 		return m.Key
 	}
-	return nil
+	return ""
 }
 
 // MsgRegisterEncryptionKeyResponse defines the Msg/RegisterEncryptionKey response type
@@ -126,24 +126,24 @@ func init() {
 func init() { proto.RegisterFile("e2ee/tx.proto", fileDescriptor_85e46bdbb1c358a8) }
 
 var fileDescriptor_85e46bdbb1c358a8 = []byte{
-	// 260 bytes of a gzipped FileDescriptorProto
+	// 257 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4d, 0x35, 0x4a, 0x4d,
 	0xd5, 0x2f, 0xa9, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0x71, 0xa5, 0xc4, 0x93,
 	0xf3, 0x8b, 0x73, 0xf3, 0x8b, 0xf5, 0x73, 0x8b, 0xd3, 0xf5, 0xcb, 0x0c, 0x41, 0x14, 0x44, 0x5a,
 	0x29, 0x84, 0x4b, 0xc2, 0xb7, 0x38, 0x3d, 0x28, 0x35, 0x3d, 0xb3, 0xb8, 0x24, 0xb5, 0xc8, 0x35,
 	0x2f, 0xb9, 0xa8, 0xb2, 0xa0, 0x24, 0x33, 0x3f, 0xcf, 0x3b, 0xb5, 0x52, 0x48, 0x82, 0x8b, 0x3d,
 	0x31, 0x25, 0xa5, 0x28, 0xb5, 0xb8, 0x58, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x15,
-	0x12, 0xe0, 0x62, 0xce, 0x4e, 0xad, 0x94, 0x60, 0x52, 0x60, 0xd4, 0xe0, 0x09, 0x02, 0x31, 0xad,
-	0x78, 0x9a, 0x9e, 0x6f, 0xd0, 0x82, 0xc9, 0x2b, 0x29, 0x71, 0x29, 0xe0, 0x32, 0x35, 0x28, 0xb5,
-	0xb8, 0x20, 0x3f, 0xaf, 0x38, 0xd5, 0x28, 0x97, 0x8b, 0xd9, 0xb7, 0x38, 0x5d, 0x28, 0x9e, 0x4b,
-	0x14, 0xbb, 0xed, 0x72, 0x7a, 0x20, 0x97, 0xeb, 0xe1, 0x32, 0x47, 0x4a, 0x0d, 0xbf, 0x3c, 0xcc,
-	0x1e, 0x29, 0xd6, 0x86, 0xe7, 0x1b, 0xb4, 0x18, 0x9d, 0x7c, 0x4e, 0x3c, 0x92, 0x63, 0xbc, 0xf0,
-	0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8,
-	0xf1, 0x58, 0x8e, 0x21, 0xca, 0x28, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57,
-	0x1f, 0x6c, 0x40, 0xbe, 0x6e, 0x7e, 0x51, 0xba, 0x6e, 0x72, 0x46, 0x62, 0x66, 0x9e, 0x7e, 0x72,
-	0x51, 0x7e, 0x5e, 0x7e, 0xb1, 0x7e, 0x99, 0x91, 0x7e, 0x85, 0x3e, 0x24, 0x60, 0x2b, 0x0b, 0x52,
-	0x8b, 0x93, 0xd8, 0xc0, 0xa1, 0x67, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x38, 0xff, 0xe3, 0x1c,
-	0x6d, 0x01, 0x00, 0x00,
+	0x12, 0xe0, 0x62, 0xce, 0x4e, 0xad, 0x94, 0x60, 0x02, 0x8b, 0x82, 0x98, 0x56, 0x3c, 0x4d, 0xcf,
+	0x37, 0x68, 0xc1, 0xe4, 0x95, 0x94, 0xb8, 0x14, 0x70, 0x99, 0x1a, 0x94, 0x5a, 0x5c, 0x90, 0x9f,
+	0x57, 0x9c, 0x6a, 0x94, 0xcb, 0xc5, 0xec, 0x5b, 0x9c, 0x2e, 0x14, 0xcf, 0x25, 0x8a, 0xdd, 0x76,
+	0x39, 0x3d, 0x90, 0xcb, 0xf5, 0x70, 0x99, 0x23, 0xa5, 0x86, 0x5f, 0x1e, 0x66, 0x8f, 0x14, 0x6b,
+	0xc3, 0xf3, 0x0d, 0x5a, 0x8c, 0x4e, 0x3e, 0x27, 0x1e, 0xc9, 0x31, 0x5e, 0x78, 0x24, 0xc7, 0xf8,
+	0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, 0x31, 0xdc, 0x78, 0x2c, 0xc7,
+	0x10, 0x65, 0x94, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0x36, 0x20,
+	0x5f, 0x37, 0xbf, 0x28, 0x5d, 0x37, 0x39, 0x23, 0x31, 0x33, 0x4f, 0x3f, 0xb9, 0x28, 0x3f, 0x2f,
+	0xbf, 0x58, 0xbf, 0xcc, 0x48, 0xbf, 0x42, 0x1f, 0x12, 0xb0, 0x95, 0x05, 0xa9, 0xc5, 0x49, 0x6c,
+	0xe0, 0xd0, 0x33, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xc8, 0x1e, 0x5a, 0x2c, 0x6d, 0x01, 0x00,
+	0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -396,7 +396,7 @@ func (m *MsgRegisterEncryptionKey) Unmarshal(dAtA []byte) error {
 			if wireType != 2 {
 				return fmt.Errorf("proto: wrong wireType = %d for field Key", wireType)
 			}
-			var byteLen int
+			var stringLen uint64
 			for shift := uint(0); ; shift += 7 {
 				if shift >= 64 {
 					return ErrIntOverflowTx
@@ -406,25 +406,23 @@ func (m *MsgRegisterEncryptionKey) Unmarshal(dAtA []byte) error {
 				}
 				b := dAtA[iNdEx]
 				iNdEx++
-				byteLen |= int(b&0x7F) << shift
+				stringLen |= uint64(b&0x7F) << shift
 				if b < 0x80 {
 					break
 				}
 			}
-			if byteLen < 0 {
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
 				return ErrInvalidLengthTx
 			}
-			postIndex := iNdEx + byteLen
+			postIndex := iNdEx + intStringLen
 			if postIndex < 0 {
 				return ErrInvalidLengthTx
 			}
 			if postIndex > l {
 				return io.ErrUnexpectedEOF
 			}
-			m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
-			if m.Key == nil {
-				m.Key = []byte{}
-			}
+			m.Key = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
 		default:
 			iNdEx = preIndex