From 3ba1d996239cf1655119c8ce8f10a44bfce4f1ee Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Mon, 6 May 2024 14:44:11 +0200
Subject: [PATCH 01/14] add: simulate nested messages

---
 baseapp/abci_test.go            | 120 +++-
 baseapp/baseapp.go              |  62 ++
 baseapp/testutil/messages.go    |  25 +-
 baseapp/testutil/messages.pb.go | 997 ++++++++++++++++++++++++++++++--
 baseapp/testutil/messages.proto |  29 +-
 baseapp/utils_test.go           |  56 ++
 6 files changed, 1235 insertions(+), 54 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index 9a3d6edd1682..47ff4a024f78 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -23,6 +23,7 @@ import (
 	protoio "github.com/cosmos/gogoproto/io"
 	"github.com/cosmos/gogoproto/jsonpb"
 	"github.com/cosmos/gogoproto/proto"
+	any "github.com/cosmos/gogoproto/types/any"
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/require"
 
@@ -33,7 +34,6 @@ import (
 	snapshottypes "cosmossdk.io/store/snapshots/types"
 	storetypes "cosmossdk.io/store/types"
 	"cosmossdk.io/x/auth/signing"
-
 	"github.com/cosmos/cosmos-sdk/baseapp"
 	baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
 	"github.com/cosmos/cosmos-sdk/baseapp/testutil/mock"
@@ -758,6 +758,124 @@ func TestABCI_FinalizeBlock_MultiMsg(t *testing.T) {
 	require.Equal(t, int64(2), msgCounter2)
 }
 
+func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
+	gasConsumed := uint64(5)
+	anteOpt := func(bapp *baseapp.BaseApp) {
+		bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
+			newCtx = ctx.WithGasMeter(storetypes.NewGasMeter(gasConsumed))
+			return
+		})
+	}
+	suite := NewBaseAppSuite(t, anteOpt)
+
+	_, err := suite.baseApp.InitChain(&abci.RequestInitChain{
+		ConsensusParams: &cmtproto.ConsensusParams{},
+	})
+	require.NoError(t, err)
+
+	baseapptestutil.RegisterNestedMessagesServer(suite.baseApp.MsgServiceRouter(), NesteMessgesServerImpl{})
+	baseapptestutil.RegisterSendServer(suite.baseApp.MsgServiceRouter(), SendServerImpl{})
+
+	_, _, addr := testdata.KeyTestPubAddr()
+	_, _, toAddr := testdata.KeyTestPubAddr()
+	tests := []struct {
+		name       string
+		nestedMsgs []*baseapptestutil.MsgSend
+		wantErr    bool
+	}{
+		{
+			name: "ok nested message",
+			nestedMsgs: []*baseapptestutil.MsgSend{
+				{
+					From:   addr.String(),
+					To:     toAddr.String(),
+					Amount: "10000stake",
+				},
+			},
+		},
+		{
+			name: "different signers",
+			nestedMsgs: []*baseapptestutil.MsgSend{
+				{
+					From:   toAddr.String(),
+					To:     addr.String(),
+					Amount: "10000stake",
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "empty from",
+			nestedMsgs: []*baseapptestutil.MsgSend{
+				{
+					From:   "",
+					To:     toAddr.String(),
+					Amount: "10000stake",
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "empty to",
+			nestedMsgs: []*baseapptestutil.MsgSend{
+				{
+					From:   addr.String(),
+					To:     "",
+					Amount: "10000stake",
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "negative amount",
+			nestedMsgs: []*baseapptestutil.MsgSend{
+				{
+					From:   addr.String(),
+					To:     toAddr.String(),
+					Amount: "-10000stake",
+				},
+			},
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			nestedMessages := make([]*any.Any, len(tt.nestedMsgs))
+			for i, msg := range tt.nestedMsgs {
+				b, err := suite.cdc.Marshal(msg)
+				require.NoError(t, err)
+				nestedMessages[i] = &any.Any{
+					TypeUrl: sdk.MsgTypeURL(msg),
+					Value:   b,
+				}
+			}
+			msg := &baseapptestutil.MsgNestedMessages{
+				Messages: nestedMessages,
+				Signer:   addr.String(),
+			}
+
+			builder := suite.txConfig.NewTxBuilder()
+			err = builder.SetMsgs(msg)
+			require.NoError(t, err)
+			setTxSignature(t, builder, 0)
+			tx := builder.GetTx()
+
+			txBytes, err := suite.txConfig.TxEncoder()(tx)
+			require.Nil(t, err)
+
+			_, result, err := suite.baseApp.Simulate(txBytes)
+			if tt.wantErr {
+				require.Error(t, err)
+				require.Nil(t, result)
+			} else {
+				require.NoError(t, err)
+				require.NotNil(t, result)
+			}
+
+		})
+	}
+}
+
 func TestABCI_Query_SimulateTx(t *testing.T) {
 	gasConsumed := uint64(5)
 	anteOpt := func(bapp *baseapp.BaseApp) {
diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go
index ed25df52d362..16d6f046cc9f 100644
--- a/baseapp/baseapp.go
+++ b/baseapp/baseapp.go
@@ -13,9 +13,11 @@ import (
 	"github.com/cometbft/cometbft/crypto/tmhash"
 	cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
 	dbm "github.com/cosmos/cosmos-db"
+	"github.com/cosmos/cosmos-proto/anyutil"
 	"github.com/cosmos/gogoproto/proto"
 	"golang.org/x/exp/maps"
 	protov2 "google.golang.org/protobuf/proto"
+	"google.golang.org/protobuf/types/known/anypb"
 
 	"cosmossdk.io/core/header"
 	errorsmod "cosmossdk.io/errors"
@@ -810,6 +812,10 @@ func (app *BaseApp) endBlock(ctx context.Context) (sdk.EndBlock, error) {
 	return endblock, nil
 }
 
+type HasNestedMsgs interface {
+	GetMsgs() ([]sdk.Msg, error)
+}
+
 // runTx processes a transaction within a given execution mode, encoded transaction
 // bytes, and the decoded transaction itself. All state transitions occur through
 // a cached Context depending on the mode provided. State only gets persisted
@@ -954,6 +960,20 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res
 		result, err = app.runMsgs(runMsgCtx, msgs, msgsV2, mode)
 	}
 
+	if mode == execModeSimulate {
+		nestedMsgsContext, _ := app.cacheTxContext(ctx, txBytes)
+		for _, msg := range msgs {
+			msg, ok := msg.(HasNestedMsgs)
+			if !ok {
+				continue
+			}
+			nestedErr := app.simulateNestedMessages(nestedMsgsContext, msg)
+			if nestedErr != nil {
+				return gInfo, nil, anteEvents, nestedErr
+			}
+		}
+	}
+
 	// Run optional postHandlers (should run regardless of the execution result).
 	//
 	// Note: If the postHandler fails, we also revert the runMsgs state.
@@ -1060,6 +1080,48 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, msgsV2 []protov2.Me
 	}, nil
 }
 
+// simulateNestedMessages simulates a message nested messages.
+func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg HasNestedMsgs) error {
+	msgs, err := msg.GetMsgs()
+	if err != nil {
+		return err
+	}
+
+	if err := validateBasicTxMsgs(app.msgServiceRouter, msgs); err != nil {
+		return err
+	}
+
+	msgsV2, err := app.msgsV1ToMsgsV2(msgs)
+	if err != nil {
+		return err
+	}
+
+	_, err = app.runMsgs(ctx, msgs, msgsV2, execModeSimulate)
+	return err
+}
+
+// msgsV1ToMsgsV2 transforms v1 messages into v2.
+func (app *BaseApp) msgsV1ToMsgsV2(msgs []sdk.Msg) ([]protov2.Message, error) {
+	msgsV2 := make([]protov2.Message, len(msgs))
+	for i, msg := range msgs {
+		gogoAny, err := codectypes.NewAnyWithValue(msg)
+		if err != nil {
+			return nil, err
+		}
+		anyMsg := &anypb.Any{
+			TypeUrl: gogoAny.TypeUrl,
+			Value:   gogoAny.Value,
+		}
+		msgV2, err := anyutil.Unpack(anyMsg, app.cdc.InterfaceRegistry().SigningContext().FileResolver(), app.cdc.InterfaceRegistry().SigningContext().TypeResolver())
+		if err != nil {
+			return nil, err
+		}
+		msgsV2[i] = msgV2
+	}
+
+	return msgsV2, nil
+}
+
 // makeABCIData generates the Data field to be sent to ABCI Check/DeliverTx.
 func makeABCIData(msgResponses []*codectypes.Any) ([]byte, error) {
 	return proto.Marshal(&sdk.TxMsgData{MsgResponses: msgResponses})
diff --git a/baseapp/testutil/messages.go b/baseapp/testutil/messages.go
index a4b1cd9abbcb..bffce60ba976 100644
--- a/baseapp/testutil/messages.go
+++ b/baseapp/testutil/messages.go
@@ -2,7 +2,7 @@ package testutil
 
 import (
 	errorsmod "cosmossdk.io/errors"
-
+	codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
 	"github.com/cosmos/cosmos-sdk/codec/types"
 	"github.com/cosmos/cosmos-sdk/crypto/codec"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -16,10 +16,15 @@ func RegisterInterfaces(registry types.InterfaceRegistry) {
 		&MsgCounter{},
 		&MsgCounter2{},
 		&MsgKeyValue{},
+		&MsgNestedMessages{},
+		&MsgSend{},
 	)
+
 	msgservice.RegisterMsgServiceDesc(registry, &_Counter_serviceDesc)
 	msgservice.RegisterMsgServiceDesc(registry, &_Counter2_serviceDesc)
 	msgservice.RegisterMsgServiceDesc(registry, &_KeyValue_serviceDesc)
+	msgservice.RegisterMsgServiceDesc(registry, &_NestedMessages_serviceDesc)
+	msgservice.RegisterMsgServiceDesc(registry, &_Send_serviceDesc)
 
 	codec.RegisterInterfaces(registry)
 }
@@ -63,3 +68,21 @@ func (msg *MsgKeyValue) ValidateBasic() error {
 	}
 	return nil
 }
+
+func (msg *MsgNestedMessages) GetMsgs() ([]sdk.Msg, error) {
+	cdc := codectestutil.CodecOptions{}.NewCodec()
+	RegisterInterfaces(cdc.InterfaceRegistry())
+	msgs := make([]sdk.Msg, len(msg.GetMessages()))
+	for i, m := range msg.GetMessages() {
+		mm, err := cdc.InterfaceRegistry().Resolve(m.TypeUrl)
+		if err != nil {
+			return nil, err
+		}
+		err = cdc.UnpackAny(m, &mm)
+		if err != nil {
+			return nil, err
+		}
+		msgs[i] = mm
+	}
+	return msgs, nil
+}
diff --git a/baseapp/testutil/messages.pb.go b/baseapp/testutil/messages.pb.go
index 2884ff0f649a..da3421a020f9 100644
--- a/baseapp/testutil/messages.pb.go
+++ b/baseapp/testutil/messages.pb.go
@@ -6,11 +6,11 @@ package testutil
 import (
 	context "context"
 	fmt "fmt"
-	_ "github.com/cosmos/cosmos-sdk/codec/types"
 	_ "github.com/cosmos/cosmos-sdk/types/msgservice"
 	_ "github.com/cosmos/gogoproto/gogoproto"
 	grpc1 "github.com/cosmos/gogoproto/grpc"
 	proto "github.com/cosmos/gogoproto/proto"
+	any "github.com/cosmos/gogoproto/types/any"
 	grpc "google.golang.org/grpc"
 	codes "google.golang.org/grpc/codes"
 	status "google.golang.org/grpc/status"
@@ -282,43 +282,240 @@ func (m *MsgCreateKeyValueResponse) XXX_DiscardUnknown() {
 
 var xxx_messageInfo_MsgCreateKeyValueResponse proto.InternalMessageInfo
 
+type MsgSend struct {
+	From   string `protobuf:"bytes,1,opt,name=from,proto3" json:"from,omitempty"`
+	To     string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"`
+	Amount string `protobuf:"bytes,3,opt,name=amount,proto3" json:"amount,omitempty"`
+}
+
+func (m *MsgSend) Reset()         { *m = MsgSend{} }
+func (m *MsgSend) String() string { return proto.CompactTextString(m) }
+func (*MsgSend) ProtoMessage()    {}
+func (*MsgSend) Descriptor() ([]byte, []int) {
+	return fileDescriptor_4dc296cbfe5ffcd5, []int{5}
+}
+func (m *MsgSend) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgSend) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgSend.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 *MsgSend) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgSend.Merge(m, src)
+}
+func (m *MsgSend) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgSend) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgSend.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSend proto.InternalMessageInfo
+
+func (m *MsgSend) GetFrom() string {
+	if m != nil {
+		return m.From
+	}
+	return ""
+}
+
+func (m *MsgSend) GetTo() string {
+	if m != nil {
+		return m.To
+	}
+	return ""
+}
+
+func (m *MsgSend) GetAmount() string {
+	if m != nil {
+		return m.Amount
+	}
+	return ""
+}
+
+type MsgSendResponse struct {
+}
+
+func (m *MsgSendResponse) Reset()         { *m = MsgSendResponse{} }
+func (m *MsgSendResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgSendResponse) ProtoMessage()    {}
+func (*MsgSendResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_4dc296cbfe5ffcd5, []int{6}
+}
+func (m *MsgSendResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgSendResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgSendResponse.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 *MsgSendResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgSendResponse.Merge(m, src)
+}
+func (m *MsgSendResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgSendResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgSendResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSendResponse proto.InternalMessageInfo
+
+type MsgNestedMessages struct {
+	Messages []*any.Any `protobuf:"bytes,1,rep,name=messages,proto3" json:"messages,omitempty"`
+	Signer   string     `protobuf:"bytes,2,opt,name=signer,proto3" json:"signer,omitempty"`
+}
+
+func (m *MsgNestedMessages) Reset()         { *m = MsgNestedMessages{} }
+func (m *MsgNestedMessages) String() string { return proto.CompactTextString(m) }
+func (*MsgNestedMessages) ProtoMessage()    {}
+func (*MsgNestedMessages) Descriptor() ([]byte, []int) {
+	return fileDescriptor_4dc296cbfe5ffcd5, []int{7}
+}
+func (m *MsgNestedMessages) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgNestedMessages) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgNestedMessages.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 *MsgNestedMessages) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgNestedMessages.Merge(m, src)
+}
+func (m *MsgNestedMessages) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgNestedMessages) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgNestedMessages.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgNestedMessages proto.InternalMessageInfo
+
+func (m *MsgNestedMessages) GetMessages() []*any.Any {
+	if m != nil {
+		return m.Messages
+	}
+	return nil
+}
+
+func (m *MsgNestedMessages) GetSigner() string {
+	if m != nil {
+		return m.Signer
+	}
+	return ""
+}
+
+type MsgCreateNestedMessagesResponse struct {
+}
+
+func (m *MsgCreateNestedMessagesResponse) Reset()         { *m = MsgCreateNestedMessagesResponse{} }
+func (m *MsgCreateNestedMessagesResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCreateNestedMessagesResponse) ProtoMessage()    {}
+func (*MsgCreateNestedMessagesResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_4dc296cbfe5ffcd5, []int{8}
+}
+func (m *MsgCreateNestedMessagesResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCreateNestedMessagesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCreateNestedMessagesResponse.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 *MsgCreateNestedMessagesResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCreateNestedMessagesResponse.Merge(m, src)
+}
+func (m *MsgCreateNestedMessagesResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCreateNestedMessagesResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCreateNestedMessagesResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCreateNestedMessagesResponse proto.InternalMessageInfo
+
 func init() {
 	proto.RegisterType((*MsgCounter)(nil), "MsgCounter")
 	proto.RegisterType((*MsgCounter2)(nil), "MsgCounter2")
 	proto.RegisterType((*MsgCreateCounterResponse)(nil), "MsgCreateCounterResponse")
 	proto.RegisterType((*MsgKeyValue)(nil), "MsgKeyValue")
 	proto.RegisterType((*MsgCreateKeyValueResponse)(nil), "MsgCreateKeyValueResponse")
+	proto.RegisterType((*MsgSend)(nil), "MsgSend")
+	proto.RegisterType((*MsgSendResponse)(nil), "MsgSendResponse")
+	proto.RegisterType((*MsgNestedMessages)(nil), "MsgNestedMessages")
+	proto.RegisterType((*MsgCreateNestedMessagesResponse)(nil), "MsgCreateNestedMessagesResponse")
 }
 
 func init() { proto.RegisterFile("messages.proto", fileDescriptor_4dc296cbfe5ffcd5) }
 
 var fileDescriptor_4dc296cbfe5ffcd5 = []byte{
-	// 390 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x92, 0xcf, 0xaa, 0xd3, 0x40,
-	0x14, 0xc6, 0x1b, 0x83, 0x6d, 0x3d, 0xad, 0x5a, 0x42, 0xd1, 0x34, 0x42, 0x28, 0x5d, 0x48, 0x11,
-	0x9a, 0xc1, 0xb8, 0x6b, 0x77, 0x8a, 0x54, 0x11, 0x11, 0x22, 0xb8, 0xe8, 0xa6, 0x4c, 0xd2, 0xd3,
-	0x69, 0x68, 0x32, 0x13, 0x32, 0x93, 0x42, 0xb7, 0x3e, 0x81, 0x8f, 0xe2, 0x63, 0xb8, 0xec, 0xd2,
-	0xa5, 0xb4, 0x0b, 0x5f, 0x43, 0xf2, 0xaf, 0x75, 0x71, 0x7b, 0xb9, 0xab, 0xbb, 0x9a, 0xf3, 0x7d,
-	0x87, 0x9c, 0xdf, 0xc9, 0xc7, 0x81, 0x27, 0x31, 0x4a, 0x49, 0x19, 0x4a, 0x27, 0x49, 0x85, 0x12,
-	0x56, 0x9f, 0x09, 0x26, 0x8a, 0x92, 0xe4, 0x55, 0xe5, 0x0e, 0x98, 0x10, 0x2c, 0x42, 0x52, 0x28,
-	0x3f, 0x5b, 0x13, 0xca, 0xf7, 0x55, 0xeb, 0x79, 0x20, 0x64, 0x2c, 0x24, 0x89, 0x25, 0x23, 0xbb,
-	0xd7, 0xf9, 0x53, 0x36, 0x46, 0x12, 0xe0, 0xb3, 0x64, 0xef, 0x44, 0xc6, 0x15, 0xa6, 0x86, 0x09,
-	0xad, 0xa0, 0x2c, 0x4d, 0x6d, 0xa8, 0x8d, 0x75, 0xaf, 0x96, 0xc6, 0x4b, 0x78, 0xba, 0xa6, 0x61,
-	0xb4, 0x14, 0x7c, 0xb9, 0xa1, 0x7c, 0x15, 0x61, 0x6a, 0x3e, 0x18, 0x6a, 0xe3, 0xb6, 0xf7, 0x38,
-	0xb7, 0xbf, 0xf0, 0x0f, 0xa5, 0x69, 0x3c, 0x83, 0xa6, 0x0c, 0x19, 0xc7, 0xd4, 0xd4, 0x87, 0xda,
-	0xf8, 0x91, 0x57, 0xa9, 0x69, 0xe7, 0xfb, 0xdf, 0x9f, 0xaf, 0x2a, 0x31, 0x52, 0xd0, 0xb9, 0x40,
-	0xdd, 0xfb, 0xa2, 0x5a, 0x60, 0xe6, 0xd4, 0x14, 0xa9, 0xc2, 0x8a, 0xed, 0xa1, 0x4c, 0x04, 0x97,
-	0x38, 0x5a, 0x14, 0x1b, 0x7d, 0xc2, 0xfd, 0x37, 0x1a, 0x65, 0x68, 0xf4, 0x40, 0xdf, 0xe2, 0xbe,
-	0xd8, 0xa6, 0xeb, 0xe5, 0xa5, 0xd1, 0x87, 0x87, 0xbb, 0xbc, 0x55, 0xf0, 0xbb, 0x5e, 0x29, 0xee,
-	0xc6, 0x7d, 0x01, 0x83, 0x33, 0xb7, 0x26, 0xd4, 0x60, 0xf7, 0x3d, 0xb4, 0xea, 0xf0, 0xa7, 0xd0,
-	0xfb, 0xc8, 0x83, 0x14, 0x63, 0xe4, 0xaa, 0xf6, 0x3a, 0xce, 0x25, 0x28, 0x6b, 0xe0, 0x5c, 0xdb,
-	0xdf, 0x9d, 0x43, 0xfb, 0x1c, 0xe7, 0xec, 0x86, 0x39, 0xdd, 0xff, 0xe6, 0xb8, 0xb7, 0x0d, 0x9a,
-	0x41, 0xfb, 0x9c, 0x02, 0x01, 0xfd, 0x2b, 0xaa, 0xf2, 0xdb, 0xda, 0xb4, 0x2c, 0xe7, 0xea, 0xcf,
-	0xbc, 0x9d, 0xff, 0x3a, 0xda, 0xda, 0xe1, 0x68, 0x6b, 0x7f, 0x8e, 0xb6, 0xf6, 0xe3, 0x64, 0x37,
-	0x0e, 0x27, 0xbb, 0xf1, 0xfb, 0x64, 0x37, 0x16, 0x13, 0x16, 0xaa, 0x4d, 0xe6, 0x3b, 0x81, 0x88,
-	0x49, 0x75, 0x8a, 0xe5, 0x33, 0x91, 0xab, 0x2d, 0xf1, 0xa9, 0x44, 0x9a, 0x24, 0x44, 0xa1, 0x54,
-	0x99, 0x0a, 0x23, 0xbf, 0x59, 0x1c, 0xe7, 0x9b, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x44, 0x91,
-	0x2d, 0xb3, 0xf8, 0x02, 0x00, 0x00,
+	// 535 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x93, 0xcf, 0x6e, 0xd3, 0x40,
+	0x10, 0xc6, 0xeb, 0xa4, 0x6d, 0x92, 0x49, 0x48, 0xd3, 0x55, 0x04, 0x8e, 0x91, 0x4c, 0xc8, 0x01,
+	0x45, 0x95, 0xba, 0x06, 0x73, 0x4b, 0xc5, 0x01, 0x2a, 0x54, 0x10, 0x0a, 0x45, 0xae, 0xc4, 0xa1,
+	0x97, 0xca, 0x49, 0x26, 0x9b, 0x90, 0x78, 0x37, 0xf2, 0xae, 0x2b, 0xe5, 0xca, 0x13, 0xf0, 0x28,
+	0x3c, 0x06, 0xc7, 0x1e, 0x39, 0xa2, 0xe4, 0xc0, 0x6b, 0x20, 0xaf, 0xff, 0xb4, 0x14, 0x02, 0x9c,
+	0x38, 0xed, 0xcc, 0xac, 0x77, 0x7e, 0xb3, 0xfb, 0x7d, 0x86, 0x7a, 0x80, 0x52, 0xfa, 0x0c, 0x25,
+	0x5d, 0x84, 0x42, 0x09, 0xab, 0xc9, 0x04, 0x13, 0x3a, 0x74, 0xe2, 0x28, 0xad, 0xb6, 0x98, 0x10,
+	0x6c, 0x8e, 0x8e, 0xce, 0x06, 0xd1, 0xd8, 0xf1, 0xf9, 0x32, 0xdd, 0xba, 0x37, 0x14, 0x32, 0x10,
+	0xd2, 0x09, 0x24, 0x73, 0x2e, 0x9f, 0xc4, 0x4b, 0xb2, 0xd1, 0x91, 0x00, 0x7d, 0xc9, 0x8e, 0x45,
+	0xc4, 0x15, 0x86, 0xc4, 0x84, 0xd2, 0x30, 0x09, 0x4d, 0xa3, 0x6d, 0x74, 0x8b, 0x5e, 0x96, 0x92,
+	0x47, 0xb0, 0x37, 0xf6, 0xa7, 0xf3, 0x0b, 0xc1, 0x2f, 0x26, 0x3e, 0x1f, 0xcd, 0x31, 0x34, 0x0b,
+	0x6d, 0xa3, 0x5b, 0xf6, 0xee, 0xc4, 0xe5, 0x53, 0xfe, 0x2a, 0x29, 0x92, 0xbb, 0xb0, 0x2b, 0xa7,
+	0x8c, 0x63, 0x68, 0x16, 0xdb, 0x46, 0xb7, 0xe2, 0xa5, 0x59, 0xaf, 0xfa, 0xf1, 0xfb, 0xe7, 0x83,
+	0x34, 0xe9, 0x28, 0xa8, 0x5e, 0x43, 0xdd, 0xff, 0x45, 0xb5, 0xc0, 0x8c, 0xa9, 0x21, 0xfa, 0x0a,
+	0x53, 0xb6, 0x87, 0x72, 0x21, 0xb8, 0xc4, 0xce, 0xb9, 0x9e, 0xe8, 0x0d, 0x2e, 0xdf, 0xfb, 0xf3,
+	0x08, 0x49, 0x03, 0x8a, 0x33, 0x5c, 0xea, 0x69, 0x6a, 0x5e, 0x1c, 0x92, 0x26, 0xec, 0x5c, 0xc6,
+	0x5b, 0x9a, 0x5f, 0xf3, 0x92, 0xe4, 0xdf, 0xb8, 0xf7, 0xa1, 0x95, 0x73, 0x33, 0x42, 0x0e, 0x7e,
+	0x07, 0xa5, 0xbe, 0x64, 0x67, 0xc8, 0x47, 0x84, 0xc0, 0xf6, 0x38, 0x14, 0x81, 0xa6, 0x56, 0x3c,
+	0x1d, 0x93, 0x3a, 0x14, 0x94, 0xd0, 0xcc, 0x8a, 0x57, 0x50, 0x22, 0x06, 0xfa, 0x41, 0x3c, 0x7b,
+	0x06, 0x4c, 0xb2, 0x5e, 0x25, 0x06, 0xea, 0x23, 0x9d, 0x7d, 0xd8, 0x4b, 0x3b, 0xe6, 0x90, 0x0f,
+	0xb0, 0xdf, 0x97, 0xec, 0x2d, 0x4a, 0x85, 0xa3, 0x7e, 0xea, 0x24, 0xf2, 0x18, 0xca, 0x99, 0xab,
+	0x4c, 0xa3, 0x5d, 0xec, 0x56, 0xdd, 0x26, 0x4d, 0x0c, 0x44, 0x33, 0x03, 0xd1, 0xe7, 0x7c, 0xe9,
+	0xe5, 0x5f, 0xdd, 0xb8, 0x6d, 0x61, 0xf3, 0x6d, 0x1f, 0xc2, 0x83, 0xfc, 0xb6, 0x3f, 0x13, 0xb3,
+	0x71, 0xdc, 0x97, 0x50, 0xca, 0x0c, 0xd7, 0x83, 0xc6, 0x6b, 0x3e, 0x0c, 0x31, 0x40, 0xae, 0xb2,
+	0x5a, 0x95, 0x5e, 0x9b, 0xc3, 0x6a, 0xd1, 0x4d, 0x9a, 0xb9, 0x27, 0x50, 0xce, 0x2d, 0x74, 0xf4,
+	0x9b, 0x3e, 0xb5, 0x1b, 0x7d, 0xdc, 0x3f, 0x35, 0x3a, 0x82, 0x72, 0xae, 0xbc, 0x03, 0xc5, 0x33,
+	0x54, 0xc9, 0xd9, 0xac, 0x68, 0x59, 0x74, 0xa3, 0x80, 0xee, 0x01, 0x6c, 0x6b, 0xf5, 0x3a, 0xe9,
+	0x5a, 0xa6, 0xe9, 0xeb, 0x5b, 0x0d, 0x7a, 0x4b, 0x07, 0xf7, 0x14, 0xea, 0xb7, 0x44, 0x78, 0x06,
+	0x3b, 0xc7, 0x13, 0x1c, 0xce, 0x08, 0xa1, 0xbf, 0x28, 0x64, 0xb5, 0xe9, 0x5f, 0x5e, 0xf2, 0xc5,
+	0xc9, 0x97, 0x95, 0x6d, 0x5c, 0xad, 0x6c, 0xe3, 0xdb, 0xca, 0x36, 0x3e, 0xad, 0xed, 0xad, 0xab,
+	0xb5, 0xbd, 0xf5, 0x75, 0x6d, 0x6f, 0x9d, 0x1f, 0xb2, 0xa9, 0x9a, 0x44, 0x03, 0x3a, 0x14, 0x81,
+	0x93, 0xfe, 0xfb, 0xc9, 0x72, 0x28, 0x47, 0x33, 0x67, 0xe0, 0x4b, 0xf4, 0x17, 0x0b, 0x47, 0xa1,
+	0x54, 0x91, 0x9a, 0xce, 0x07, 0xbb, 0x5a, 0xf2, 0xa7, 0x3f, 0x02, 0x00, 0x00, 0xff, 0xff, 0x12,
+	0x9a, 0xa4, 0x74, 0x69, 0x04, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -545,6 +742,150 @@ var _KeyValue_serviceDesc = grpc.ServiceDesc{
 	Metadata: "messages.proto",
 }
 
+// SendClient is the client API for Send service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type SendClient interface {
+	Send(ctx context.Context, in *MsgSend, opts ...grpc.CallOption) (*MsgSendResponse, error)
+}
+
+type sendClient struct {
+	cc grpc1.ClientConn
+}
+
+func NewSendClient(cc grpc1.ClientConn) SendClient {
+	return &sendClient{cc}
+}
+
+func (c *sendClient) Send(ctx context.Context, in *MsgSend, opts ...grpc.CallOption) (*MsgSendResponse, error) {
+	out := new(MsgSendResponse)
+	err := c.cc.Invoke(ctx, "/Send/Send", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// SendServer is the server API for Send service.
+type SendServer interface {
+	Send(context.Context, *MsgSend) (*MsgSendResponse, error)
+}
+
+// UnimplementedSendServer can be embedded to have forward compatible implementations.
+type UnimplementedSendServer struct {
+}
+
+func (*UnimplementedSendServer) Send(ctx context.Context, req *MsgSend) (*MsgSendResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Send not implemented")
+}
+
+func RegisterSendServer(s grpc1.Server, srv SendServer) {
+	s.RegisterService(&_Send_serviceDesc, srv)
+}
+
+func _Send_Send_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgSend)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(SendServer).Send(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/Send/Send",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(SendServer).Send(ctx, req.(*MsgSend))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Send_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "Send",
+	HandlerType: (*SendServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Send",
+			Handler:    _Send_Send_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "messages.proto",
+}
+
+// NestedMessagesClient is the client API for NestedMessages service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type NestedMessagesClient interface {
+	Check(ctx context.Context, in *MsgNestedMessages, opts ...grpc.CallOption) (*MsgCreateNestedMessagesResponse, error)
+}
+
+type nestedMessagesClient struct {
+	cc grpc1.ClientConn
+}
+
+func NewNestedMessagesClient(cc grpc1.ClientConn) NestedMessagesClient {
+	return &nestedMessagesClient{cc}
+}
+
+func (c *nestedMessagesClient) Check(ctx context.Context, in *MsgNestedMessages, opts ...grpc.CallOption) (*MsgCreateNestedMessagesResponse, error) {
+	out := new(MsgCreateNestedMessagesResponse)
+	err := c.cc.Invoke(ctx, "/NestedMessages/Check", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// NestedMessagesServer is the server API for NestedMessages service.
+type NestedMessagesServer interface {
+	Check(context.Context, *MsgNestedMessages) (*MsgCreateNestedMessagesResponse, error)
+}
+
+// UnimplementedNestedMessagesServer can be embedded to have forward compatible implementations.
+type UnimplementedNestedMessagesServer struct {
+}
+
+func (*UnimplementedNestedMessagesServer) Check(ctx context.Context, req *MsgNestedMessages) (*MsgCreateNestedMessagesResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Check not implemented")
+}
+
+func RegisterNestedMessagesServer(s grpc1.Server, srv NestedMessagesServer) {
+	s.RegisterService(&_NestedMessages_serviceDesc, srv)
+}
+
+func _NestedMessages_Check_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgNestedMessages)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(NestedMessagesServer).Check(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/NestedMessages/Check",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(NestedMessagesServer).Check(ctx, req.(*MsgNestedMessages))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _NestedMessages_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "NestedMessages",
+	HandlerType: (*NestedMessagesServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Check",
+			Handler:    _NestedMessages_Check_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "messages.proto",
+}
+
 func (m *MsgCounter) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -725,39 +1066,173 @@ func (m *MsgCreateKeyValueResponse) MarshalToSizedBuffer(dAtA []byte) (int, erro
 	return len(dAtA) - i, nil
 }
 
-func encodeVarintMessages(dAtA []byte, offset int, v uint64) int {
-	offset -= sovMessages(v)
-	base := offset
-	for v >= 1<<7 {
-		dAtA[offset] = uint8(v&0x7f | 0x80)
-		v >>= 7
-		offset++
+func (m *MsgSend) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
 	}
-	dAtA[offset] = uint8(v)
-	return base
+	return dAtA[:n], nil
 }
-func (m *MsgCounter) Size() (n int) {
-	if m == nil {
-		return 0
-	}
+
+func (m *MsgSend) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSend) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
 	var l int
 	_ = l
-	if m.Counter != 0 {
-		n += 1 + sovMessages(uint64(m.Counter))
+	if len(m.Amount) > 0 {
+		i -= len(m.Amount)
+		copy(dAtA[i:], m.Amount)
+		i = encodeVarintMessages(dAtA, i, uint64(len(m.Amount)))
+		i--
+		dAtA[i] = 0x1a
 	}
-	if m.FailOnHandler {
-		n += 2
+	if len(m.To) > 0 {
+		i -= len(m.To)
+		copy(dAtA[i:], m.To)
+		i = encodeVarintMessages(dAtA, i, uint64(len(m.To)))
+		i--
+		dAtA[i] = 0x12
 	}
-	l = len(m.Signer)
-	if l > 0 {
-		n += 1 + l + sovMessages(uint64(l))
+	if len(m.From) > 0 {
+		i -= len(m.From)
+		copy(dAtA[i:], m.From)
+		i = encodeVarintMessages(dAtA, i, uint64(len(m.From)))
+		i--
+		dAtA[i] = 0xa
 	}
-	return n
+	return len(dAtA) - i, nil
 }
 
-func (m *MsgCounter2) Size() (n int) {
-	if m == nil {
-		return 0
+func (m *MsgSendResponse) 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 *MsgSendResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSendResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgNestedMessages) 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 *MsgNestedMessages) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgNestedMessages) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Signer) > 0 {
+		i -= len(m.Signer)
+		copy(dAtA[i:], m.Signer)
+		i = encodeVarintMessages(dAtA, i, uint64(len(m.Signer)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Messages) > 0 {
+		for iNdEx := len(m.Messages) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.Messages[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintMessages(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCreateNestedMessagesResponse) 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 *MsgCreateNestedMessagesResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCreateNestedMessagesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintMessages(dAtA []byte, offset int, v uint64) int {
+	offset -= sovMessages(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *MsgCounter) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.Counter != 0 {
+		n += 1 + sovMessages(uint64(m.Counter))
+	}
+	if m.FailOnHandler {
+		n += 2
+	}
+	l = len(m.Signer)
+	if l > 0 {
+		n += 1 + l + sovMessages(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgCounter2) Size() (n int) {
+	if m == nil {
+		return 0
 	}
 	var l int
 	_ = l
@@ -813,6 +1288,64 @@ func (m *MsgCreateKeyValueResponse) Size() (n int) {
 	return n
 }
 
+func (m *MsgSend) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.From)
+	if l > 0 {
+		n += 1 + l + sovMessages(uint64(l))
+	}
+	l = len(m.To)
+	if l > 0 {
+		n += 1 + l + sovMessages(uint64(l))
+	}
+	l = len(m.Amount)
+	if l > 0 {
+		n += 1 + l + sovMessages(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgSendResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgNestedMessages) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.Messages) > 0 {
+		for _, e := range m.Messages {
+			l = e.Size()
+			n += 1 + l + sovMessages(uint64(l))
+		}
+	}
+	l = len(m.Signer)
+	if l > 0 {
+		n += 1 + l + sovMessages(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgCreateNestedMessagesResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
 func sovMessages(x uint64) (n int) {
 	return (math_bits.Len64(x|1) + 6) / 7
 }
@@ -1311,6 +1844,368 @@ func (m *MsgCreateKeyValueResponse) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *MsgSend) 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 ErrIntOverflowMessages
+			}
+			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: MsgSend: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgSend: 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 ErrIntOverflowMessages
+				}
+				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 ErrInvalidLengthMessages
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthMessages
+			}
+			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 To", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowMessages
+				}
+				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 ErrInvalidLengthMessages
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthMessages
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.To = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowMessages
+				}
+				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 ErrInvalidLengthMessages
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthMessages
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Amount = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipMessages(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthMessages
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgSendResponse) 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 ErrIntOverflowMessages
+			}
+			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: MsgSendResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgSendResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipMessages(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthMessages
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgNestedMessages) 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 ErrIntOverflowMessages
+			}
+			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: MsgNestedMessages: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgNestedMessages: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Messages", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowMessages
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthMessages
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthMessages
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Messages = append(m.Messages, &any.Any{})
+			if err := m.Messages[len(m.Messages)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Signer", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowMessages
+				}
+				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 ErrInvalidLengthMessages
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthMessages
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Signer = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipMessages(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthMessages
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCreateNestedMessagesResponse) 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 ErrIntOverflowMessages
+			}
+			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: MsgCreateNestedMessagesResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCreateNestedMessagesResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipMessages(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthMessages
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func skipMessages(dAtA []byte) (n int, err error) {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/baseapp/testutil/messages.proto b/baseapp/testutil/messages.proto
index d2b25d24c117..d730cad03c04 100644
--- a/baseapp/testutil/messages.proto
+++ b/baseapp/testutil/messages.proto
@@ -34,6 +34,25 @@ message MsgKeyValue {
 
 message MsgCreateKeyValueResponse {}
 
+message MsgSend {
+  option (cosmos.msg.v1.signer) = "from";
+
+  string from = 1;
+  string to = 2;
+  string amount = 3;
+}
+
+message MsgSendResponse {}
+
+message MsgNestedMessages {
+  option (cosmos.msg.v1.signer) = "signer";
+
+  repeated google.protobuf.Any messages = 1;
+  string signer = 2;
+}
+
+message MsgCreateNestedMessagesResponse {}
+
 service Counter {
   rpc IncrementCounter(MsgCounter) returns (MsgCreateCounterResponse);
 }
@@ -44,4 +63,12 @@ service Counter2 {
 
 service KeyValue {
   rpc Set(MsgKeyValue) returns (MsgCreateKeyValueResponse);
-}
\ No newline at end of file
+}
+
+service Send {
+  rpc Send(MsgSend) returns (MsgSendResponse);
+}
+
+service NestedMessages {
+  rpc Check(MsgNestedMessages) returns (MsgCreateNestedMessagesResponse);
+}
diff --git a/baseapp/utils_test.go b/baseapp/utils_test.go
index 8369a42975c7..a127018c15a5 100644
--- a/baseapp/utils_test.go
+++ b/baseapp/utils_test.go
@@ -7,6 +7,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
 	"net/url"
 	"reflect"
 	"strconv"
@@ -375,3 +376,58 @@ func wonkyMsg(t *testing.T, cfg client.TxConfig, tx signing.Tx) signing.Tx {
 	require.NoError(t, err)
 	return builder.GetTx()
 }
+
+type SendServerImpl struct{}
+
+func (s SendServerImpl) Send(ctx context.Context, send *baseapptestutil.MsgSend) (*baseapptestutil.MsgSendResponse, error) {
+
+	if send.From == "" {
+		return nil, errors.New("from address cannot be empty")
+	}
+	if send.To == "" {
+		return nil, errors.New("to address cannot be empty")
+	}
+
+	_, err := sdk.ParseCoinNormalized(send.Amount)
+	if err != nil {
+		return nil, err
+	}
+
+	return &baseapptestutil.MsgSendResponse{}, nil
+}
+
+type NesteMessgesServerImpl struct {
+}
+
+func (n NesteMessgesServerImpl) Check(ctx context.Context, message *baseapptestutil.MsgNestedMessages) (*baseapptestutil.MsgCreateNestedMessagesResponse, error) {
+	cdc := codectestutil.CodecOptions{}.NewCodec()
+	baseapptestutil.RegisterInterfaces(cdc.InterfaceRegistry())
+
+	signer, _, err := cdc.GetMsgV1Signers(message)
+	if err != nil {
+		return nil, err
+	}
+	if len(signer) != 1 {
+		return nil, fmt.Errorf("expected 1 signer, got %d", len(signer))
+	}
+
+	msgs, err := message.GetMsgs()
+	if err != nil {
+		return nil, err
+	}
+
+	for _, msg := range msgs {
+		s, _, err := cdc.GetMsgV1Signers(msg)
+		if err != nil {
+			return nil, err
+		}
+		if len(s) != 1 {
+			return nil, fmt.Errorf("expected 1 signer, got %d", len(s))
+		}
+		if !bytes.Equal(signer[0], s[0]) {
+			return nil, errors.New("signer does not match")
+		}
+
+	}
+	return nil, nil
+}

From cc1efda68cb8e24b514b06e39a791f8d82785f9c Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Mon, 6 May 2024 14:59:00 +0200
Subject: [PATCH 02/14] lint

---
 baseapp/abci_test.go            | 2 +-
 baseapp/testutil/messages.go    | 1 +
 baseapp/testutil/messages.proto | 6 +++---
 baseapp/utils_test.go           | 6 ++----
 4 files changed, 7 insertions(+), 8 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index 47ff4a024f78..34a2439a0402 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -34,6 +34,7 @@ import (
 	snapshottypes "cosmossdk.io/store/snapshots/types"
 	storetypes "cosmossdk.io/store/types"
 	"cosmossdk.io/x/auth/signing"
+
 	"github.com/cosmos/cosmos-sdk/baseapp"
 	baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
 	"github.com/cosmos/cosmos-sdk/baseapp/testutil/mock"
@@ -871,7 +872,6 @@ func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
 				require.NoError(t, err)
 				require.NotNil(t, result)
 			}
-
 		})
 	}
 }
diff --git a/baseapp/testutil/messages.go b/baseapp/testutil/messages.go
index bffce60ba976..1bc8a8bd920f 100644
--- a/baseapp/testutil/messages.go
+++ b/baseapp/testutil/messages.go
@@ -2,6 +2,7 @@ package testutil
 
 import (
 	errorsmod "cosmossdk.io/errors"
+
 	codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
 	"github.com/cosmos/cosmos-sdk/codec/types"
 	"github.com/cosmos/cosmos-sdk/crypto/codec"
diff --git a/baseapp/testutil/messages.proto b/baseapp/testutil/messages.proto
index d730cad03c04..2e0cfd15195f 100644
--- a/baseapp/testutil/messages.proto
+++ b/baseapp/testutil/messages.proto
@@ -37,8 +37,8 @@ message MsgCreateKeyValueResponse {}
 message MsgSend {
   option (cosmos.msg.v1.signer) = "from";
 
-  string from = 1;
-  string to = 2;
+  string from   = 1;
+  string to     = 2;
   string amount = 3;
 }
 
@@ -48,7 +48,7 @@ message MsgNestedMessages {
   option (cosmos.msg.v1.signer) = "signer";
 
   repeated google.protobuf.Any messages = 1;
-  string signer = 2;
+  string                       signer   = 2;
 }
 
 message MsgCreateNestedMessagesResponse {}
diff --git a/baseapp/utils_test.go b/baseapp/utils_test.go
index a127018c15a5..648146a53f50 100644
--- a/baseapp/utils_test.go
+++ b/baseapp/utils_test.go
@@ -7,7 +7,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
 	"net/url"
 	"reflect"
 	"strconv"
@@ -33,6 +32,7 @@ import (
 	baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
 	"github.com/cosmos/cosmos-sdk/client"
 	addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
+	codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
 	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
 	"github.com/cosmos/cosmos-sdk/testutil/testdata"
 	sdk "github.com/cosmos/cosmos-sdk/types"
@@ -380,7 +380,6 @@ func wonkyMsg(t *testing.T, cfg client.TxConfig, tx signing.Tx) signing.Tx {
 type SendServerImpl struct{}
 
 func (s SendServerImpl) Send(ctx context.Context, send *baseapptestutil.MsgSend) (*baseapptestutil.MsgSendResponse, error) {
-
 	if send.From == "" {
 		return nil, errors.New("from address cannot be empty")
 	}
@@ -396,8 +395,7 @@ func (s SendServerImpl) Send(ctx context.Context, send *baseapptestutil.MsgSend)
 	return &baseapptestutil.MsgSendResponse{}, nil
 }
 
-type NesteMessgesServerImpl struct {
-}
+type NesteMessgesServerImpl struct{}
 
 func (n NesteMessgesServerImpl) Check(ctx context.Context, message *baseapptestutil.MsgNestedMessages) (*baseapptestutil.MsgCreateNestedMessagesResponse, error) {
 	cdc := codectestutil.CodecOptions{}.NewCodec()

From 9556fe5b8205186d8aaaf0584a542c2bf2ec8f43 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Mon, 6 May 2024 15:59:18 +0200
Subject: [PATCH 03/14] CHANGELOG

---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 00b22e6b3e42..db72cbc26e09 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -41,6 +41,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
 Every module contains its own CHANGELOG.md. Please refer to the module you are interested in.
 
 ### Features
+
+* (baseapp) [#20291](https://github.com/cosmos/cosmos-sdk/pull/20291) Simualte nested messages.
 * (tests) [#20013](https://github.com/cosmos/cosmos-sdk/pull/20013) Introduce system tests to run multi node local testnet in CI
 * (runtime) [#19953](https://github.com/cosmos/cosmos-sdk/pull/19953) Implement `core/transaction.Service` in runtime.
 * (client) [#19905](https://github.com/cosmos/cosmos-sdk/pull/19905) Add grpc client config to `client.toml`.

From 940916779d37183e2f43cacc0086426b1964cb93 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Mon, 6 May 2024 18:13:19 +0200
Subject: [PATCH 04/14] fix: typo

---
 baseapp/abci_test.go  | 2 +-
 baseapp/utils_test.go | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index 34a2439a0402..9c0a6714a18a 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -774,7 +774,7 @@ func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
 	})
 	require.NoError(t, err)
 
-	baseapptestutil.RegisterNestedMessagesServer(suite.baseApp.MsgServiceRouter(), NesteMessgesServerImpl{})
+	baseapptestutil.RegisterNestedMessagesServer(suite.baseApp.MsgServiceRouter(), NestedMessgesServerImpl{})
 	baseapptestutil.RegisterSendServer(suite.baseApp.MsgServiceRouter(), SendServerImpl{})
 
 	_, _, addr := testdata.KeyTestPubAddr()
diff --git a/baseapp/utils_test.go b/baseapp/utils_test.go
index 648146a53f50..a16f4b63fb78 100644
--- a/baseapp/utils_test.go
+++ b/baseapp/utils_test.go
@@ -395,9 +395,9 @@ func (s SendServerImpl) Send(ctx context.Context, send *baseapptestutil.MsgSend)
 	return &baseapptestutil.MsgSendResponse{}, nil
 }
 
-type NesteMessgesServerImpl struct{}
+type NestedMessgesServerImpl struct{}
 
-func (n NesteMessgesServerImpl) Check(ctx context.Context, message *baseapptestutil.MsgNestedMessages) (*baseapptestutil.MsgCreateNestedMessagesResponse, error) {
+func (n NestedMessgesServerImpl) Check(_ context.Context, message *baseapptestutil.MsgNestedMessages) (*baseapptestutil.MsgCreateNestedMessagesResponse, error) {
 	cdc := codectestutil.CodecOptions{}.NewCodec()
 	baseapptestutil.RegisterInterfaces(cdc.InterfaceRegistry())
 

From 267b4b4b48d23b5b8fc1c0b3c0a043206e0ad375 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Tue, 7 May 2024 10:07:50 +0200
Subject: [PATCH 05/14] typo

---
 CHANGELOG.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index db72cbc26e09..71b9c416caa7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -42,7 +42,7 @@ Every module contains its own CHANGELOG.md. Please refer to the module you are i
 
 ### Features
 
-* (baseapp) [#20291](https://github.com/cosmos/cosmos-sdk/pull/20291) Simualte nested messages.
+* (baseapp) [#20291](https://github.com/cosmos/cosmos-sdk/pull/20291) Simulate nested messages.
 * (tests) [#20013](https://github.com/cosmos/cosmos-sdk/pull/20013) Introduce system tests to run multi node local testnet in CI
 * (runtime) [#19953](https://github.com/cosmos/cosmos-sdk/pull/19953) Implement `core/transaction.Service` in runtime.
 * (client) [#19905](https://github.com/cosmos/cosmos-sdk/pull/19905) Add grpc client config to `client.toml`.

From acfa637baefe6c4ca4547453e9ae82fe066a9394 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Tue, 7 May 2024 10:18:28 +0200
Subject: [PATCH 06/14] fix: InitChainRequest

---
 baseapp/abci_test.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index 54447a6095a3..73bb03a04292 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -23,7 +23,7 @@ import (
 	protoio "github.com/cosmos/gogoproto/io"
 	"github.com/cosmos/gogoproto/jsonpb"
 	"github.com/cosmos/gogoproto/proto"
-  gogotypes "github.com/cosmos/gogoproto/types"
+	gogotypes "github.com/cosmos/gogoproto/types"
 	any "github.com/cosmos/gogoproto/types/any"
 	"github.com/golang/mock/gomock"
 	"github.com/stretchr/testify/require"
@@ -770,7 +770,7 @@ func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
 	}
 	suite := NewBaseAppSuite(t, anteOpt)
 
-	_, err := suite.baseApp.InitChain(&abci.RequestInitChain{
+	_, err := suite.baseApp.InitChain(&abci.InitChainRequest{
 		ConsensusParams: &cmtproto.ConsensusParams{},
 	})
 	require.NoError(t, err)

From e9569ed6764ece10f79763130cc42833ec00d479 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Thu, 9 May 2024 12:29:10 +0200
Subject: [PATCH 07/14] update: use protoadapt instead of unpack

---
 baseapp/baseapp.go | 17 ++---------------
 1 file changed, 2 insertions(+), 15 deletions(-)

diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go
index 0a8af93c615e..a98fe4ca2faf 100644
--- a/baseapp/baseapp.go
+++ b/baseapp/baseapp.go
@@ -13,11 +13,10 @@ import (
 	cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
 	"github.com/cometbft/cometbft/crypto/tmhash"
 	dbm "github.com/cosmos/cosmos-db"
-	"github.com/cosmos/cosmos-proto/anyutil"
 	"github.com/cosmos/gogoproto/proto"
 	"golang.org/x/exp/maps"
 	protov2 "google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/types/known/anypb"
+	"google.golang.org/protobuf/protoadapt"
 
 	"cosmossdk.io/core/header"
 	errorsmod "cosmossdk.io/errors"
@@ -1104,19 +1103,7 @@ func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg HasNestedMsgs) e
 func (app *BaseApp) msgsV1ToMsgsV2(msgs []sdk.Msg) ([]protov2.Message, error) {
 	msgsV2 := make([]protov2.Message, len(msgs))
 	for i, msg := range msgs {
-		gogoAny, err := codectypes.NewAnyWithValue(msg)
-		if err != nil {
-			return nil, err
-		}
-		anyMsg := &anypb.Any{
-			TypeUrl: gogoAny.TypeUrl,
-			Value:   gogoAny.Value,
-		}
-		msgV2, err := anyutil.Unpack(anyMsg, app.cdc.InterfaceRegistry().SigningContext().FileResolver(), app.cdc.InterfaceRegistry().SigningContext().TypeResolver())
-		if err != nil {
-			return nil, err
-		}
-		msgsV2[i] = msgV2
+		msgsV2[i] = protoadapt.MessageV2Of(msg)
 	}
 
 	return msgsV2, nil

From 6d0e987299c66226491609204a770ca558c607c9 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Thu, 9 May 2024 16:44:03 +0200
Subject: [PATCH 08/14] update: check for nested messages in nested messages

---
 baseapp/abci_test.go | 123 +++++++++++++++++++++++++++++--------------
 baseapp/baseapp.go   |  37 ++++++++++---
 2 files changed, 112 insertions(+), 48 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index 73bb03a04292..0f1de773cbda 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -14,6 +14,8 @@ import (
 	"testing"
 	"time"
 
+	"github.com/cosmos/cosmos-sdk/codec"
+
 	abci "github.com/cometbft/cometbft/abci/types"
 	cmtprotocrypto "github.com/cometbft/cometbft/api/cometbft/crypto/v1"
 	cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
@@ -760,6 +762,16 @@ func TestABCI_FinalizeBlock_MultiMsg(t *testing.T) {
 	require.Equal(t, int64(2), msgCounter2)
 }
 
+func anyMessage(t *testing.T, cdc codec.Codec, msg *baseapptestutil.MsgSend) *any.Any {
+	t.Helper()
+	b, err := cdc.Marshal(msg)
+	require.NoError(t, err)
+	return &any.Any{
+		TypeUrl: sdk.MsgTypeURL(msg),
+		Value:   b,
+	}
+}
+
 func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
 	gasConsumed := uint64(5)
 	anteOpt := func(bapp *baseapp.BaseApp) {
@@ -781,60 +793,92 @@ func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
 	_, _, addr := testdata.KeyTestPubAddr()
 	_, _, toAddr := testdata.KeyTestPubAddr()
 	tests := []struct {
-		name       string
-		nestedMsgs []*baseapptestutil.MsgSend
-		wantErr    bool
+		name    string
+		message sdk.Msg
+		wantErr bool
 	}{
 		{
 			name: "ok nested message",
-			nestedMsgs: []*baseapptestutil.MsgSend{
-				{
-					From:   addr.String(),
-					To:     toAddr.String(),
-					Amount: "10000stake",
-				},
+			message: &baseapptestutil.MsgSend{
+				From:   addr.String(),
+				To:     toAddr.String(),
+				Amount: "10000stake",
 			},
 		},
 		{
 			name: "different signers",
-			nestedMsgs: []*baseapptestutil.MsgSend{
-				{
-					From:   toAddr.String(),
-					To:     addr.String(),
-					Amount: "10000stake",
-				},
+			message: &baseapptestutil.MsgSend{
+				From:   toAddr.String(),
+				To:     addr.String(),
+				Amount: "10000stake",
 			},
 			wantErr: true,
 		},
 		{
 			name: "empty from",
-			nestedMsgs: []*baseapptestutil.MsgSend{
-				{
-					From:   "",
-					To:     toAddr.String(),
-					Amount: "10000stake",
-				},
+			message: &baseapptestutil.MsgSend{
+				From:   "",
+				To:     toAddr.String(),
+				Amount: "10000stake",
 			},
 			wantErr: true,
 		},
 		{
 			name: "empty to",
-			nestedMsgs: []*baseapptestutil.MsgSend{
-				{
-					From:   addr.String(),
-					To:     "",
-					Amount: "10000stake",
-				},
+			message: &baseapptestutil.MsgSend{
+				From:   addr.String(),
+				To:     "",
+				Amount: "10000stake",
 			},
 			wantErr: true,
 		},
 		{
 			name: "negative amount",
-			nestedMsgs: []*baseapptestutil.MsgSend{
-				{
-					From:   addr.String(),
-					To:     toAddr.String(),
-					Amount: "-10000stake",
+			message: &baseapptestutil.MsgSend{
+				From:   addr.String(),
+				To:     toAddr.String(),
+				Amount: "-10000stake",
+			},
+			wantErr: true,
+		},
+		{
+			name: "with nested messages",
+			message: &baseapptestutil.MsgNestedMessages{
+				Signer: addr.String(),
+				Messages: []*any.Any{
+					anyMessage(t, suite.cdc, &baseapptestutil.MsgSend{
+						From:   addr.String(),
+						To:     toAddr.String(),
+						Amount: "10000stake",
+					}),
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "with invalid nested messages",
+			message: &baseapptestutil.MsgNestedMessages{
+				Signer: addr.String(),
+				Messages: []*any.Any{
+					anyMessage(t, suite.cdc, &baseapptestutil.MsgSend{
+						From:   "",
+						To:     toAddr.String(),
+						Amount: "10000stake",
+					}),
+				},
+			},
+			wantErr: true,
+		},
+		{
+			name: "with different signer ",
+			message: &baseapptestutil.MsgNestedMessages{
+				Signer: addr.String(),
+				Messages: []*any.Any{
+					anyMessage(t, suite.cdc, &baseapptestutil.MsgSend{
+						From:   toAddr.String(),
+						To:     addr.String(),
+						Amount: "10000stake",
+					}),
 				},
 			},
 			wantErr: true,
@@ -842,15 +886,14 @@ func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			nestedMessages := make([]*any.Any, len(tt.nestedMsgs))
-			for i, msg := range tt.nestedMsgs {
-				b, err := suite.cdc.Marshal(msg)
-				require.NoError(t, err)
-				nestedMessages[i] = &any.Any{
-					TypeUrl: sdk.MsgTypeURL(msg),
-					Value:   b,
-				}
+			nestedMessages := make([]*any.Any, 1)
+			b, err := suite.cdc.Marshal(tt.message)
+			require.NoError(t, err)
+			nestedMessages[0] = &any.Any{
+				TypeUrl: sdk.MsgTypeURL(tt.message),
+				Value:   b,
 			}
+
 			msg := &baseapptestutil.MsgNestedMessages{
 				Messages: nestedMessages,
 				Signer:   addr.String(),
diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go
index a98fe4ca2faf..91e4a97da5e7 100644
--- a/baseapp/baseapp.go
+++ b/baseapp/baseapp.go
@@ -13,10 +13,11 @@ import (
 	cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
 	"github.com/cometbft/cometbft/crypto/tmhash"
 	dbm "github.com/cosmos/cosmos-db"
+	"github.com/cosmos/cosmos-proto/anyutil"
 	"github.com/cosmos/gogoproto/proto"
 	"golang.org/x/exp/maps"
 	protov2 "google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/protoadapt"
+	"google.golang.org/protobuf/types/known/anypb"
 
 	"cosmossdk.io/core/header"
 	errorsmod "cosmossdk.io/errors"
@@ -962,10 +963,6 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res
 	if mode == execModeSimulate {
 		nestedMsgsContext, _ := app.cacheTxContext(ctx, txBytes)
 		for _, msg := range msgs {
-			msg, ok := msg.(HasNestedMsgs)
-			if !ok {
-				continue
-			}
 			nestedErr := app.simulateNestedMessages(nestedMsgsContext, msg)
 			if nestedErr != nil {
 				return gInfo, nil, anteEvents, nestedErr
@@ -1080,12 +1077,24 @@ func (app *BaseApp) runMsgs(ctx sdk.Context, msgs []sdk.Msg, msgsV2 []protov2.Me
 }
 
 // simulateNestedMessages simulates a message nested messages.
-func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg HasNestedMsgs) error {
-	msgs, err := msg.GetMsgs()
+func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg sdk.Msg) error {
+	nestedMsgs, ok := msg.(HasNestedMsgs)
+	if !ok {
+		return nil
+	}
+
+	msgs, err := nestedMsgs.GetMsgs()
 	if err != nil {
 		return err
 	}
 
+	for _, msg := range msgs {
+		err = app.simulateNestedMessages(ctx, msg)
+		if err != nil {
+			return err
+		}
+	}
+
 	if err := validateBasicTxMsgs(app.msgServiceRouter, msgs); err != nil {
 		return err
 	}
@@ -1103,7 +1112,19 @@ func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg HasNestedMsgs) e
 func (app *BaseApp) msgsV1ToMsgsV2(msgs []sdk.Msg) ([]protov2.Message, error) {
 	msgsV2 := make([]protov2.Message, len(msgs))
 	for i, msg := range msgs {
-		msgsV2[i] = protoadapt.MessageV2Of(msg)
+		gogoAny, err := codectypes.NewAnyWithValue(msg)
+		if err != nil {
+			return nil, err
+		}
+		anyMsg := &anypb.Any{
+			TypeUrl: gogoAny.TypeUrl,
+			Value:   gogoAny.Value,
+		}
+		msgV2, err := anyutil.Unpack(anyMsg, app.cdc.InterfaceRegistry().SigningContext().FileResolver(), app.cdc.InterfaceRegistry().SigningContext().TypeResolver())
+		if err != nil {
+			return nil, err
+		}
+		msgsV2[i] = msgV2
 	}
 
 	return msgsV2, nil

From f1544b226b486bf3eb43f3b571080ff9459dd19d Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Mon, 24 Jun 2024 11:31:55 +0200
Subject: [PATCH 09/14] fix: use protoreflect

---
 baseapp/abci_test.go  |  1 -
 baseapp/baseapp.go    | 14 +++++++++-----
 baseapp/utils_test.go |  4 ++--
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index bd280f0c98bb..7c9074180109 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -16,7 +16,6 @@ import (
 
 	"github.com/cosmos/cosmos-sdk/codec"
 
-	abci "github.com/cometbft/cometbft/abci/types"
 	abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
 	cmtprotocrypto "github.com/cometbft/cometbft/api/cometbft/crypto/v1"
 	cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go
index 06e558a2104d..feff33e980f6 100644
--- a/baseapp/baseapp.go
+++ b/baseapp/baseapp.go
@@ -17,8 +17,8 @@ import (
 	"github.com/cosmos/gogoproto/proto"
 	"golang.org/x/exp/maps"
 	protov2 "google.golang.org/protobuf/proto"
-	"google.golang.org/protobuf/types/known/anypb"
 	"google.golang.org/protobuf/reflect/protoreflect"
+	"google.golang.org/protobuf/types/known/anypb"
 
 	"cosmossdk.io/core/header"
 	"cosmossdk.io/core/log"
@@ -1101,12 +1101,16 @@ func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg sdk.Msg) error {
 		return err
 	}
 
-	msgsV2, err := app.msgsV1ToMsgsV2(msgs)
-	if err != nil {
-		return err
+	protoMessages := make([]protoreflect.Message, len(msgs))
+	for i, msg := range msgs {
+		_, protoMsg, err := app.cdc.GetMsgSigners(msg)
+		if err != nil {
+			return err
+		}
+		protoMessages[i] = protoMsg
 	}
 
-	_, err = app.runMsgs(ctx, msgs, msgsV2, execModeSimulate)
+	_, err = app.runMsgs(ctx, msgs, protoMessages, execModeSimulate)
 	return err
 }
 
diff --git a/baseapp/utils_test.go b/baseapp/utils_test.go
index ded4f0d8d272..6cb86ceee2c5 100644
--- a/baseapp/utils_test.go
+++ b/baseapp/utils_test.go
@@ -401,7 +401,7 @@ func (n NestedMessgesServerImpl) Check(_ context.Context, message *baseapptestut
 	cdc := codectestutil.CodecOptions{}.NewCodec()
 	baseapptestutil.RegisterInterfaces(cdc.InterfaceRegistry())
 
-	signer, _, err := cdc.GetMsgV1Signers(message)
+	signer, _, err := cdc.GetMsgSigners(message)
 	if err != nil {
 		return nil, err
 	}
@@ -415,7 +415,7 @@ func (n NestedMessgesServerImpl) Check(_ context.Context, message *baseapptestut
 	}
 
 	for _, msg := range msgs {
-		s, _, err := cdc.GetMsgV1Signers(msg)
+		s, _, err := cdc.GetMsgSigners(msg)
 		if err != nil {
 			return nil, err
 		}

From 60516193c4b3f80fabc025c4c4dee882ac0177cc Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Tue, 25 Jun 2024 11:15:58 +0200
Subject: [PATCH 10/14] update: check if gas should be added

---
 baseapp/abci_test.go   | 82 ++++++++++++++++++++++++++++++++++++++++--
 baseapp/baseapp.go     | 14 ++++++--
 baseapp/options.go     | 13 +++++++
 baseapp/utils_test.go  | 24 ++++++++++---
 simapp/app.go          |  3 +-
 simapp/app_di.go       |  4 +--
 x/gov/keeper/config.go |  4 +++
 7 files changed, 131 insertions(+), 13 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index 7c9074180109..6627917a4fbb 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -774,10 +774,9 @@ func anyMessage(t *testing.T, cdc codec.Codec, msg *baseapptestutil.MsgSend) *an
 }
 
 func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
-	gasConsumed := uint64(5)
 	anteOpt := func(bapp *baseapp.BaseApp) {
 		bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
-			newCtx = ctx.WithGasMeter(storetypes.NewGasMeter(gasConsumed))
+			newCtx = ctx.WithGasMeter(storetypes.NewGasMeter(uint64(15)))
 			return
 		})
 	}
@@ -854,7 +853,6 @@ func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
 					}),
 				},
 			},
-			wantErr: false,
 		},
 		{
 			name: "with invalid nested messages",
@@ -921,6 +919,84 @@ func TestABCI_Query_SimulateNestedMessagesTx(t *testing.T) {
 	}
 }
 
+func TestABCI_Query_SimulateNestedMessagesGas(t *testing.T) {
+	anteOpt := func(bapp *baseapp.BaseApp) {
+		bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
+			newCtx = ctx.WithGasMeter(storetypes.NewGasMeter(uint64(10)))
+			return
+		})
+	}
+
+	_, _, addr := testdata.KeyTestPubAddr()
+	_, _, toAddr := testdata.KeyTestPubAddr()
+
+	tests := []struct {
+		name        string
+		suite       *BaseAppSuite
+		message     sdk.Msg
+		consumedGas uint64
+	}{
+		{
+			name:  "add gas",
+			suite: NewBaseAppSuite(t, anteOpt),
+			message: &baseapptestutil.MsgSend{
+				From:   addr.String(),
+				To:     toAddr.String(),
+				Amount: "10000stake",
+			},
+			consumedGas: 10,
+		},
+		{
+			name:  "don't add gas",
+			suite: NewBaseAppSuite(t, anteOpt, baseapp.SetExcludeNestedMsgsGas([]sdk.Msg{&baseapptestutil.MsgNestedMessages{}})),
+			message: &baseapptestutil.MsgSend{
+				From:   addr.String(),
+				To:     toAddr.String(),
+				Amount: "10000stake",
+			},
+			consumedGas: 5,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			_, err := tt.suite.baseApp.InitChain(&abci.InitChainRequest{
+				ConsensusParams: &cmtproto.ConsensusParams{},
+			})
+			require.NoError(t, err)
+
+			baseapptestutil.RegisterNestedMessagesServer(tt.suite.baseApp.MsgServiceRouter(), NestedMessgesServerImpl{})
+			baseapptestutil.RegisterSendServer(tt.suite.baseApp.MsgServiceRouter(), SendServerImpl{})
+
+			nestedMessages := make([]*any.Any, 1)
+			b, err := tt.suite.cdc.Marshal(tt.message)
+			require.NoError(t, err)
+			nestedMessages[0] = &any.Any{
+				TypeUrl: sdk.MsgTypeURL(tt.message),
+				Value:   b,
+			}
+
+			msg := &baseapptestutil.MsgNestedMessages{
+				Messages: nestedMessages,
+				Signer:   addr.String(),
+			}
+
+			builder := tt.suite.txConfig.NewTxBuilder()
+			err = builder.SetMsgs(msg)
+			require.NoError(t, err)
+			setTxSignature(t, builder, 0)
+			tx := builder.GetTx()
+
+			txBytes, err := tt.suite.txConfig.TxEncoder()(tx)
+			require.Nil(t, err)
+
+			gas, result, err := tt.suite.baseApp.Simulate(txBytes)
+			require.NoError(t, err)
+			require.NotNil(t, result)
+			require.True(t, gas.GasUsed == tt.consumedGas)
+		})
+	}
+}
+
 func TestABCI_Query_SimulateTx(t *testing.T) {
 	gasConsumed := uint64(5)
 	anteOpt := func(bapp *baseapp.BaseApp) {
diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go
index feff33e980f6..50064d06a894 100644
--- a/baseapp/baseapp.go
+++ b/baseapp/baseapp.go
@@ -189,6 +189,9 @@ type BaseApp struct {
 	// including the goroutine handling.This is experimental and must be enabled
 	// by developers.
 	optimisticExec *oe.OptimisticExecution
+
+	// excludeNestedMsgsGas holds a set of message types for which gas costs for its nested messages are not calculated.
+	excludeNestedMsgsGas map[string]struct{}
 }
 
 // NewBaseApp returns a reference to an initialized BaseApp. It accepts a
@@ -236,7 +239,9 @@ func NewBaseApp(
 	if app.interBlockCache != nil {
 		app.cms.SetInterBlockCache(app.interBlockCache)
 	}
-
+	if app.excludeNestedMsgsGas == nil {
+		app.excludeNestedMsgsGas = make(map[string]struct{})
+	}
 	app.runTxRecoveryMiddleware = newDefaultRecoveryMiddleware()
 
 	// Initialize with an empty interface registry to avoid nil pointer dereference.
@@ -963,12 +968,15 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res
 	}
 
 	if mode == execModeSimulate {
-		nestedMsgsContext, _ := app.cacheTxContext(ctx, txBytes)
+		gas := ctx.GasMeter().GasConsumed()
 		for _, msg := range msgs {
-			nestedErr := app.simulateNestedMessages(nestedMsgsContext, msg)
+			nestedErr := app.simulateNestedMessages(ctx, msg)
 			if nestedErr != nil {
 				return gInfo, nil, anteEvents, nestedErr
 			}
+			if _, ok := app.excludeNestedMsgsGas[sdk.MsgTypeURL(msg)]; ok {
+				ctx.GasMeter().RefundGas(ctx.GasMeter().GasConsumed()-gas, "simulation of nested messages")
+			}
 		}
 	}
 
diff --git a/baseapp/options.go b/baseapp/options.go
index 372c2411cf82..79aa2792b477 100644
--- a/baseapp/options.go
+++ b/baseapp/options.go
@@ -119,6 +119,19 @@ func SetOptimisticExecution(opts ...func(*oe.OptimisticExecution)) func(*BaseApp
 	}
 }
 
+// SetExcludeNestedMsgsGas sets the message types for which gas costs for its nested messages are not calculated when simulating.
+func SetExcludeNestedMsgsGas(msgs []sdk.Msg) func(*BaseApp) {
+	return func(app *BaseApp) {
+		app.excludeNestedMsgsGas = make(map[string]struct{})
+		for _, msg := range msgs {
+			if _, ok := msg.(HasNestedMsgs); !ok {
+				continue
+			}
+			app.excludeNestedMsgsGas[sdk.MsgTypeURL(msg)] = struct{}{}
+		}
+	}
+}
+
 func (app *BaseApp) SetName(name string) {
 	if app.sealed {
 		panic("SetName() on sealed BaseApp")
diff --git a/baseapp/utils_test.go b/baseapp/utils_test.go
index 6cb86ceee2c5..27668ce590c5 100644
--- a/baseapp/utils_test.go
+++ b/baseapp/utils_test.go
@@ -377,9 +377,12 @@ func wonkyMsg(t *testing.T, cfg client.TxConfig, tx signing.Tx) signing.Tx {
 	return builder.GetTx()
 }
 
-type SendServerImpl struct{}
+type SendServerImpl struct {
+	gas uint64
+}
 
 func (s SendServerImpl) Send(ctx context.Context, send *baseapptestutil.MsgSend) (*baseapptestutil.MsgSendResponse, error) {
+	sdkCtx := sdk.UnwrapSDKContext(ctx)
 	if send.From == "" {
 		return nil, errors.New("from address cannot be empty")
 	}
@@ -391,13 +394,20 @@ func (s SendServerImpl) Send(ctx context.Context, send *baseapptestutil.MsgSend)
 	if err != nil {
 		return nil, err
 	}
-
+	gas := s.gas
+	if gas == 0 {
+		gas = 5
+	}
+	sdkCtx.GasMeter().ConsumeGas(gas, "send test")
 	return &baseapptestutil.MsgSendResponse{}, nil
 }
 
-type NestedMessgesServerImpl struct{}
+type NestedMessgesServerImpl struct {
+	gas uint64
+}
 
-func (n NestedMessgesServerImpl) Check(_ context.Context, message *baseapptestutil.MsgNestedMessages) (*baseapptestutil.MsgCreateNestedMessagesResponse, error) {
+func (n NestedMessgesServerImpl) Check(ctx context.Context, message *baseapptestutil.MsgNestedMessages) (*baseapptestutil.MsgCreateNestedMessagesResponse, error) {
+	sdkCtx := sdk.UnwrapSDKContext(ctx)
 	cdc := codectestutil.CodecOptions{}.NewCodec()
 	baseapptestutil.RegisterInterfaces(cdc.InterfaceRegistry())
 
@@ -427,5 +437,11 @@ func (n NestedMessgesServerImpl) Check(_ context.Context, message *baseapptestut
 		}
 
 	}
+
+	gas := n.gas
+	if gas == 0 {
+		gas = 5
+	}
+	sdkCtx.GasMeter().ConsumeGas(gas, "nested messages test")
 	return nil, nil
 }
diff --git a/simapp/app.go b/simapp/app.go
index c441659f18c1..1d4bdd47b6af 100644
--- a/simapp/app.go
+++ b/simapp/app.go
@@ -246,7 +246,8 @@ func NewSimApp(
 		voteExtHandler := NewVoteExtensionHandler()
 		voteExtHandler.SetHandlers(bApp)
 	}
-	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution())
+	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution(),
+		baseapp.SetExcludeNestedMsgsGas(govkeeper.DefaultConfig().ExcludedNestedMsgsForGasSim))
 
 	bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...)
 	bApp.SetCommitMultiStoreTracer(traceStore)
diff --git a/simapp/app_di.go b/simapp/app_di.go
index d2b27c7eefdc..78ed2944183c 100644
--- a/simapp/app_di.go
+++ b/simapp/app_di.go
@@ -137,7 +137,6 @@ func NewSimApp(
 				appOpts,
 				// supply the logger
 				logger,
-
 				// ADVANCED CONFIGURATION
 
 				//
@@ -239,7 +238,8 @@ func NewSimApp(
 		voteExtHandler := NewVoteExtensionHandler()
 		voteExtHandler.SetHandlers(bApp)
 	}
-	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution())
+	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution(),
+		baseapp.SetExcludeNestedMsgsGas(govkeeper.DefaultConfig().ExcludedNestedMsgsForGasSim))
 
 	app.App = appBuilder.Build(db, traceStore, baseAppOptions...)
 
diff --git a/x/gov/keeper/config.go b/x/gov/keeper/config.go
index ea01891974a5..b066dce7396f 100644
--- a/x/gov/keeper/config.go
+++ b/x/gov/keeper/config.go
@@ -3,6 +3,7 @@ package keeper
 import (
 	"context"
 
+	"cosmossdk.io/core/transaction"
 	"cosmossdk.io/math"
 	v1 "cosmossdk.io/x/gov/types/v1"
 )
@@ -33,6 +34,8 @@ type Config struct {
 	// CalculateVoteResultsAndVotingPowerFn is a function signature for calculating vote results and voting power
 	// Keeping it nil will use the default implementation
 	CalculateVoteResultsAndVotingPowerFn CalculateVoteResultsAndVotingPowerFn
+	// ExcludedNestedMsgsForGasSim is a list of messages that will avoid adding the gas of their nested messages when simulating
+	ExcludedNestedMsgsForGasSim []transaction.Msg
 }
 
 // DefaultConfig returns the default config for gov.
@@ -43,5 +46,6 @@ func DefaultConfig() Config {
 		MaxSummaryLen:                        10200,
 		MaxVoteOptionsLen:                    0, // 0 means this param is disabled, hence all supported options are allowed
 		CalculateVoteResultsAndVotingPowerFn: nil,
+		ExcludedNestedMsgsForGasSim:          []transaction.Msg{&v1.MsgSubmitProposal{}},
 	}
 }

From c035d34f310860549f41fc59e8d81eb89256be86 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Tue, 25 Jun 2024 11:24:21 +0200
Subject: [PATCH 11/14] lint

---
 baseapp/abci_test.go |  3 +--
 baseapp/baseapp.go   | 25 -------------------------
 2 files changed, 1 insertion(+), 27 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index 6627917a4fbb..3d9197ebb3c1 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -14,8 +14,6 @@ import (
 	"testing"
 	"time"
 
-	"github.com/cosmos/cosmos-sdk/codec"
-
 	abci "github.com/cometbft/cometbft/api/cometbft/abci/v1"
 	cmtprotocrypto "github.com/cometbft/cometbft/api/cometbft/crypto/v1"
 	cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
@@ -41,6 +39,7 @@ import (
 	"github.com/cosmos/cosmos-sdk/baseapp"
 	baseapptestutil "github.com/cosmos/cosmos-sdk/baseapp/testutil"
 	"github.com/cosmos/cosmos-sdk/baseapp/testutil/mock"
+	"github.com/cosmos/cosmos-sdk/codec"
 	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
 	"github.com/cosmos/cosmos-sdk/testutil"
 	"github.com/cosmos/cosmos-sdk/testutil/testdata"
diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go
index 50064d06a894..9a3bb1666922 100644
--- a/baseapp/baseapp.go
+++ b/baseapp/baseapp.go
@@ -13,12 +13,9 @@ import (
 	cmtproto "github.com/cometbft/cometbft/api/cometbft/types/v1"
 	"github.com/cometbft/cometbft/crypto/tmhash"
 	dbm "github.com/cosmos/cosmos-db"
-	"github.com/cosmos/cosmos-proto/anyutil"
 	"github.com/cosmos/gogoproto/proto"
 	"golang.org/x/exp/maps"
-	protov2 "google.golang.org/protobuf/proto"
 	"google.golang.org/protobuf/reflect/protoreflect"
-	"google.golang.org/protobuf/types/known/anypb"
 
 	"cosmossdk.io/core/header"
 	"cosmossdk.io/core/log"
@@ -1122,28 +1119,6 @@ func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg sdk.Msg) error {
 	return err
 }
 
-// msgsV1ToMsgsV2 transforms v1 messages into v2.
-func (app *BaseApp) msgsV1ToMsgsV2(msgs []sdk.Msg) ([]protov2.Message, error) {
-	msgsV2 := make([]protov2.Message, len(msgs))
-	for i, msg := range msgs {
-		gogoAny, err := codectypes.NewAnyWithValue(msg)
-		if err != nil {
-			return nil, err
-		}
-		anyMsg := &anypb.Any{
-			TypeUrl: gogoAny.TypeUrl,
-			Value:   gogoAny.Value,
-		}
-		msgV2, err := anyutil.Unpack(anyMsg, app.cdc.InterfaceRegistry().SigningContext().FileResolver(), app.cdc.InterfaceRegistry().SigningContext().TypeResolver())
-		if err != nil {
-			return nil, err
-		}
-		msgsV2[i] = msgV2
-	}
-
-	return msgsV2, nil
-}
-
 // makeABCIData generates the Data field to be sent to ABCI Check/DeliverTx.
 func makeABCIData(msgResponses []*codectypes.Any) ([]byte, error) {
 	return proto.Marshal(&sdk.TxMsgData{MsgResponses: msgResponses})

From e5362a59cc026c2fab80dd336954cc9c68698ab4 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Mon, 15 Jul 2024 12:40:14 +0200
Subject: [PATCH 12/14] update: gas is not added by default

---
 baseapp/abci_test.go   | 10 +++++-----
 baseapp/baseapp.go     | 27 +++++++++++++++------------
 baseapp/options.go     |  8 ++++----
 simapp/app.go          |  3 +--
 simapp/app_di.go       |  3 +--
 x/gov/keeper/config.go |  4 ----
 6 files changed, 26 insertions(+), 29 deletions(-)

diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go
index 3d9197ebb3c1..4fea776a8bf7 100644
--- a/baseapp/abci_test.go
+++ b/baseapp/abci_test.go
@@ -936,24 +936,24 @@ func TestABCI_Query_SimulateNestedMessagesGas(t *testing.T) {
 		consumedGas uint64
 	}{
 		{
-			name:  "add gas",
+			name:  "don't add gas",
 			suite: NewBaseAppSuite(t, anteOpt),
 			message: &baseapptestutil.MsgSend{
 				From:   addr.String(),
 				To:     toAddr.String(),
 				Amount: "10000stake",
 			},
-			consumedGas: 10,
+			consumedGas: 5,
 		},
 		{
-			name:  "don't add gas",
-			suite: NewBaseAppSuite(t, anteOpt, baseapp.SetExcludeNestedMsgsGas([]sdk.Msg{&baseapptestutil.MsgNestedMessages{}})),
+			name:  "add gas",
+			suite: NewBaseAppSuite(t, anteOpt, baseapp.SetIncludeNestedMsgsGas([]sdk.Msg{&baseapptestutil.MsgNestedMessages{}})),
 			message: &baseapptestutil.MsgSend{
 				From:   addr.String(),
 				To:     toAddr.String(),
 				Amount: "10000stake",
 			},
-			consumedGas: 5,
+			consumedGas: 10,
 		},
 	}
 	for _, tt := range tests {
diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go
index f00887b35b77..3599a35517ec 100644
--- a/baseapp/baseapp.go
+++ b/baseapp/baseapp.go
@@ -187,8 +187,8 @@ type BaseApp struct {
 	// by developers.
 	optimisticExec *oe.OptimisticExecution
 
-	// excludeNestedMsgsGas holds a set of message types for which gas costs for its nested messages are not calculated.
-	excludeNestedMsgsGas map[string]struct{}
+	// includeNestedMsgsGas holds a set of message types for which gas costs for its nested messages are calculated.
+	includeNestedMsgsGas map[string]struct{}
 }
 
 // NewBaseApp returns a reference to an initialized BaseApp. It accepts a
@@ -236,8 +236,8 @@ func NewBaseApp(
 	if app.interBlockCache != nil {
 		app.cms.SetInterBlockCache(app.interBlockCache)
 	}
-	if app.excludeNestedMsgsGas == nil {
-		app.excludeNestedMsgsGas = make(map[string]struct{})
+	if app.includeNestedMsgsGas == nil {
+		app.includeNestedMsgsGas = make(map[string]struct{})
 	}
 	app.runTxRecoveryMiddleware = newDefaultRecoveryMiddleware()
 
@@ -965,15 +965,11 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res
 	}
 
 	if mode == execModeSimulate {
-		gas := ctx.GasMeter().GasConsumed()
 		for _, msg := range msgs {
 			nestedErr := app.simulateNestedMessages(ctx, msg)
 			if nestedErr != nil {
 				return gInfo, nil, anteEvents, nestedErr
 			}
-			if _, ok := app.excludeNestedMsgsGas[sdk.MsgTypeURL(msg)]; ok {
-				ctx.GasMeter().RefundGas(ctx.GasMeter().GasConsumed()-gas, "simulation of nested messages")
-			}
 		}
 	}
 
@@ -1095,6 +1091,10 @@ func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg sdk.Msg) error {
 		return err
 	}
 
+	if err := validateBasicTxMsgs(app.msgServiceRouter, msgs); err != nil {
+		return err
+	}
+
 	for _, msg := range msgs {
 		err = app.simulateNestedMessages(ctx, msg)
 		if err != nil {
@@ -1102,10 +1102,6 @@ func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg sdk.Msg) error {
 		}
 	}
 
-	if err := validateBasicTxMsgs(app.msgServiceRouter, msgs); err != nil {
-		return err
-	}
-
 	protoMessages := make([]protoreflect.Message, len(msgs))
 	for i, msg := range msgs {
 		_, protoMsg, err := app.cdc.GetMsgSigners(msg)
@@ -1115,7 +1111,14 @@ func (app *BaseApp) simulateNestedMessages(ctx sdk.Context, msg sdk.Msg) error {
 		protoMessages[i] = protoMsg
 	}
 
+	initialGas := ctx.GasMeter().GasConsumed()
 	_, err = app.runMsgs(ctx, msgs, protoMessages, execModeSimulate)
+	if err == nil {
+		if _, includeGas := app.includeNestedMsgsGas[sdk.MsgTypeURL(msg)]; !includeGas {
+			consumedGas := ctx.GasMeter().GasConsumed() - initialGas
+			ctx.GasMeter().RefundGas(consumedGas, "simulation of nested messages")
+		}
+	}
 	return err
 }
 
diff --git a/baseapp/options.go b/baseapp/options.go
index 79aa2792b477..bcff418fb8b0 100644
--- a/baseapp/options.go
+++ b/baseapp/options.go
@@ -119,15 +119,15 @@ func SetOptimisticExecution(opts ...func(*oe.OptimisticExecution)) func(*BaseApp
 	}
 }
 
-// SetExcludeNestedMsgsGas sets the message types for which gas costs for its nested messages are not calculated when simulating.
-func SetExcludeNestedMsgsGas(msgs []sdk.Msg) func(*BaseApp) {
+// SetIncludeNestedMsgsGas sets the message types for which gas costs for its nested messages are calculated when simulating.
+func SetIncludeNestedMsgsGas(msgs []sdk.Msg) func(*BaseApp) {
 	return func(app *BaseApp) {
-		app.excludeNestedMsgsGas = make(map[string]struct{})
+		app.includeNestedMsgsGas = make(map[string]struct{})
 		for _, msg := range msgs {
 			if _, ok := msg.(HasNestedMsgs); !ok {
 				continue
 			}
-			app.excludeNestedMsgsGas[sdk.MsgTypeURL(msg)] = struct{}{}
+			app.includeNestedMsgsGas[sdk.MsgTypeURL(msg)] = struct{}{}
 		}
 	}
 }
diff --git a/simapp/app.go b/simapp/app.go
index 7a80caf2d000..d5a0269f8197 100644
--- a/simapp/app.go
+++ b/simapp/app.go
@@ -246,8 +246,7 @@ func NewSimApp(
 		voteExtHandler := NewVoteExtensionHandler()
 		voteExtHandler.SetHandlers(bApp)
 	}
-	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution(),
-		baseapp.SetExcludeNestedMsgsGas(govkeeper.DefaultConfig().ExcludedNestedMsgsForGasSim))
+	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution())
 
 	bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...)
 	bApp.SetCommitMultiStoreTracer(traceStore)
diff --git a/simapp/app_di.go b/simapp/app_di.go
index fbe269ffab93..63beee09ab7e 100644
--- a/simapp/app_di.go
+++ b/simapp/app_di.go
@@ -240,8 +240,7 @@ func NewSimApp(
 		voteExtHandler := NewVoteExtensionHandler()
 		voteExtHandler.SetHandlers(bApp)
 	}
-	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution(),
-		baseapp.SetExcludeNestedMsgsGas(govkeeper.DefaultConfig().ExcludedNestedMsgsForGasSim))
+	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution())
 
 	app.App = appBuilder.Build(db, traceStore, baseAppOptions...)
 
diff --git a/x/gov/keeper/config.go b/x/gov/keeper/config.go
index b066dce7396f..ea01891974a5 100644
--- a/x/gov/keeper/config.go
+++ b/x/gov/keeper/config.go
@@ -3,7 +3,6 @@ package keeper
 import (
 	"context"
 
-	"cosmossdk.io/core/transaction"
 	"cosmossdk.io/math"
 	v1 "cosmossdk.io/x/gov/types/v1"
 )
@@ -34,8 +33,6 @@ type Config struct {
 	// CalculateVoteResultsAndVotingPowerFn is a function signature for calculating vote results and voting power
 	// Keeping it nil will use the default implementation
 	CalculateVoteResultsAndVotingPowerFn CalculateVoteResultsAndVotingPowerFn
-	// ExcludedNestedMsgsForGasSim is a list of messages that will avoid adding the gas of their nested messages when simulating
-	ExcludedNestedMsgsForGasSim []transaction.Msg
 }
 
 // DefaultConfig returns the default config for gov.
@@ -46,6 +43,5 @@ func DefaultConfig() Config {
 		MaxSummaryLen:                        10200,
 		MaxVoteOptionsLen:                    0, // 0 means this param is disabled, hence all supported options are allowed
 		CalculateVoteResultsAndVotingPowerFn: nil,
-		ExcludedNestedMsgsForGasSim:          []transaction.Msg{&v1.MsgSubmitProposal{}},
 	}
 }

From 38854b6baf8c74e2a783faa0931d97e1462e1bc9 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Fri, 26 Jul 2024 10:46:19 +0200
Subject: [PATCH 13/14] UPGRADING

---
 UPGRADING.md | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/UPGRADING.md b/UPGRADING.md
index 887fb2a1e454..57ca7a65a82b 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -5,6 +5,39 @@ Note, always read the **SimApp** section for more information on application wir
 
 ## [Unreleased]
 
+### BaseApp
+
+#### Nested Messages Simulation
+
+Now it is possible to simulate the nested messages of a message, providing developers with a powerful tool for
+testing and predicting the behavior of complex transactions. This feature allows for a more comprehensive
+evaluation of gas consumption, state changes, and potential errors that may occur when executing nested
+messages. However, it's important to note that while the simulation can provide valuable insights, it does not
+guarantee the correct execution of the nested messages in the future. Factors such as changes in the
+blockchain state or updates to the protocol could potentially affect the actual execution of these nested
+messages when the transaction is finally processed on the network.
+
+For example, consider a governance proposal that includes nested messages to update multiple protocol
+parameters. At the time of simulation, the blockchain state may be suitable for executing all these nested
+messages successfully. However, by the time the actual governance proposal is executed (which could be days or
+weeks later), the blockchain state might have changed significantly. As a result, while the simulation showed
+a successful execution, the actual governance proposal might fail when it's finally processed.
+
+By default, when simulating transactions, the gas cost of nested messages is not calculated. This means that
+only the gas cost of the top-level message is considered. However, this behavior can be customized using the
+`SetIncludeNestedMsgsGas` option when building the BaseApp. By providing a list of message types to this option,
+you can specify which messages should have their nested message gas costs included in the simulation. This
+allows for more accurate gas estimation for transactions involving specific message types that contain nested
+messages, while maintaining the default behavior for other message types.
+
+Here is an example on how `SetIncludeNestedMsgsGas` option could be set to calculate the gas of a gov proposal
+nested messages:
+```go
+baseAppOptions = append(baseAppOptions, baseapp.SetIncludeNestedMsgsGas([]sdk.Message{&gov.MsgSubmitProposal{}}))
+// ...
+app.App = appBuilder.Build(db, traceStore, baseAppOptions...)
+```
+
 ### SimApp
 
 In this section we describe the changes made in Cosmos SDK' SimApp.

From 99a166b9b9416988b6350bac30024e5d19a14ff2 Mon Sep 17 00:00:00 2001
From: Julian Toledano <jtoledanodiaz@gmail.com>
Date: Fri, 26 Jul 2024 11:19:16 +0200
Subject: [PATCH 14/14] update: add SetIncludeNestedMsgsGas to app.go

---
 simapp/app.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/simapp/app.go b/simapp/app.go
index d5a0269f8197..f276e5eaeb72 100644
--- a/simapp/app.go
+++ b/simapp/app.go
@@ -66,6 +66,7 @@ import (
 	"cosmossdk.io/x/gov"
 	govkeeper "cosmossdk.io/x/gov/keeper"
 	govtypes "cosmossdk.io/x/gov/types"
+	govv1 "cosmossdk.io/x/gov/types/v1"
 	govv1beta1 "cosmossdk.io/x/gov/types/v1beta1"
 	"cosmossdk.io/x/group"
 	groupkeeper "cosmossdk.io/x/group/keeper"
@@ -246,7 +247,8 @@ func NewSimApp(
 		voteExtHandler := NewVoteExtensionHandler()
 		voteExtHandler.SetHandlers(bApp)
 	}
-	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution())
+	baseAppOptions = append(baseAppOptions, voteExtOp, baseapp.SetOptimisticExecution(),
+		baseapp.SetIncludeNestedMsgsGas([]sdk.Msg{&govv1.MsgSubmitProposal{}}))
 
 	bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...)
 	bApp.SetCommitMultiStoreTracer(traceStore)