# Module migration

In this section, we will be migrating the pofe module from our Launchpad application.

# Module differences

Before proceeding with the migration, it is important to note the differences between Launchpad and Stargate modules. Below is an overview of the differences when scaffolding a module with Starport -

Copy # # removed in Staragte # +++ added in Stargate ├── proto │ └── pofe │ ├── +++ claim.proto │ ├── +++ genesis.proto │ └── +++ query.proto └── x └── pofe ├── client │ ├── cli │ │ ├── query.go │ │ ├── queryClaim.go │ │ ├── tx.go │ │ └── txClaim.go │ └── rest │ ├── queryClaim.go │ ├── rest.go │ └── txClaim.go ├── genesis.go ├── handler.go ├── +++ handler_claim.go ├── # handlerMsgCreateClaim.go ├── # handlerMsgDeleteClaim.go ├── # handlerMsgSetClaim.go ├── keeper │ ├── claim.go │ ├── +++ grpc_query.go │ ├── +++ grpc_query_claim.go │ ├── keeper.go │ ├── # params.go │ ├── # querier.go │ ├── +++ query.go │ └── +++ query_claim.go ├── module.go └── types ├── # MsgCreateClaim.go ├── # MsgDeleteClaim.go ├── # MsgSetClaim.go ├── +++ messages_claim.go ├── # TypeClaim.go ├── +++ claim.pb.go ├── codec.go ├── errors.go ├── # events.go ├── # expected_keepers.go ├── genesis.go ├── +++ genesis.pb.go ├── keys.go ├── # params.go ├── query.go ├── +++ query.pb.go ├── +++ query.pb.gw.go └── types.go

One of the key differences is the integration of gRPC and protobuf in your application. Protobufs are an efficient method for serializing messages and the current industry standard.

# Scaffolding your Stargate module

In theory, you could copy a module over from your existing application and make relevant changes based off this (opens new window) document - if that is your preferred method, of migrating your modules, then you are better off following the aforementioned documentation.

Instead, we will be using Starport to genrate fresh Stargate-compatible files and migrate the logic from our Launchpad application. Note that we will also have to change some of the logic to make it Stargate compatible.

Essentially, we need to inspect the Claim type that is defined in our Launchpad application.

Copy // launchpad/pofe/x/pofe/types/TypeClaim.go package types import ( sdk "github.com/cosmos/cosmos-sdk/types" ) type Claim struct { Creator sdk.AccAddress `json:"creator" yaml:"creator"` ID string `json:"id" yaml:"id"` Proof string `json:"proof" yaml:"proof"` }

As Creator and ID are both default fields for Starport, we can use the starport type command to create our Claim type while defining the proof as a string -

Copy $ starport type claim proof:string 🎉 Created a type `claim`.

Once this is done, you should see a few new files added to your application -

  • proto/pofe/claim.proto
  • x/pofe/{cli|rest}/{queryClaim|txClaim}.go
  • x/pofe/keeper/claim.go
  • x/pofe/keeper/grpc_query_claim.go
  • x/pofe/keeper/query_claim.go
  • x/pofe/keeper/query.go
  • x/pofe/types/claim.pb.go
  • x/pofe/types/messages_claim.go
  • x/pofe/types/query.pb.gw.go
  • x/pofe/handler_claim.go

# Protobuf and gRPC

Compared to your Launchpad application, the key differences in type scaffolding is the addition of proto/pofe/claim.proto, x/pofe/keeper/grpc_query_claim.go, and x/pofe/types/query_pb.go files.

If you take a look inside the claim.proto file, you will see the following file which contains transaction (tx) messages. We can always assume that these transaction messages will mutate state.

Copy // stargate/pofe/proto/pofe/claim.proto syntax = "proto3"; package user.pofe.pofe; option go_package = "github.com/user/pofe/x/pofe/types"; import "gogoproto/gogo.proto"; message Claim { string creator = 1; string id = 2; string proof = 3; } message MsgCreateClaim { string creator = 1; string proof = 2; } message MsgUpdateClaim { string creator = 1; string id = 2; string proof = 3; } message MsgDeleteClaim { string creator = 1; string id = 2; }

You will also see a query.proto file, which contains messages used for querying the state of the blockchain. You will notice that there is a gRPC Query service defined.

Copy // stargate/pofe/proto/pofe/query.proto syntax = "proto3"; package user.pofe.pofe; import "google/api/annotations.proto"; import "cosmos/base/query/v1beta1/pagination.proto"; // this line is used by starport scaffolding # 1 import "pofe/claim.proto"; option go_package = "github.com/user/pofe/x/pofe/types"; // Query defines the gRPC querier service. service Query { // this line is used by starport scaffolding # 2 rpc Claim(QueryGetClaimRequest) returns (QueryGetClaimResponse) { option (google.api.http).get = "/user/pofe/pofe/claim/{id}"; } rpc ClaimAll(QueryAllClaimRequest) returns (QueryAllClaimResponse) { option (google.api.http).get = "/user/pofe/pofe/claim"; } } // this line is used by starport scaffolding # 3 message QueryGetClaimRequest { string id = 1; } message QueryGetClaimResponse { Claim Claim = 1; } message QueryAllClaimRequest { cosmos.base.query.v1beta1.PageRequest pagination = 1; } message QueryAllClaimResponse { repeated Claim Claim = 1; cosmos.base.query.v1beta1.PageResponse pagination = 2; }

If you are unfamiliar with gRPC, a simple explanation is that each gRPC query is registered with a message structure that it receives, and the message structure of the response. For example, QueryGetClaimRequest would require an string id, and would return a Claim, which is defined in the claim.proto file in the same directory.

As a matter of fact, these messages will be used to generate the messages used in the application. You will notice this in x/pofe/types/claim.pb.go - take for example the Claim type. There will also be a few pre-generated helper functions including getters and marshaling methods for the messages.

Copy // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: pofe/claim.proto package types import ( fmt "fmt" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" math "math" math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type Claim struct { Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty"` Id string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` Proof string `protobuf:"bytes,3,opt,name=proof,proto3" json:"proof,omitempty"` } func (m *Claim) Reset() { *m = Claim{} } func (m *Claim) String() string { return proto.CompactTextString(m) } func (*Claim) ProtoMessage() {} func (*Claim) Descriptor() ([]byte, []int) { return fileDescriptor_6792804c00432dd8, []int{0} } func (m *Claim) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } func (m *Claim) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { return xxx_messageInfo_Claim.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 *Claim) XXX_Merge(src proto.Message) { xxx_messageInfo_Claim.Merge(m, src) } func (m *Claim) XXX_Size() int { return m.Size() } func (m *Claim) XXX_DiscardUnknown() { xxx_messageInfo_Claim.DiscardUnknown(m) } var xxx_messageInfo_Claim proto.InternalMessageInfo func (m *Claim) GetCreator() string { if m != nil { return m.Creator } return "" } func (m *Claim) GetId() string { if m != nil { return m.Id } return "" } func (m *Claim) GetProof() string { if m != nil { return m.Proof } return "" } // MsgCreateClaim, MsgUpdateClaim, MsgDeleteClaim

Note that you can also generate these .pb.go files by running ./scripts/protocgen.

The same concept goes for the query methods - so updating the proto/pofe/query.proto file and running the script will update the contents of types/query.pb.go and query.pb.gw.go.

Now, we can continue to migrate our logic to Stargate!

# Migrating your logic

As the core logic that we implemented was within the CLI, we need to be able to modify the contents and adapt them to the Stargate release.

The most important file to note is the modification we made in the launchpad/pofe/x/pofe/client/cli/txClaim.go file:

Copy // launchpad package cli import ( "bufio" "crypto/sha256" "encoding/hex" "io/ioutil" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client/context" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth" "github.com/cosmos/cosmos-sdk/x/auth/client/utils" "github.com/user/pofe/x/pofe/types" ) // CLI transaction command to create a claim func GetCmdCreateClaim(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "create-claim [path-to-file]", Short: "Creates a new claim from a path to a file", Args: cobra.MinimumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { // accept a filepath, read the file, and hash it hasher := sha256.New() s, _ := ioutil.ReadFile(args[0]) hasher.Write(s) argsProof := hex.EncodeToString(hasher.Sum(nil)) // automatically scaffolded by `starport type` cliCtx := context.NewCLIContext().WithCodec(cdc) inBuf := bufio.NewReader(cmd.InOrStdin()) txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) msg := types.NewMsgCreateClaim(cliCtx.GetFromAddress(), string(argsProof)) err := msg.ValidateBasic() if err != nil { return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } } func GetCmdSetClaim(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "set-claim [id] [proof]", Short: "Set a new claim", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { id := args[0] argsProof := string(args[1]) cliCtx := context.NewCLIContext().WithCodec(cdc) inBuf := bufio.NewReader(cmd.InOrStdin()) txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) msg := types.NewMsgSetClaim(cliCtx.GetFromAddress(), id, string(argsProof)) err := msg.ValidateBasic() if err != nil { return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } } func GetCmdDeleteClaim(cdc *codec.Codec) *cobra.Command { return &cobra.Command{ Use: "delete-claim [id]", Short: "Delete a new claim by ID", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { cliCtx := context.NewCLIContext().WithCodec(cdc) inBuf := bufio.NewReader(cmd.InOrStdin()) txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(utils.GetTxEncoder(cdc)) msg := types.NewMsgDeleteClaim(args[0], cliCtx.GetFromAddress()) err := msg.ValidateBasic() if err != nil { return err } return utils.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) }, } }

As well as boilerplate txClaim.go file in our Stargate application:

Copy package cli import ( "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/user/pofe/x/pofe/types" ) func CmdCreateClaim() *cobra.Command { cmd := &cobra.Command{ Use: "create-claim [proof]", Short: "Creates a new claim", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { argsProof := string(args[0]) clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) if err != nil { return err } msg := types.NewMsgCreateClaim(clientCtx.GetFromAddress().String(), string(argsProof)) if err := msg.ValidateBasic(); err != nil { return err } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) return cmd } func CmdUpdateClaim() *cobra.Command { cmd := &cobra.Command{ Use: "update-claim [id] [proof]", Short: "Update a claim", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { id := args[0] argsProof := string(args[1]) clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) if err != nil { return err } msg := types.NewMsgUpdateClaim(clientCtx.GetFromAddress().String(), id, string(argsProof)) if err := msg.ValidateBasic(); err != nil { return err } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) return cmd } func CmdDeleteClaim() *cobra.Command { cmd := &cobra.Command{ Use: "delete-claim [id] [proof]", Short: "Delete a claim by id", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { id := args[0] clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) if err != nil { return err } msg := types.NewMsgDeleteClaim(clientCtx.GetFromAddress().String(), id) if err := msg.ValidateBasic(); err != nil { return err } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) return cmd }

If we compare the two files, we will notice that there is a difference in how the commands are defined. For instance, the Stargate version of the functions no longer require a codec as an argument - eg. func GetCmdXClaim(cdc *codec.Codec) vs func CmdXClaim(). In this case, we can remove cdc *codec.Codec from each of the functions as well as the "github.com/cosmos/cosmos-sdk/codec" import.

Another difference you will notice is how the application reads CLI commands. This includes reading the command, building the message, and Generating or broadcasting the command. For instance, the contents of the GetCmdCreateClaim command will be updated as such when following the updates generated by Starport and described in further detail here (opens new window).

Copy func GetCmdCreateClaim() *cobra.Command { cmd := &cobra.Command{ Use: "create-claim [proof]", Short: "Creates a new claim", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { hasher := sha256.New() s, _ := ioutil.ReadFile(args[0]) hasher.Write(s) argsProof := hex.EncodeToString(hasher.Sum(nil)) clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) if err != nil { return err } msg := types.NewMsgCreateClaim(clientCtx.GetFromAddress().String(), string(argsProof)) if err := msg.ValidateBasic(); err != nil { return err } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) return cmd }

Once we finish updating the rest of our custom logic, our new file stargate/pofe/x/pofe/client/cli/txClaim.go should look as such:

Copy package cli import ( "crypto/sha256" "encoding/hex" "io/ioutil" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/tx" "github.com/user/pofe/x/pofe/types" ) func GetCmdCreateClaim() *cobra.Command { cmd := &cobra.Command{ Use: "create-claim [proof]", Short: "Creates a new claim", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { hasher := sha256.New() s, _ := ioutil.ReadFile(args[0]) hasher.Write(s) argsProof := hex.EncodeToString(hasher.Sum(nil)) clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) if err != nil { return err } msg := types.NewMsgCreateClaim(clientCtx.GetFromAddress().String(), string(argsProof)) if err := msg.ValidateBasic(); err != nil { return err } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) return cmd } func GetCmdUpdateClaim() *cobra.Command { cmd := &cobra.Command{ Use: "update-claim [id] [proof]", Short: "Update a claim", Args: cobra.ExactArgs(2), RunE: func(cmd *cobra.Command, args []string) error { id := args[0] argsProof := string(args[1]) clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) if err != nil { return err } msg := types.NewMsgUpdateClaim(clientCtx.GetFromAddress().String(), id, string(argsProof)) if err := msg.ValidateBasic(); err != nil { return err } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) return cmd } func GetCmdDeleteClaim() *cobra.Command { cmd := &cobra.Command{ Use: "delete-claim [id] [proof]", Short: "Delete a claim by id", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { id := args[0] clientCtx := client.GetClientContextFromCmd(cmd) clientCtx, err := client.ReadTxCommandFlags(clientCtx, cmd.Flags()) if err != nil { return err } msg := types.NewMsgDeleteClaim(clientCtx.GetFromAddress().String(), id) if err := msg.ValidateBasic(); err != nil { return err } return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, } flags.AddTxFlagsToCmd(cmd) return cmd }

The last thing we need to do is rename the CLI commands in the stargate/pofe/x/pofe/client/cli/tx.go file:

Copy package cli import ( "fmt" "github.com/spf13/cobra" "github.com/cosmos/cosmos-sdk/client" // "github.com/cosmos/cosmos-sdk/client/flags" "github.com/user/pofe/x/pofe/types" ) // GetTxCmd returns the transaction commands for this module func GetTxCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, } // this line is used by starport scaffolding # 1 cmd.AddCommand(GetCmdCreateClaim()) cmd.AddCommand(GetCmdUpdateClaim()) cmd.AddCommand(GetCmdDeleteClaim()) return cmd }

Once this has been updated, we can try to run the app with starport serve, and our application should run as expected!

Copy $ pofed tx pofe create-claim $(which pofed) --from=user1 {"body":{"messages":[{"@type":"/lukitsbrian.pofe.pofe.MsgCreateClaim","creator":"cosmos1et76j637haxpp4kzddah6l6cfxn9ytcqsrltj0","proof":"d301c4933c688d1d890e7b44fe14df91e4bf14719c8a1fd720445366b060507d"}],"memo":"","timeout_height":"0","extension_options":[],"non_critical_extension_options":[]},"auth_info":{"signer_infos":[],"fee":{"amount":[],"gas_limit":"200000","payer":"","granter":""}},"signatures":[]} confirm transaction before signing and broadcasting [y/N]: y {"height":"199","txhash":"536258DEFA8A4C46FC8ABA75684C967D61BABBB467058F8381B8AB5A3400A9F3","codespace":"","code":0,"data":"0A0D0A0B437265617465436C61696D","raw_log":"[{\"events\":[{\"type\":\"message\",\"attributes\":[{\"key\":\"action\",\"value\":\"CreateClaim\"}]}]}]","logs":[{"msg_index":0,"log":"","events":[{"type":"message","attributes":[{"key":"action","value":"CreateClaim"}]}]}],"info":"","gas_wanted":"200000","gas_used":"47011","tx":null,"timestamp":""}

# Migrating your own application

Throughout this tutorial, we have shown you how to migrate your application from Launchpad to Stargate with the help of Starport, which will always generate the most up-to-date Cosmos SDK boilerplate code. One might be expectd to migrate their application manually by following the documentation, which is still a viable options, albeit less efficient.

The approach we used in this tutorial minimizes the changes we had to make when migrating the application - we only had to migrate the custom logic that was implemented, rather than modifying each individual file that needed to be updated. Having boilerplate code also allows us to reference it as an example when examining the fundamental changes within the application itself when making the necessary changes.