Skip to main content

v0.25.0

Protobuf directory migration

v0.25.0 changes the location of scaffolded .proto files. Previously, .proto files were located in ./proto/{moduleName}/, where moduleName is the same name of the Cosmos SDK module found in ./x/{moduleName}/. This new version of ignite modifies the scaffolded protobuf files so that they are now generated in ./proto/{appName}/{moduleName}.

The only change that is needed to be made is to create an {appName} folder in the proto directory, and then place the sub-directories within it. An example below demonstrates this change:

Previous Directory Structure

This example shows a chain that was generated using ignite with v0.24.0 using the following command:

ignite s chain github.com/cosmos/planet --no-module
ignite s module mars
├── app  
├── cmd
├── docs
├── proto
│ ├── mars
├── x
│ ├── mars
├── README.md
├── config.yml
├── go.mod
├── go.sum
└── .gitignore

v0.25.0 Directory Structure

This example shows a chain that was generated using ignite with v0.25.0 using the following command:

ignite s chain github.com/cosmos/planet --no-module
ignite s module mars
├── app  
├── cmd
├── docs
├── proto
│ ├── planet
│ │ ├── mars
├── x
│ ├── mars
├── README.md
├── config.yml
├── go.mod
├── go.sum
└── .gitignore

The only difference is the additional directory planet which is the name of the application. The name of the app can be verified by checking the package in the go.mod file. In this example, the package is github.com/cosmos/planet where planet is the app name.


Removing cosmoscmd

v0.25.0 removes the cosmoscmd package from scaffolded chains. This package provided utility for creating commands and starting up their application. The cosmoscmd package is now deprecated, and it is suggested that chains implement this functionality in their codebase so they can be more easily upgraded and customized.

The main functionality of cosmoscmd will be moved to the app package of your chain. Some imports in these examples contain the sample string, {ModulePath}. Replace this string with the Go module path of your blockchain. For example, if your blockchain module path is github.com/planet/mars, {ModulePath}/app/params would be become github.com/planet/mars/app/params.

Migration in app package

To begin, create a new file, ./app/params/encoding.go, containing the following code:

package params

import (
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
)

// EncodingConfig specifies the concrete encoding types to use for a given app.
// This is provided for compatibility between protobuf and amino implementations.
type EncodingConfig struct {
InterfaceRegistry types.InterfaceRegistry
Marshaler codec.Codec
TxConfig client.TxConfig
Amino *codec.LegacyAmino
}

Next, create a new file, ./app/encoding.go, containing the following code:

package app

import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/std"
"github.com/cosmos/cosmos-sdk/x/auth/tx"

"{ModulePath}/app/params"
)

// makeEncodingConfig creates an EncodingConfig for an amino based test configuration.
func makeEncodingConfig() params.EncodingConfig {
amino := codec.NewLegacyAmino()
interfaceRegistry := types.NewInterfaceRegistry()
marshaler := codec.NewProtoCodec(interfaceRegistry)
txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes)

return params.EncodingConfig{
InterfaceRegistry: interfaceRegistry,
Marshaler: marshaler,
TxConfig: txCfg,
Amino: amino,
}
}

// MakeEncodingConfig creates an EncodingConfig for testing
func MakeEncodingConfig() params.EncodingConfig {
encodingConfig := makeEncodingConfig()
std.RegisterLegacyAminoCodec(encodingConfig.Amino)
std.RegisterInterfaces(encodingConfig.InterfaceRegistry)
ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino)
ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry)
return encodingConfig
}

Next, modify ./app/simulation_test.go so that it looks like the following:

package app_test

import (
"os"
"testing"
"time"

"github.com/cosmos/cosmos-sdk/simapp"
simulationtypes "github.com/cosmos/cosmos-sdk/types/simulation"
"github.com/cosmos/cosmos-sdk/x/simulation"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
tmtypes "github.com/tendermint/tendermint/types"
"github.com/ignite/cli/ignite/pkg/cosmoscmd"

"{ModulePath}/app"
)

type SimApp interface {
cosmoscmd.App
GetBaseApp() *baseapp.BaseApp
AppCodec() codec.Codec
SimulationManager() *module.SimulationManager
ModuleAccountAddrs() map[string]bool
Name() string
LegacyAmino() *codec.LegacyAmino
BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock)
abci.ResponseBeginBlock
EndBlocker(ctx sdk.Context, req abci.RequestEndBlock)
abci.ResponseEndBlock
InitChainer(ctx sdk.Context, req abci.RequestInitChain)
abci.ResponseInitChain
}

// ...

// BenchmarkSimulation run the chain simulation
// Running using starport command:
// `starport chain simulate -v --numBlocks 200 --blockSize 50`
// Running as go benchmark test:
// `go test -benchmem -run=^$ -bench ^BenchmarkSimulation ./app -NumBlocks=200 -BlockSize 50 -Commit=true -Verbose=true -Enabled=true`
func BenchmarkSimulation(b *testing.B) {

// ...

encoding := cosmoscmd.MakeEncodingConfig(app.ModuleBasics)
encoding := app.MakeEncodingConfig()

app := app.New(
logger,
db,
nil,
true,
map[int64]bool{},
app.DefaultNodeHome,
0,
encoding,
simapp.EmptyAppOptions{},
)

simApp, ok := app.(SimApp)
require.True(b, ok, "can't use simapp")

// Run randomized simulations
_, simParams, simErr := simulation.SimulateFromSeed(
b,
os.Stdout,
app.BaseApp,
simapp.AppStateFn(app.AppCodec(), app.SimulationManager()),
simulationtypes.RandomAccounts,
simapp.SimulationOperations(app, app.AppCodec(), config),
app.ModuleAccountAddrs(),
config,
app.AppCodec(),
)

// export state and simParams before the simulation error is checked
err = simapp.CheckExportSimulation(app, config, simParams)
require.NoError(b, err)
require.NoError(b, simErr)

// ...
}

The main changes here are that the SimApp interface has been removed and is being replaced with app.

The final modification in the app package is in app/app.go:

package app

import (
// ...

// this line is used by starport scaffolding # stargate/app/moduleImport

"github.com/ignite/cli/ignite/pkg/cosmoscmd"

appparams "{ModulePath}/app/params"
"{ModulePath}/docs"
)

// ...

var (
_ cosmoscmd.App = (*App)(nil)
_ servertypes.Application = (*App)(nil)
_ simapp.App = (*App)(nil)
)

// ...

// New returns a reference to an initialized blockchain app
func New(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
loadLatest bool,
skipUpgradeHeights map[int64]bool,
homePath string,
invCheckPeriod uint,
encodingConfig appparams.EncodingConfig,
appOpts servertypes.AppOptions,
baseAppOptions ...func(*baseapp.BaseApp),
) *App {
appCodec := encodingConfig.Marshaler
cdc := encodingConfig.Amino
interfaceRegistry := encodingConfig.InterfaceRegistry

bApp := baseapp.NewBaseApp(
Name,
logger,
db,
encodingConfig.TxConfig.TxDecoder(),
baseAppOptions...,
)

// ...

}

// ...

// Name returns the name of the App
func (app *App) Name() string { return app.BaseApp.Name() }

// GetBaseApp returns the base app of the application
func (app App) GetBaseApp() *baseapp.BaseApp { return app.BaseApp }

// BeginBlocker application updates every begin block
func (app *App) BeginBlocker(ctx sdk.Context, req abci.RequestBeginBlock) abci.ResponseBeginBlock {
return app.mm.BeginBlock(ctx, req)
}

// ...

Again, here we are removing the use of cosmoscmd and replacing it with app.

Migration in cmd package

Some imports in these examples contain the sample string, {binaryNamePrefix}d. Replace this string with the binary name of your blockchain. For example, if your blockchain module path is github.com/planet/mars, ./cmd/{binaryNamePrefix}d/cmd/ would be become ./cmd/marsd/cmd/.

First, create the new file ./cmd/{binaryNamePrefix}d/cmd/config.go with the following code:

package cmd

import (
sdk "github.com/cosmos/cosmos-sdk/types"

"{ModulePath}/app"
)

func initSDKConfig() {
// Set prefixes
accountPubKeyPrefix := app.AccountAddressPrefix + "pub"
validatorAddressPrefix := app.AccountAddressPrefix + "valoper"
validatorPubKeyPrefix := app.AccountAddressPrefix + "valoperpub"
consNodeAddressPrefix := app.AccountAddressPrefix + "valcons"
consNodePubKeyPrefix := app.AccountAddressPrefix + "valconspub"

// Set and seal config
config := sdk.GetConfig()
config.SetBech32PrefixForAccount(app.AccountAddressPrefix, accountPubKeyPrefix)
config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix)
config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix)
config.Seal()
}

Next, create the new file ./cmd/{binaryNamePrefix}d/cmd/genaccounts.go with the following code:

package cmd

import (
"bufio"
"encoding/json"
"errors"
"fmt"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
authvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/genutil"
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
"github.com/spf13/cobra"
)

const (
flagVestingStart = "vesting-start-time"
flagVestingEnd = "vesting-end-time"
flagVestingAmt = "vesting-amount"
)

// AddGenesisAccountCmd returns add-genesis-account cobra Command.
func AddGenesisAccountCmd(defaultNodeHome string) *cobra.Command {
cmd := &cobra.Command{
Use: "add-genesis-account [address_or_key_name] [coin][,[coin]]",
Short: "Add a genesis account to genesis.json",
Long: `Add a genesis account to genesis.json. The provided account must specify
the account address or key name and a list of initial coins. If a key name is given,
the address will be looked up in the local Keybase. The list of initial tokens must
contain valid denominations. Accounts may optionally be supplied with vesting parameters.
`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx := client.GetClientContextFromCmd(cmd)
cdc := clientCtx.Codec

serverCtx := server.GetServerContextFromCmd(cmd)
config := serverCtx.Config

config.SetRoot(clientCtx.HomeDir)

coins, err := sdk.ParseCoinsNormalized(args[1])
if err != nil {
return fmt.Errorf("failed to parse coins: %w", err)
}

addr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
inBuf := bufio.NewReader(cmd.InOrStdin())
keyringBackend, err := cmd.Flags().GetString(flags.FlagKeyringBackend)
if err != nil {
return err
}

// attempt to lookup address from Keybase if no address was provided
kb, err := keyring.New(sdk.KeyringServiceName(), keyringBackend, clientCtx.HomeDir, inBuf, cdc)
if err != nil {
return err
}

info, err := kb.Key(args[0])
if err != nil {
return fmt.Errorf("failed to get address from Keybase: %w", err)
}

addr, err = info.GetAddress()
if err != nil {
return fmt.Errorf("failed to get address from Keybase: %w", err)
}
}

vestingStart, err := cmd.Flags().GetInt64(flagVestingStart)
if err != nil {
return err
}
vestingEnd, err := cmd.Flags().GetInt64(flagVestingEnd)
if err != nil {
return err
}
vestingAmtStr, err := cmd.Flags().GetString(flagVestingAmt)
if err != nil {
return err
}

vestingAmt, err := sdk.ParseCoinsNormalized(vestingAmtStr)
if err != nil {
return fmt.Errorf("failed to parse vesting amount: %w", err)
}

// create concrete account type based on input parameters
var genAccount authtypes.GenesisAccount

balances := banktypes.Balance{Address: addr.String(), Coins: coins.Sort()}
baseAccount := authtypes.NewBaseAccount(addr, nil, 0, 0)

if !vestingAmt.IsZero() {
baseVestingAccount := authvesting.NewBaseVestingAccount(baseAccount, vestingAmt.Sort(), vestingEnd)

if (balances.Coins.IsZero() && !baseVestingAccount.OriginalVesting.IsZero()) ||
baseVestingAccount.OriginalVesting.IsAnyGT(balances.Coins) {
return errors.New("vesting amount cannot be greater than total amount")
}

switch {
case vestingStart != 0 && vestingEnd != 0:
genAccount = authvesting.NewContinuousVestingAccountRaw(baseVestingAccount, vestingStart)

case vestingEnd != 0:
genAccount = authvesting.NewDelayedVestingAccountRaw(baseVestingAccount)

default:
return errors.New("invalid vesting parameters; must supply start and end time or end time")
}
} else {
genAccount = baseAccount
}

if err := genAccount.Validate(); err != nil {
return fmt.Errorf("failed to validate new genesis account: %w", err)
}

genFile := config.GenesisFile()
appState, genDoc, err := genutiltypes.GenesisStateFromGenFile(genFile)
if err != nil {
return fmt.Errorf("failed to unmarshal genesis state: %w", err)
}

authGenState := authtypes.GetGenesisStateFromAppState(cdc, appState)

accs, err := authtypes.UnpackAccounts(authGenState.Accounts)
if err != nil {
return fmt.Errorf("failed to get accounts from any: %w", err)
}

if accs.Contains(addr) {
return fmt.Errorf("cannot add account at existing address %s", addr)
}

// Add the new account to the set of genesis accounts and sanitize the
// accounts afterwards.
accs = append(accs, genAccount)
accs = authtypes.SanitizeGenesisAccounts(accs)

genAccs, err := authtypes.PackAccounts(accs)
if err != nil {
return fmt.Errorf("failed to convert accounts into any's: %w", err)
}
authGenState.Accounts = genAccs

authGenStateBz, err := cdc.MarshalJSON(&authGenState)
if err != nil {
return fmt.Errorf("failed to marshal auth genesis state: %w", err)
}

appState[authtypes.ModuleName] = authGenStateBz

bankGenState := banktypes.GetGenesisStateFromAppState(cdc, appState)
bankGenState.Balances = append(bankGenState.Balances, balances)
bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances)

bankGenStateBz, err := cdc.MarshalJSON(bankGenState)
if err != nil {
return fmt.Errorf("failed to marshal bank genesis state: %w", err)
}

appState[banktypes.ModuleName] = bankGenStateBz

appStateJSON, err := json.Marshal(appState)
if err != nil {
return fmt.Errorf("failed to marshal application genesis state: %w", err)
}

genDoc.AppState = appStateJSON
return genutil.ExportGenesisFile(genDoc, genFile)
},
}

cmd.Flags().String(flags.FlagKeyringBackend, flags.DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)")
cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
cmd.Flags().String(flagVestingAmt, "", "amount of coins for vesting accounts")
cmd.Flags().Int64(flagVestingStart, 0, "schedule start time (unix epoch) for vesting accounts")
cmd.Flags().Int64(flagVestingEnd, 0, "schedule end time (unix epoch) for vesting accounts")
flags.AddQueryFlagsToCmd(cmd)

return cmd
}

This command allows one to generate new accounts: appd add-genesis-account.

Next, create the new file ./cmd/{binaryNamePrefix}d/cmd/root.go with the following code:

package cmd

import (
"errors"
"io"
"os"
"path/filepath"
"strings"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/config"
"github.com/cosmos/cosmos-sdk/client/debug"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/server"
serverconfig "github.com/cosmos/cosmos-sdk/server/config"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/snapshots"
snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
"github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/cosmos/cosmos-sdk/x/crisis"
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
"github.com/ignite/cli/ignite/services/network"
"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
tmcfg "github.com/tendermint/tendermint/config"
tmcli "github.com/tendermint/tendermint/libs/cli"
"github.com/tendermint/tendermint/libs/log"
dbm "github.com/tendermint/tm-db"
// this line is used by starport scaffolding # root/moduleImport

"{ModulePath}/app"
appparams "{ModulePath}/app/params"
)

// NewRootCmd creates a new root command for a Cosmos SDK application
func NewRootCmd() (*cobra.Command, appparams.EncodingConfig) {
encodingConfig := app.MakeEncodingConfig()
initClientCtx := client.Context{}.
WithCodec(encodingConfig.Marshaler).
WithInterfaceRegistry(encodingConfig.InterfaceRegistry).
WithTxConfig(encodingConfig.TxConfig).
WithLegacyAmino(encodingConfig.Amino).
WithInput(os.Stdin).
WithAccountRetriever(types.AccountRetriever{}).
WithHomeDir(app.DefaultNodeHome).
WithViper("")

rootCmd := &cobra.Command{
Use: app.Name + "d",
Short: "Stargate CosmosHub App",
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
// set the default command outputs
cmd.SetOut(cmd.OutOrStdout())
cmd.SetErr(cmd.ErrOrStderr())
initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags())
if err != nil {
return err
}
initClientCtx, err = config.ReadFromClientConfig(initClientCtx)
if err != nil {
return err
}

if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil {
return err
}

customAppTemplate, customAppConfig := initAppConfig()
customTMConfig := initTendermintConfig()
return server.InterceptConfigsPreRunHandler(
cmd, customAppTemplate, customAppConfig, customTMConfig,
)
},
}

initRootCmd(rootCmd, encodingConfig)
overwriteFlagDefaults(rootCmd, map[string]string{
flags.FlagChainID: strings.ReplaceAll(app.Name, "-", ""),
flags.FlagKeyringBackend: "test",
})

return rootCmd, encodingConfig
}

// initTendermintConfig helps to override default Tendermint Config values.
// return tmcfg.DefaultConfig if no custom configuration is required for the application.
func initTendermintConfig() *tmcfg.Config {
cfg := tmcfg.DefaultConfig()
return cfg
}

func initRootCmd(
rootCmd *cobra.Command,
encodingConfig appparams.EncodingConfig,
) {
// Set config
initSDKConfig()

rootCmd.AddCommand(
genutilcli.InitCmd(app.ModuleBasics, app.DefaultNodeHome),
genutilcli.CollectGenTxsCmd(banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome),
genutilcli.MigrateGenesisCmd(),
genutilcli.GenTxCmd(
app.ModuleBasics,
encodingConfig.TxConfig,
banktypes.GenesisBalancesIterator{},
app.DefaultNodeHome,
),
genutilcli.ValidateGenesisCmd(app.ModuleBasics),
AddGenesisAccountCmd(app.DefaultNodeHome),
tmcli.NewCompletionCmd(rootCmd, true),
debug.Cmd(),
config.Cmd(),
// this line is used by starport scaffolding # root/commands
)

a := appCreator{
encodingConfig,
}

// add server commands
server.AddCommands(
rootCmd,
app.DefaultNodeHome,
a.newApp,
a.appExport,
addModuleInitFlags,
)

// add keybase, auxiliary RPC, query, and tx child commands
rootCmd.AddCommand(
rpc.StatusCommand(),
queryCommand(),
txCommand(),
keys.Commands(app.DefaultNodeHome),
)
}

// queryCommand returns the sub-command to send queries to the app
func queryCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "query",
Aliases: []string{"q"},
Short: "Querying subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}

cmd.AddCommand(
authcmd.GetAccountCmd(),
rpc.ValidatorCommand(),
rpc.BlockCommand(),
authcmd.QueryTxsByEventsCmd(),
authcmd.QueryTxCmd(),
)

app.ModuleBasics.AddQueryCommands(cmd)
cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID")

return cmd
}

// txCommand returns the sub-command to send transactions to the app
func txCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "tx",
Short: "Transactions subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}

cmd.AddCommand(
authcmd.GetSignCommand(),
authcmd.GetSignBatchCommand(),
authcmd.GetMultiSignCommand(),
authcmd.GetValidateSignaturesCommand(),
flags.LineBreak,
authcmd.GetBroadcastCommand(),
authcmd.GetEncodeCommand(),
authcmd.GetDecodeCommand(),
)

app.ModuleBasics.AddTxCommands(cmd)
cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID")

return cmd
}

func addModuleInitFlags(startCmd *cobra.Command) {
crisis.AddModuleInitFlags(startCmd)
// this line is used by starport scaffolding # root/arguments
}

func overwriteFlagDefaults(c *cobra.Command, defaults map[string]string) {
set := func(s *pflag.FlagSet, key, val string) {
if f := s.Lookup(key); f != nil {
f.DefValue = val
f.Value.Set(val)
}
}
for key, val := range defaults {
set(c.Flags(), key, val)
set(c.PersistentFlags(), key, val)
}
for _, c := range c.Commands() {
overwriteFlagDefaults(c, defaults)
}
}

type appCreator struct {
encodingConfig appparams.EncodingConfig
}

// newApp creates a new Cosmos SDK app
func (a appCreator) newApp(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
appOpts servertypes.AppOptions,
) servertypes.Application {
var cache sdk.MultiStorePersistentCache

if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) {
cache = store.NewCommitKVStoreCacheManager()
}

skipUpgradeHeights := make(map[int64]bool)
for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) {
skipUpgradeHeights[int64(h)] = true
}

pruningOpts, err := server.GetPruningOptionsFromFlags(appOpts)
if err != nil {
panic(err)
}

snapshotDir := filepath.Join(cast.ToString(appOpts.Get(flags.FlagHome)), "data", "snapshots")
snapshotDB, err := dbm.NewDB("metadata", dbm.GoLevelDBBackend, snapshotDir)
if err != nil {
panic(err)
}
snapshotStore, err := snapshots.NewStore(snapshotDB, snapshotDir)
if err != nil {
panic(err)
}

snapshotOptions := snapshottypes.NewSnapshotOptions(
cast.ToUint64(appOpts.Get(server.FlagStateSyncSnapshotInterval)),
cast.ToUint32(appOpts.Get(server.FlagStateSyncSnapshotKeepRecent)),
)

return app.New(
logger,
db,
traceStore,
true,
skipUpgradeHeights,
cast.ToString(appOpts.Get(flags.FlagHome)),
cast.ToUint(appOpts.Get(server.FlagInvCheckPeriod)),
a.encodingConfig,
appOpts,
baseapp.SetPruning(pruningOpts),
baseapp.SetMinGasPrices(cast.ToString(appOpts.Get(server.FlagMinGasPrices))),
baseapp.SetMinRetainBlocks(cast.ToUint64(appOpts.Get(server.FlagMinRetainBlocks))),
baseapp.SetHaltHeight(cast.ToUint64(appOpts.Get(server.FlagHaltHeight))),
baseapp.SetHaltTime(cast.ToUint64(appOpts.Get(server.FlagHaltTime))),
baseapp.SetInterBlockCache(cache),
baseapp.SetTrace(cast.ToBool(appOpts.Get(server.FlagTrace))),
baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))),
baseapp.SetSnapshot(snapshotStore, snapshotOptions),
)
}

// appExport creates a new simapp (optionally at a given height)
func (a appCreator) appExport(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
height int64,
forZeroHeight bool,
jailAllowedAddrs []string,
appOpts servertypes.AppOptions,
) (servertypes.ExportedApp, error) {
homePath, ok := appOpts.Get(flags.FlagHome).(string)
if !ok || homePath == "" {
return servertypes.ExportedApp{}, errors.New("application home not set")
}

app := app.New(
logger,
db,
traceStore,
height == -1, // -1: no height provided
map[int64]bool{},
homePath,
uint(1),
a.encodingConfig,
appOpts,
)

if height != -1 {
if err := app.LoadHeight(height); err != nil {
return servertypes.ExportedApp{}, err
}
}

return app.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs)
}

// initAppConfig helps to override default appConfig template and configs.
// return "", nil if no custom configuration is required for the application.
func initAppConfig() (string, interface{}) {
// The following code snippet is just for reference.

// WASMConfig defines configuration for the wasm module.
type WASMConfig struct {
// This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries
QueryGasLimit uint64 `mapstructure:"query_gas_limit"`

// Address defines the gRPC-web server to listen on
LruSize uint64 `mapstructure:"lru_size"`
}

type CustomAppConfig struct {
serverconfig.Config

WASM WASMConfig `mapstructure:"wasm"`
}

// Optionally allow the chain developer to overwrite the SDK's default
// server config.
srvCfg := serverconfig.DefaultConfig()
// The SDK's default minimum gas price is set to "" (empty value) inside
// app.toml. If left empty by validators, the node will halt on startup.
// However, the chain developer can set a default app.toml value for their
// validators here.
//
// In summary:
// - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their
// own app.toml config,
// - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their
// own app.toml to override, or use this default value.
//
// In simapp, we set the min gas prices to 0.
srvCfg.MinGasPrices = "0stake"

customAppConfig := CustomAppConfig{
Config: *srvCfg,
WASM: WASMConfig{
LruSize: 1,
QueryGasLimit: 300000,
},
}

customAppTemplate := serverconfig.DefaultConfigTemplate + `
[wasm]
# This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries
query_gas_limit = 300000
# This is the number of wasm vm instances we keep cached in memory for speed-up
# Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally
lru_size = 0`

return customAppTemplate, customAppConfig
}

Finally, modify ./cmd/{binaryNamePrefix}d/main.go to include the new changes:

package main

import (
"os"

"github.com/cosmos/cosmos-sdk/server"
svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
"github.com/ignite/cli/ignite/pkg/cosmoscmd"

"{ModulePath}/app"
"{ModulePath}/cmd/{BinaryNamePrefix}d/cmd"
)

func main() {
rootCmd, _ := cmd.NewRootCmd()
if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
switch e := err.(type) {
case server.ErrorCode:
os.Exit(e.Code)

default:
os.Exit(1)
}
}
}

Migration in testutil package

Modify ./testutil/network/network.go to include the new changes:

package network

import (
"fmt"
"testing"
"time"

"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
pruningtypes "github.com/cosmos/cosmos-sdk/pruning/types"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/network"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/stretchr/testify/require"
tmrand "github.com/tendermint/tendermint/libs/rand"
tmdb "github.com/tendermint/tm-db"

"{ModulePath}/app"

"github.com/ignite/cli/ignite/pkg/cosmoscmd"
)

// ...

// DefaultConfig will initialize config for the network with custom application,
// genesis and single validator. All other parameters are inherited from cosmos-sdk/testutil/network.DefaultConfig
func DefaultConfig() network.Config {
encoding := app.MakeEncodingConfig()
encoding := cosmoscmd.MakeEncodingConfig(app.ModuleBasics)
return network.Config{
Codec: encoding.Marshaler,
TxConfig: encoding.TxConfig,
LegacyAmino: encoding.Amino,
InterfaceRegistry: encoding.InterfaceRegistry,
AccountRetriever: authtypes.AccountRetriever{},
AppConstructor: func(val network.Validator) servertypes.Application {
return app.New(
val.Ctx.Logger, tmdb.NewMemDB(), nil, true, map[int64]bool{}, val.Ctx.Config.RootDir, 0,
encoding,
simapp.EmptyAppOptions{},
baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.AppConfig.Pruning)),
baseapp.SetMinGasPrices(val.AppConfig.MinGasPrices),
)
},
GenesisState: app.ModuleBasics.DefaultGenesis(encoding.Marshaler),
TimeoutCommit: 2 * time.Second,
ChainID: "chain-" + tmrand.NewRand().Str(6),
NumValidators: 1,
BondDenom: sdk.DefaultBondDenom,
MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction),
StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction),
BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction),
PruningStrategy: pruningtypes.PruningOptionNothing,
CleanupDir: true,
SigningAlgo: string(hd.Secp256k1Type),
KeyringOptions: []keyring.Option{},
}
}

Fix ICA controller keeper wiring

Related issue: https://github.com/ignite/cli/issues/2867

Apply the following changes to app/app.go file :

package app

import (

icacontrollerkeeper "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/keeper"
icacontrollertypes "github.com/cosmos/ibc-go/v5/modules/apps/27-interchain-accounts/controller/types"
// ...
)

// New returns a reference to an initialized blockchain app
func New(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
loadLatest bool,
skipUpgradeHeights map[int64]bool,
homePath string,
invCheckPeriod uint,
encodingConfig appparams.EncodingConfig,
appOpts servertypes.AppOptions,
baseAppOptions ...func(*baseapp.BaseApp),
) *App {

// ...

keys := sdk.NewKVStoreKeys(
authtypes.StoreKey, authz.ModuleName, banktypes.StoreKey,
stakingtypes.StoreKey,
minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey,
govtypes.StoreKey,
paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey,
feegrant.StoreKey, evidencetypes.StoreKey,
ibctransfertypes.StoreKey, icahosttypes.StoreKey,
capabilitytypes.StoreKey, group.StoreKey,
icacontrollertypes.StoreKey,
yourchainmoduletypes.StoreKey,
// this line is used by starport scaffolding # stargate/app/storeKey
)

// ...

icaModule := ica.NewAppModule(nil, &app.ICAHostKeeper)
icaControllerKeeper := icacontrollerkeeper.NewKeeper(
appCodec, keys[icacontrollertypes.StoreKey],
app.GetSubspace(icacontrollertypes.SubModuleName),
app.IBCKeeper.ChannelKeeper, // may be replaced with middleware such as ics29 fee
app.IBCKeeper.ChannelKeeper, &app.IBCKeeper.PortKeeper,
scopedICAControllerKeeper, app.MsgServiceRouter(),
)
icaModule := ica.NewAppModule(&icaControllerKeeper, &app.ICAHostKeeper)
icaHostIBCModule := icahost.NewIBCModule(app.ICAHostKeeper)

// ...
}

// ...

// initParamsKeeper init params keeper and its subspaces
func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino, key, tkey storetypes.StoreKey) paramskeeper.Keeper {
paramsKeeper := paramskeeper.NewKeeper(appCodec, legacyAmino, key, tkey)

paramsKeeper.Subspace(authtypes.ModuleName)
paramsKeeper.Subspace(banktypes.ModuleName)
paramsKeeper.Subspace(stakingtypes.ModuleName)
paramsKeeper.Subspace(minttypes.ModuleName)
paramsKeeper.Subspace(distrtypes.ModuleName)
paramsKeeper.Subspace(slashingtypes.ModuleName)
paramsKeeper.Subspace(govtypes.ModuleName).WithKeyTable(govv1.ParamKeyTable())
paramsKeeper.Subspace(crisistypes.ModuleName)
paramsKeeper.Subspace(ibctransfertypes.ModuleName)
paramsKeeper.Subspace(ibchost.ModuleName)
paramsKeeper.Subspace(icacontrollertypes.SubModuleName)
paramsKeeper.Subspace(icahosttypes.SubModuleName)
paramsKeeper.Subspace(mychainmoduletypes.ModuleName)
// this line is used by starport scaffolding # stargate/app/paramSubspace

return paramsKeeper
}

Fix capability keeper not sealed

Related issue: https://github.com/ignite/cli/issues/1921

Apply the following change to app/app.go file :

package app

// New returns a reference to an initialized blockchain app
func New(
logger log.Logger,
db dbm.DB,
traceStore io.Writer,
loadLatest bool,
skipUpgradeHeights map[int64]bool,
homePath string,
invCheckPeriod uint,
encodingConfig appparams.EncodingConfig,
appOpts servertypes.AppOptions,
baseAppOptions ...func(*baseapp.BaseApp),
) *App {

// ...

// this line is used by starport scaffolding # stargate/app/keeperDefinition

// Sealing prevents other modules from creating scoped sub-keepers
app.CapabilityKeeper.Seal()

// Create static IBC router, add transfer route, then set and seal it

// ...
}