Parcourir la source

Add tokenfactory module to wormchain (#3146)

* Add tokenfactory module to wormchain

* Remove SetMetadata, ForceTransfer, and BurnFrom capabilities. Fix
IsCapabilityEnabled() bug. Comment out test cases that use those
capabilities. Remove unnecessary var declaration.

* Move test_helpers.go to apptesting folder. Remove unnecessary
EncodingConfig struct/method/file. Move and rename mock.go to
mock_key.go and move to apptesting folder.

* Fix lint issues

* Add tokenfactory proto files

* Update the go package for wormchain, add tokenfactory to proto gen
dockerfile, and re-generate proto files

* Disable tokenfactory wasm custom querier so that wormchain custom
querier works

* Remove subdemon check since the bug has been fixed in our ibc-go version

* Disable unit test using tokenfactory custom querier
Steve il y a 2 ans
Parent
commit
5255e933d6
73 fichiers modifiés avec 13075 ajouts et 5 suppressions
  1. 1 0
      wormchain/Dockerfile.proto
  2. 42 4
      wormchain/app/app.go
  3. 17 0
      wormchain/app/apptesting/events.go
  4. 50 0
      wormchain/app/apptesting/mock_key.go
  5. 248 0
      wormchain/app/apptesting/test_helpers.go
  6. 295 0
      wormchain/app/apptesting/test_suite.go
  7. 16 0
      wormchain/app/params/const.go
  8. 14 0
      wormchain/app/params/weights.go
  9. 1 1
      wormchain/go.mod
  10. 17 0
      wormchain/proto/osmosis/tokenfactory/v1beta1/authorityMetadata.proto
  11. 32 0
      wormchain/proto/osmosis/tokenfactory/v1beta1/genesis.proto
  12. 26 0
      wormchain/proto/osmosis/tokenfactory/v1beta1/params.proto
  13. 71 0
      wormchain/proto/osmosis/tokenfactory/v1beta1/query.proto
  14. 109 0
      wormchain/proto/osmosis/tokenfactory/v1beta1/tx.proto
  15. 155 0
      wormchain/x/tokenfactory/README.md
  16. 334 0
      wormchain/x/tokenfactory/bindings/custom_msg_test.go
  17. 74 0
      wormchain/x/tokenfactory/bindings/custom_query_test.go
  18. 93 0
      wormchain/x/tokenfactory/bindings/helpers_test.go
  19. 369 0
      wormchain/x/tokenfactory/bindings/message_plugin.go
  20. 57 0
      wormchain/x/tokenfactory/bindings/queries.go
  21. 122 0
      wormchain/x/tokenfactory/bindings/query_plugin.go
  22. 19 0
      wormchain/x/tokenfactory/bindings/testdata/download_releases.sh
  23. BIN
      wormchain/x/tokenfactory/bindings/testdata/token_reflect.wasm
  24. 1 0
      wormchain/x/tokenfactory/bindings/testdata/version.txt
  25. 68 0
      wormchain/x/tokenfactory/bindings/types/msg.go
  26. 59 0
      wormchain/x/tokenfactory/bindings/types/query.go
  27. 37 0
      wormchain/x/tokenfactory/bindings/types/types.go
  28. 452 0
      wormchain/x/tokenfactory/bindings/validate_msg_test.go
  29. 121 0
      wormchain/x/tokenfactory/bindings/validate_queries_test.go
  30. 30 0
      wormchain/x/tokenfactory/bindings/wasm.go
  31. 116 0
      wormchain/x/tokenfactory/client/cli/query.go
  32. 220 0
      wormchain/x/tokenfactory/client/cli/tx.go
  33. 49 0
      wormchain/x/tokenfactory/keeper/admins.go
  34. 525 0
      wormchain/x/tokenfactory/keeper/admins_test.go
  35. 86 0
      wormchain/x/tokenfactory/keeper/bankactions.go
  36. 87 0
      wormchain/x/tokenfactory/keeper/createdenom.go
  37. 163 0
      wormchain/x/tokenfactory/keeper/createdenom_test.go
  38. 29 0
      wormchain/x/tokenfactory/keeper/creators.go
  39. 58 0
      wormchain/x/tokenfactory/keeper/genesis.go
  40. 59 0
      wormchain/x/tokenfactory/keeper/genesis_test.go
  41. 35 0
      wormchain/x/tokenfactory/keeper/grpc_query.go
  42. 87 0
      wormchain/x/tokenfactory/keeper/keeper.go
  43. 66 0
      wormchain/x/tokenfactory/keeper/keeper_test.go
  44. 209 0
      wormchain/x/tokenfactory/keeper/msg_server.go
  45. 248 0
      wormchain/x/tokenfactory/keeper/msg_server_test.go
  46. 18 0
      wormchain/x/tokenfactory/keeper/params.go
  47. 229 0
      wormchain/x/tokenfactory/module.go
  48. 25 0
      wormchain/x/tokenfactory/simulation/genesis.go
  49. 411 0
      wormchain/x/tokenfactory/simulation/operations.go
  50. 23 0
      wormchain/x/tokenfactory/simulation/params.go
  51. 8 0
      wormchain/x/tokenfactory/testhelpers/consts.go
  52. 66 0
      wormchain/x/tokenfactory/testhelpers/suite.go
  53. 15 0
      wormchain/x/tokenfactory/types/authorityMetadata.go
  54. 352 0
      wormchain/x/tokenfactory/types/authorityMetadata.pb.go
  55. 24 0
      wormchain/x/tokenfactory/types/authzcodec/codec.go
  56. 21 0
      wormchain/x/tokenfactory/types/capabilities.go
  57. 48 0
      wormchain/x/tokenfactory/types/codec.go
  58. 68 0
      wormchain/x/tokenfactory/types/denoms.go
  59. 132 0
      wormchain/x/tokenfactory/types/denoms_test.go
  60. 23 0
      wormchain/x/tokenfactory/types/errors.go
  61. 18 0
      wormchain/x/tokenfactory/types/events.go
  62. 38 0
      wormchain/x/tokenfactory/types/expected_keepers.go
  63. 51 0
      wormchain/x/tokenfactory/types/genesis.go
  64. 650 0
      wormchain/x/tokenfactory/types/genesis.pb.go
  65. 139 0
      wormchain/x/tokenfactory/types/genesis_test.go
  66. 49 0
      wormchain/x/tokenfactory/types/keys.go
  67. 277 0
      wormchain/x/tokenfactory/types/msgs.go
  68. 451 0
      wormchain/x/tokenfactory/types/msgs_test.go
  69. 71 0
      wormchain/x/tokenfactory/types/params.go
  70. 383 0
      wormchain/x/tokenfactory/types/params.pb.go
  71. 1333 0
      wormchain/x/tokenfactory/types/query.pb.go
  72. 355 0
      wormchain/x/tokenfactory/types/query.pb.gw.go
  73. 2830 0
      wormchain/x/tokenfactory/types/tx.pb.go

+ 1 - 0
wormchain/Dockerfile.proto

@@ -10,6 +10,7 @@ RUN ignite generate proto-go
 
 
 FROM scratch AS go-export
 FROM scratch AS go-export
 COPY --from=ignite-go-build /apps/wormchain/x/wormhole/types /x/wormhole/types
 COPY --from=ignite-go-build /apps/wormchain/x/wormhole/types /x/wormhole/types
+COPY --from=ignite-go-build /apps/wormchain/x/tokenfactory/types /x/tokenfactory/types
 
 
 FROM base AS ignite-vue-build
 FROM base AS ignite-vue-build
 RUN NODE_OPTIONS="" ignite generate vuex --proto-all-modules
 RUN NODE_OPTIONS="" ignite generate vuex --proto-all-modules

+ 42 - 4
wormchain/app/app.go

@@ -98,7 +98,13 @@ import (
 	wormholeclient "github.com/wormhole-foundation/wormchain/x/wormhole/client"
 	wormholeclient "github.com/wormhole-foundation/wormchain/x/wormhole/client"
 	wormholemodulekeeper "github.com/wormhole-foundation/wormchain/x/wormhole/keeper"
 	wormholemodulekeeper "github.com/wormhole-foundation/wormchain/x/wormhole/keeper"
 	wormholemoduletypes "github.com/wormhole-foundation/wormchain/x/wormhole/types"
 	wormholemoduletypes "github.com/wormhole-foundation/wormchain/x/wormhole/types"
+
 	// this line is used by starport scaffolding # stargate/app/moduleImport
 	// this line is used by starport scaffolding # stargate/app/moduleImport
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings"
+	tokenfactorykeeper "github.com/wormhole-foundation/wormchain/x/tokenfactory/keeper"
+	tokenfactorytypes "github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
 )
 )
 
 
 const (
 const (
@@ -135,6 +141,14 @@ func GetWasmOpts(app *App, appOpts servertypes.AppOptions) []wasm.Option {
 	// add the custom wormhole query handler
 	// add the custom wormhole query handler
 	wasmOpts = append(wasmOpts, wasmkeeper.WithQueryPlugins(wormholemodulekeeper.NewCustomQueryHandler(app.WormholeKeeper)))
 	wasmOpts = append(wasmOpts, wasmkeeper.WithQueryPlugins(wormholemodulekeeper.NewCustomQueryHandler(app.WormholeKeeper)))
 
 
+	// Move custom query of token factory to stargate, still use custom msg which is tfOpts[1]
+	bankBaseKeeper, ok := app.BankKeeper.(bankkeeper.BaseKeeper)
+	if !ok {
+		panic("Cannot cast bank keeper to bank basekeeper")
+	}
+	tfOpts := bindings.RegisterCustomPlugins(&bankBaseKeeper, &app.TokenFactoryKeeper)
+	wasmOpts = append(wasmOpts, tfOpts...)
+
 	return wasmOpts
 	return wasmOpts
 }
 }
 
 
@@ -166,6 +180,7 @@ var (
 		wormholemodule.AppModuleBasic{},
 		wormholemodule.AppModuleBasic{},
 		// this line is used by starport scaffolding # stargate/app/moduleBasic
 		// this line is used by starport scaffolding # stargate/app/moduleBasic
 		wasm.AppModuleBasic{},
 		wasm.AppModuleBasic{},
+		tokenfactory.AppModuleBasic{},
 	)
 	)
 
 
 	// module account permissions
 	// module account permissions
@@ -179,8 +194,11 @@ var (
 		ibctransfertypes.ModuleName:    {authtypes.Minter, authtypes.Burner},
 		ibctransfertypes.ModuleName:    {authtypes.Minter, authtypes.Burner},
 		wormholemoduletypes.ModuleName: nil,
 		wormholemoduletypes.ModuleName: nil,
 		// this line is used by starport scaffolding # stargate/app/maccPerms
 		// this line is used by starport scaffolding # stargate/app/maccPerms
-		wasm.ModuleName: {authtypes.Burner},
+		wasm.ModuleName:              {authtypes.Burner},
+		tokenfactorytypes.ModuleName: {authtypes.Minter, authtypes.Burner},
 	}
 	}
+
+	tokenFactoryCapabilities = []string{}
 )
 )
 
 
 var (
 var (
@@ -235,7 +253,8 @@ type App struct {
 	ScopedIBCKeeper      capabilitykeeper.ScopedKeeper
 	ScopedIBCKeeper      capabilitykeeper.ScopedKeeper
 	ScopedTransferKeeper capabilitykeeper.ScopedKeeper
 	ScopedTransferKeeper capabilitykeeper.ScopedKeeper
 
 
-	WormholeKeeper wormholemodulekeeper.Keeper
+	WormholeKeeper     wormholemodulekeeper.Keeper
+	TokenFactoryKeeper tokenfactorykeeper.Keeper
 
 
 	// this line is used by starport scaffolding # stargate/app/keeperDeclaration
 	// this line is used by starport scaffolding # stargate/app/keeperDeclaration
 	wasmKeeper       wasm.Keeper
 	wasmKeeper       wasm.Keeper
@@ -274,7 +293,7 @@ func New(
 		evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey,
 		evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey,
 		wormholemoduletypes.StoreKey,
 		wormholemoduletypes.StoreKey,
 		// this line is used by starport scaffolding # stargate/app/storeKey
 		// this line is used by starport scaffolding # stargate/app/storeKey
-		wasm.StoreKey,
+		wasm.StoreKey, tokenfactorytypes.StoreKey,
 	)
 	)
 	tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
 	tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
 	memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
 	memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
@@ -386,10 +405,20 @@ func New(
 		&stakingKeeper, govRouter,
 		&stakingKeeper, govRouter,
 	)
 	)
 
 
+	app.TokenFactoryKeeper = tokenfactorykeeper.NewKeeper(
+		app.keys[tokenfactorytypes.StoreKey],
+		app.GetSubspace(tokenfactorytypes.ModuleName),
+		app.AccountKeeper,
+		app.BankKeeper,
+		app.DistrKeeper,
+		tokenFactoryCapabilities,
+	)
+
 	// The last arguments can contain custom message handlers, and custom query handlers,
 	// The last arguments can contain custom message handlers, and custom query handlers,
 	// if we want to allow any custom callbacks
 	// if we want to allow any custom callbacks
-	supportedFeatures := "iterator,staking,stargate,wormhole"
+	supportedFeatures := "iterator,staking,stargate,wormhole,token_factory"
 	wasmDir := filepath.Join(homePath, "data")
 	wasmDir := filepath.Join(homePath, "data")
+
 	// Instantiate wasm keeper with stubs for other modules as we do not need
 	// Instantiate wasm keeper with stubs for other modules as we do not need
 	// wasm to be able to write to other modules.
 	// wasm to be able to write to other modules.
 	app.wasmKeeper = wasm.NewKeeper(
 	app.wasmKeeper = wasm.NewKeeper(
@@ -459,6 +488,7 @@ func New(
 		wormholeModule,
 		wormholeModule,
 		// this line is used by starport scaffolding # stargate/app/appModule
 		// this line is used by starport scaffolding # stargate/app/appModule
 		wasm.NewAppModule(appCodec, &app.wasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper),
 		wasm.NewAppModule(appCodec, &app.wasmKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper),
+		tokenfactory.NewAppModule(app.TokenFactoryKeeper, app.AccountKeeper, app.BankKeeper),
 	)
 	)
 
 
 	// During begin block slashing happens after distr.BeginBlocker so that
 	// During begin block slashing happens after distr.BeginBlocker so that
@@ -486,6 +516,7 @@ func New(
 		wormholemoduletypes.ModuleName,
 		wormholemoduletypes.ModuleName,
 		// this line is used by starport scaffolding # stargate/app/beginBlockers
 		// this line is used by starport scaffolding # stargate/app/beginBlockers
 		wasm.ModuleName,
 		wasm.ModuleName,
+		tokenfactorytypes.ModuleName,
 	)
 	)
 
 
 	app.mm.SetOrderEndBlockers(
 	app.mm.SetOrderEndBlockers(
@@ -509,6 +540,7 @@ func New(
 		wormholemoduletypes.ModuleName,
 		wormholemoduletypes.ModuleName,
 		// this line is used by starport scaffolding # stargate/app/endBlockers
 		// this line is used by starport scaffolding # stargate/app/endBlockers
 		wasm.ModuleName,
 		wasm.ModuleName,
+		tokenfactorytypes.ModuleName,
 	)
 	)
 
 
 	// NOTE: The genutils module must occur after staking so that pools are
 	// NOTE: The genutils module must occur after staking so that pools are
@@ -540,6 +572,7 @@ func New(
 		feegrant.ModuleName,
 		feegrant.ModuleName,
 		// this line is used by starport scaffolding # stargate/app/initGenesis
 		// this line is used by starport scaffolding # stargate/app/initGenesis
 		wasm.ModuleName,
 		wasm.ModuleName,
+		tokenfactorytypes.ModuleName,
 	)
 	)
 
 
 	app.mm.RegisterInvariants(&app.CrisisKeeper)
 	app.mm.RegisterInvariants(&app.CrisisKeeper)
@@ -725,6 +758,10 @@ func (app *App) RegisterTendermintService(clientCtx client.Context) {
 	tmservice.RegisterTendermintService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry)
 	tmservice.RegisterTendermintService(app.BaseApp.GRPCQueryRouter(), clientCtx, app.interfaceRegistry)
 }
 }
 
 
+func (app *App) GetWasmKeeper() *wasmkeeper.Keeper {
+	return &app.wasmKeeper
+}
+
 // GetMaccPerms returns a copy of the module account permissions
 // GetMaccPerms returns a copy of the module account permissions
 func GetMaccPerms() map[string][]string {
 func GetMaccPerms() map[string][]string {
 	dupMaccPerms := make(map[string][]string)
 	dupMaccPerms := make(map[string][]string)
@@ -751,6 +788,7 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino
 	paramsKeeper.Subspace(wormholemoduletypes.ModuleName)
 	paramsKeeper.Subspace(wormholemoduletypes.ModuleName)
 	// this line is used by starport scaffolding # stargate/app/paramSubspace
 	// this line is used by starport scaffolding # stargate/app/paramSubspace
 	paramsKeeper.Subspace(wasm.ModuleName)
 	paramsKeeper.Subspace(wasm.ModuleName)
+	paramsKeeper.Subspace(tokenfactorytypes.ModuleName)
 
 
 	return paramsKeeper
 	return paramsKeeper
 }
 }

+ 17 - 0
wormchain/app/apptesting/events.go

@@ -0,0 +1,17 @@
+package apptesting
+
+import sdk "github.com/cosmos/cosmos-sdk/types"
+
+// AssertEventEmitted asserts that ctx's event manager has emitted the given number of events
+// of the given type.
+func (s *KeeperTestHelper) AssertEventEmitted(ctx sdk.Context, eventTypeExpected string, numEventsExpected int) {
+	allEvents := ctx.EventManager().Events()
+	// filter out other events
+	actualEvents := make([]sdk.Event, 0)
+	for _, event := range allEvents {
+		if event.Type == eventTypeExpected {
+			actualEvents = append(actualEvents, event)
+		}
+	}
+	s.Equal(numEventsExpected, len(actualEvents))
+}

+ 50 - 0
wormchain/app/apptesting/mock_key.go

@@ -0,0 +1,50 @@
+package apptesting
+
+import (
+	"github.com/tendermint/tendermint/crypto"
+	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+	tmtypes "github.com/tendermint/tendermint/types"
+
+	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+	"github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
+	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
+)
+
+var _ tmtypes.PrivValidator = PV{}
+
+// PV implements PrivValidator without any safety or persistence.
+// Only use it for testing.
+type PV struct {
+	PrivKey cryptotypes.PrivKey
+}
+
+func NewPV() PV {
+	return PV{ed25519.GenPrivKey()}
+}
+
+// GetPubKey implements PrivValidator interface
+func (pv PV) GetPubKey() (crypto.PubKey, error) {
+	return cryptocodec.ToTmPubKeyInterface(pv.PrivKey.PubKey())
+}
+
+// SignVote implements PrivValidator interface
+func (pv PV) SignVote(chainID string, vote *tmproto.Vote) error {
+	signBytes := tmtypes.VoteSignBytes(chainID, vote)
+	sig, err := pv.PrivKey.Sign(signBytes)
+	if err != nil {
+		return err
+	}
+	vote.Signature = sig
+	return nil
+}
+
+// SignProposal implements PrivValidator interface
+func (pv PV) SignProposal(chainID string, proposal *tmproto.Proposal) error {
+	signBytes := tmtypes.ProposalSignBytes(chainID, proposal)
+	sig, err := pv.PrivKey.Sign(signBytes)
+	if err != nil {
+		return err
+	}
+	proposal.Signature = sig
+	return nil
+}

+ 248 - 0
wormchain/app/apptesting/test_helpers.go

@@ -0,0 +1,248 @@
+package apptesting
+
+import (
+	"encoding/json"
+	"testing"
+	"time"
+
+	"github.com/CosmWasm/wasmd/x/wasm/keeper"
+	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
+	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+	"github.com/stretchr/testify/require"
+	"github.com/tendermint/spm/cosmoscmd"
+	abci "github.com/tendermint/tendermint/abci/types"
+	"github.com/tendermint/tendermint/crypto"
+	"github.com/tendermint/tendermint/crypto/ed25519"
+	"github.com/tendermint/tendermint/libs/log"
+	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+	tmtypes "github.com/tendermint/tendermint/types"
+	dbm "github.com/tendermint/tm-db"
+	"github.com/wormhole-foundation/wormchain/app"
+	appparams "github.com/wormhole-foundation/wormchain/app/params"
+)
+
+// SimAppChainID hardcoded chainID for simulation
+const (
+	SimAppChainID = "wormchain-app"
+)
+
+// EmptyBaseAppOptions is a stub implementing AppOptions
+type EmptyBaseAppOptions struct{}
+
+// Get implements AppOptions
+func (ao EmptyBaseAppOptions) Get(_ string) interface{} {
+	return nil
+}
+
+// DefaultConsensusParams defines the default Tendermint consensus params used
+// in wormApp testing.
+var DefaultConsensusParams = &abci.ConsensusParams{
+	Block: &abci.BlockParams{
+		MaxBytes: 200000,
+		MaxGas:   2000000,
+	},
+	Evidence: &tmproto.EvidenceParams{
+		MaxAgeNumBlocks: 302400,
+		MaxAgeDuration:  504 * time.Hour, // 3 weeks is the max duration
+		MaxBytes:        10000,
+	},
+	Validator: &tmproto.ValidatorParams{
+		PubKeyTypes: []string{
+			tmtypes.ABCIPubKeyTypeEd25519,
+		},
+	},
+}
+
+type EmptyAppOptions struct{}
+
+func (EmptyAppOptions) Get(_ string) interface{} { return nil }
+
+func Setup(t *testing.T, _ bool, _ uint) *app.App {
+	t.Helper()
+
+	privVal := NewPV()
+	pubKey, err := privVal.GetPubKey()
+	require.NoError(t, err)
+	// create validator set with single validator
+	validator := tmtypes.NewValidator(pubKey, 1)
+	valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator})
+
+	// generate genesis account
+	senderPrivKey := secp256k1.GenPrivKey()
+	acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0)
+	balance := banktypes.Balance{
+		Address: acc.GetAddress().String(),
+		Coins:   sdk.NewCoins(sdk.NewCoin(appparams.BondDenom, sdk.NewInt(100000000000000))),
+	}
+
+	app := SetupWithGenesisValSet(t, valSet, []authtypes.GenesisAccount{acc}, balance)
+
+	return app
+}
+
+// SetupWithGenesisValSet initializes a new junoApp with a validator set and genesis accounts
+// that also act as delegators. For simplicity, each validator is bonded with a delegation
+// of one consensus engine unit in the default token of the JunoApp from first genesis
+// account. A Nop logger is set in JunoApp.
+func SetupWithGenesisValSet(t *testing.T, valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount, balances ...banktypes.Balance) *app.App {
+	t.Helper()
+
+	wormApp, genesisState := setup(true, 5)
+	genesisState = genesisStateWithValSet(t, wormApp, genesisState, valSet, genAccs, balances...)
+
+	stateBytes, err := json.MarshalIndent(genesisState, "", " ")
+	require.NoError(t, err)
+
+	// init chain will set the validator set and initialize the genesis accounts
+	wormApp.InitChain(
+		abci.RequestInitChain{
+			Validators:      []abci.ValidatorUpdate{},
+			ConsensusParams: DefaultConsensusParams,
+			AppStateBytes:   stateBytes,
+		},
+	)
+
+	// commit genesis changes
+	wormApp.Commit()
+	wormApp.BeginBlock(abci.RequestBeginBlock{Header: tmproto.Header{
+		Height:             wormApp.LastBlockHeight() + 1,
+		AppHash:            wormApp.LastCommitID().Hash,
+		ValidatorsHash:     valSet.Hash(),
+		NextValidatorsHash: valSet.Hash(),
+	}})
+
+	return wormApp
+}
+
+func setup(withGenesis bool, invCheckPeriod uint) (*app.App, app.GenesisState) {
+	db := dbm.NewMemDB()
+	encoding := cosmoscmd.MakeEncodingConfig(app.ModuleBasics)
+	wormApp := app.New(
+		log.NewNopLogger(),
+		db,
+		nil,
+		true,
+		map[int64]bool{},
+		app.DefaultNodeHome,
+		invCheckPeriod,
+		encoding,
+		EmptyBaseAppOptions{},
+	)
+	if withGenesis {
+		return wormApp.(*app.App), app.NewDefaultGenesisState(encoding.Marshaler)
+	}
+
+	return wormApp.(*app.App), app.GenesisState{}
+}
+
+func genesisStateWithValSet(t *testing.T,
+	app *app.App, genesisState app.GenesisState,
+	valSet *tmtypes.ValidatorSet, genAccs []authtypes.GenesisAccount,
+	balances ...banktypes.Balance,
+) app.GenesisState {
+	// set genesis accounts
+	authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs)
+	genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis)
+
+	validators := make([]stakingtypes.Validator, 0, len(valSet.Validators))
+	delegations := make([]stakingtypes.Delegation, 0, len(valSet.Validators))
+
+	bondAmt := sdk.DefaultPowerReduction
+
+	for _, val := range valSet.Validators {
+		pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey)
+		require.NoError(t, err)
+		pkAny, err := codectypes.NewAnyWithValue(pk)
+		require.NoError(t, err)
+		validator := stakingtypes.Validator{
+			OperatorAddress:   sdk.ValAddress(val.Address).String(),
+			ConsensusPubkey:   pkAny,
+			Jailed:            false,
+			Status:            stakingtypes.Bonded,
+			Tokens:            bondAmt,
+			DelegatorShares:   sdk.OneDec(),
+			Description:       stakingtypes.Description{},
+			UnbondingHeight:   int64(0),
+			UnbondingTime:     time.Unix(0, 0).UTC(),
+			Commission:        stakingtypes.NewCommission(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec()),
+			MinSelfDelegation: sdk.ZeroInt(),
+		}
+		validators = append(validators, validator)
+		delegations = append(delegations, stakingtypes.NewDelegation(genAccs[0].GetAddress(), val.Address.Bytes(), sdk.OneDec()))
+
+	}
+	// set validators and delegations
+	defaultStParams := stakingtypes.DefaultParams()
+	stParams := stakingtypes.NewParams(
+		defaultStParams.UnbondingTime,
+		defaultStParams.MaxValidators,
+		defaultStParams.MaxEntries,
+		defaultStParams.HistoricalEntries,
+		appparams.BondDenom,
+	)
+
+	// set validators and delegations
+	stakingGenesis := stakingtypes.NewGenesisState(stParams, validators, delegations)
+	genesisState[stakingtypes.ModuleName] = app.AppCodec().MustMarshalJSON(stakingGenesis)
+
+	totalSupply := sdk.NewCoins()
+	for _, b := range balances {
+		// add genesis acc tokens to total supply
+		totalSupply = totalSupply.Add(b.Coins...)
+	}
+
+	for range delegations {
+		// add delegated tokens to total supply
+		totalSupply = totalSupply.Add(sdk.NewCoin(appparams.BondDenom, bondAmt))
+	}
+
+	// add bonded amount to bonded pool module account
+	balances = append(balances, banktypes.Balance{
+		Address: authtypes.NewModuleAddress(stakingtypes.BondedPoolName).String(),
+		Coins:   sdk.Coins{sdk.NewCoin(appparams.BondDenom, bondAmt)},
+	})
+
+	// update total supply
+	bankGenesis := banktypes.NewGenesisState(
+		banktypes.DefaultGenesisState().Params,
+		balances,
+		totalSupply,
+		[]banktypes.Metadata{},
+	)
+
+	genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis)
+
+	return genesisState
+}
+
+func keyPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) {
+	key := ed25519.GenPrivKey()
+	pub := key.PubKey()
+	addr := sdk.AccAddress(pub.Address())
+	return key, pub, addr
+}
+
+func RandomAccountAddress() sdk.AccAddress {
+	_, _, addr := keyPubAddr()
+	return addr
+}
+
+func ExecuteRawCustom(t *testing.T, ctx sdk.Context, app *app.App, contract sdk.AccAddress, sender sdk.AccAddress, msg json.RawMessage, funds sdk.Coin) error {
+	t.Helper()
+	oracleBz, err := json.Marshal(msg)
+	require.NoError(t, err)
+	// no funds sent if amount is 0
+	var coins sdk.Coins
+	if !funds.Amount.IsNil() {
+		coins = sdk.Coins{funds}
+	}
+
+	contractKeeper := keeper.NewDefaultPermissionKeeper(app.GetWasmKeeper())
+	_, err = contractKeeper.Execute(ctx, contract, sender, oracleBz, coins)
+	return err
+}

+ 295 - 0
wormchain/app/apptesting/test_suite.go

@@ -0,0 +1,295 @@
+package apptesting
+
+import (
+	"encoding/json"
+	"fmt"
+	"testing"
+	"time"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	"github.com/cosmos/cosmos-sdk/client"
+	cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
+	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
+	"github.com/cosmos/cosmos-sdk/simapp"
+	"github.com/cosmos/cosmos-sdk/store/rootmulti"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/tx/signing"
+	authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
+	"github.com/cosmos/cosmos-sdk/x/authz"
+	distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
+	slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
+	"github.com/cosmos/cosmos-sdk/x/staking"
+	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
+	"github.com/stretchr/testify/require"
+	"github.com/stretchr/testify/suite"
+	abci "github.com/tendermint/tendermint/abci/types"
+	"github.com/tendermint/tendermint/crypto/ed25519"
+	"github.com/tendermint/tendermint/libs/log"
+	tmtypes "github.com/tendermint/tendermint/proto/tendermint/types"
+	dbm "github.com/tendermint/tm-db"
+
+	authzcodec "github.com/wormhole-foundation/wormchain/x/tokenfactory/types/authzcodec"
+
+	minttypes "github.com/cosmos/cosmos-sdk/x/mint/types"
+	"github.com/wormhole-foundation/wormchain/app"
+)
+
+type KeeperTestHelper struct {
+	suite.Suite
+
+	App         *app.App
+	Ctx         sdk.Context
+	QueryHelper *baseapp.QueryServiceTestHelper
+	TestAccs    []sdk.AccAddress
+}
+
+var (
+	SecondaryDenom  = "uion"
+	SecondaryAmount = sdk.NewInt(100000000)
+)
+
+// Setup sets up basic environment for suite (App, Ctx, and test accounts)
+func (s *KeeperTestHelper) Setup() {
+	s.App = Setup(s.T(), true, 0)
+	s.Ctx = s.App.BaseApp.NewContext(false, tmtypes.Header{Height: 1, ChainID: "osmosis-1", Time: time.Now().UTC()})
+	s.QueryHelper = &baseapp.QueryServiceTestHelper{
+		GRPCQueryRouter: s.App.GRPCQueryRouter(),
+		Ctx:             s.Ctx,
+	}
+	s.TestAccs = CreateRandomAccounts(3)
+}
+
+func (s *KeeperTestHelper) SetupTestForInitGenesis() {
+	// Setting to True, leads to init genesis not running
+	s.App = Setup(s.T(), true, 0)
+	s.Ctx = s.App.BaseApp.NewContext(true, tmtypes.Header{})
+}
+
+// CreateTestContext creates a test context.
+func (s *KeeperTestHelper) CreateTestContext() sdk.Context {
+	ctx, _ := s.CreateTestContextWithMultiStore()
+	return ctx
+}
+
+// CreateTestContextWithMultiStore creates a test context and returns it together with multi store.
+func (s *KeeperTestHelper) CreateTestContextWithMultiStore() (sdk.Context, sdk.CommitMultiStore) {
+	db := dbm.NewMemDB()
+	logger := log.NewNopLogger()
+
+	ms := rootmulti.NewStore(db, logger)
+
+	return sdk.NewContext(ms, tmtypes.Header{}, false, logger), ms
+}
+
+// CreateTestContext creates a test context.
+func (s *KeeperTestHelper) Commit() {
+	oldHeight := s.Ctx.BlockHeight()
+	oldHeader := s.Ctx.BlockHeader()
+	s.App.Commit()
+	newHeader := tmtypes.Header{Height: oldHeight + 1, ChainID: oldHeader.ChainID, Time: oldHeader.Time.Add(time.Second)}
+	s.App.BeginBlock(abci.RequestBeginBlock{Header: newHeader})
+	s.Ctx = s.App.NewContext(false, newHeader)
+}
+
+// FundAcc funds target address with specified amount.
+func (s *KeeperTestHelper) FundAcc(acc sdk.AccAddress, amounts sdk.Coins) {
+	err := simapp.FundAccount(s.App.BankKeeper, s.Ctx, acc, amounts)
+	s.Require().NoError(err)
+}
+
+// FundModuleAcc funds target modules with specified amount.
+func (s *KeeperTestHelper) FundModuleAcc(moduleName string, amounts sdk.Coins) {
+	err := simapp.FundModuleAccount(s.App.BankKeeper, s.Ctx, moduleName, amounts)
+	s.Require().NoError(err)
+}
+
+func (s *KeeperTestHelper) MintCoins(coins sdk.Coins) {
+	err := s.App.BankKeeper.MintCoins(s.Ctx, minttypes.ModuleName, coins)
+	s.Require().NoError(err)
+}
+
+// SetupValidator sets up a validator and returns the ValAddress.
+func (s *KeeperTestHelper) SetupValidator(bondStatus stakingtypes.BondStatus) sdk.ValAddress {
+	valPub := secp256k1.GenPrivKey().PubKey()
+	valAddr := sdk.ValAddress(valPub.Address())
+	bondDenom := s.App.StakingKeeper.GetParams(s.Ctx).BondDenom
+	selfBond := sdk.NewCoins(sdk.Coin{Amount: sdk.NewInt(100), Denom: bondDenom})
+
+	s.FundAcc(sdk.AccAddress(valAddr), selfBond)
+
+	stakingHandler := staking.NewHandler(s.App.StakingKeeper)
+	stakingCoin := sdk.NewCoin(sdk.DefaultBondDenom, selfBond[0].Amount)
+	ZeroCommission := stakingtypes.NewCommissionRates(sdk.ZeroDec(), sdk.ZeroDec(), sdk.ZeroDec())
+	msg, err := stakingtypes.NewMsgCreateValidator(valAddr, valPub, stakingCoin, stakingtypes.Description{}, ZeroCommission, sdk.OneInt())
+	s.Require().NoError(err)
+	res, err := stakingHandler(s.Ctx, msg)
+	s.Require().NoError(err)
+	s.Require().NotNil(res)
+
+	val, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr)
+	s.Require().True(found)
+
+	val = val.UpdateStatus(bondStatus)
+	s.App.StakingKeeper.SetValidator(s.Ctx, val)
+
+	consAddr, err := val.GetConsAddr()
+	s.Suite.Require().NoError(err)
+
+	signingInfo := slashingtypes.NewValidatorSigningInfo(
+		consAddr,
+		s.Ctx.BlockHeight(),
+		0,
+		time.Unix(0, 0),
+		false,
+		0,
+	)
+	s.App.SlashingKeeper.SetValidatorSigningInfo(s.Ctx, consAddr, signingInfo)
+
+	return valAddr
+}
+
+// BeginNewBlock starts a new block.
+func (s *KeeperTestHelper) BeginNewBlock() {
+	var valAddr []byte
+
+	validators := s.App.StakingKeeper.GetAllValidators(s.Ctx)
+	if len(validators) >= 1 {
+		valAddrFancy, err := validators[0].GetConsAddr()
+		s.Require().NoError(err)
+		valAddr = valAddrFancy.Bytes()
+	} else {
+		valAddrFancy := s.SetupValidator(stakingtypes.Bonded)
+		validator, _ := s.App.StakingKeeper.GetValidator(s.Ctx, valAddrFancy)
+		valAddr2, _ := validator.GetConsAddr()
+		valAddr = valAddr2.Bytes()
+	}
+
+	s.BeginNewBlockWithProposer(valAddr)
+}
+
+// BeginNewBlockWithProposer begins a new block with a proposer.
+func (s *KeeperTestHelper) BeginNewBlockWithProposer(proposer sdk.ValAddress) {
+	validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, proposer)
+	s.Assert().True(found)
+
+	valConsAddr, err := validator.GetConsAddr()
+	s.Require().NoError(err)
+
+	valAddr := valConsAddr.Bytes()
+
+	newBlockTime := s.Ctx.BlockTime().Add(5 * time.Second)
+
+	header := tmtypes.Header{Height: s.Ctx.BlockHeight() + 1, Time: newBlockTime}
+	newCtx := s.Ctx.WithBlockTime(newBlockTime).WithBlockHeight(s.Ctx.BlockHeight() + 1)
+	s.Ctx = newCtx
+	lastCommitInfo := abci.LastCommitInfo{
+		Votes: []abci.VoteInfo{{
+			Validator:       abci.Validator{Address: valAddr, Power: 1000},
+			SignedLastBlock: true,
+		}},
+	}
+	reqBeginBlock := abci.RequestBeginBlock{Header: header, LastCommitInfo: lastCommitInfo}
+
+	fmt.Println("beginning block ", s.Ctx.BlockHeight())
+	s.App.BeginBlocker(s.Ctx, reqBeginBlock)
+}
+
+// EndBlock ends the block.
+func (s *KeeperTestHelper) EndBlock() {
+	reqEndBlock := abci.RequestEndBlock{Height: s.Ctx.BlockHeight()}
+	s.App.EndBlocker(s.Ctx, reqEndBlock)
+}
+
+// AllocateRewardsToValidator allocates reward tokens to a distribution module then allocates rewards to the validator address.
+func (s *KeeperTestHelper) AllocateRewardsToValidator(valAddr sdk.ValAddress, rewardAmt sdk.Int) {
+	validator, found := s.App.StakingKeeper.GetValidator(s.Ctx, valAddr)
+	s.Require().True(found)
+
+	// allocate reward tokens to distribution module
+	coins := sdk.Coins{sdk.NewCoin(sdk.DefaultBondDenom, rewardAmt)}
+	err := simapp.FundModuleAccount(s.App.BankKeeper, s.Ctx, distrtypes.ModuleName, coins)
+	s.Require().NoError(err)
+
+	// allocate rewards to validator
+	s.Ctx = s.Ctx.WithBlockHeight(s.Ctx.BlockHeight() + 1)
+	decTokens := sdk.DecCoins{{Denom: sdk.DefaultBondDenom, Amount: sdk.NewDec(20000)}}
+	s.App.DistrKeeper.AllocateTokensToValidator(s.Ctx, validator, decTokens)
+}
+
+// BuildTx builds a transaction.
+func (s *KeeperTestHelper) BuildTx(
+	txBuilder client.TxBuilder,
+	msgs []sdk.Msg,
+	sigV2 signing.SignatureV2,
+	memo string, txFee sdk.Coins,
+	gasLimit uint64,
+) authsigning.Tx {
+	err := txBuilder.SetMsgs(msgs[0])
+	s.Require().NoError(err)
+
+	err = txBuilder.SetSignatures(sigV2)
+	s.Require().NoError(err)
+
+	txBuilder.SetMemo(memo)
+	txBuilder.SetFeeAmount(txFee)
+	txBuilder.SetGasLimit(gasLimit)
+
+	return txBuilder.GetTx()
+}
+
+// CreateRandomAccounts is a function return a list of randomly generated AccAddresses
+func CreateRandomAccounts(numAccts int) []sdk.AccAddress {
+	testAddrs := make([]sdk.AccAddress, numAccts)
+	for i := 0; i < numAccts; i++ {
+		pk := ed25519.GenPrivKey().PubKey()
+		testAddrs[i] = sdk.AccAddress(pk.Address())
+	}
+
+	return testAddrs
+}
+
+func TestMessageAuthzSerialization(t *testing.T, msg sdk.Msg) {
+	someDate := time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)
+	const (
+		mockGranter string = "cosmos1abc"
+		mockGrantee string = "cosmos1xyz"
+	)
+
+	var (
+		mockMsgGrant  authz.MsgGrant
+		mockMsgRevoke authz.MsgRevoke
+		mockMsgExec   authz.MsgExec
+	)
+
+	// Authz: Grant Msg
+	typeURL := sdk.MsgTypeURL(msg)
+	grant, err := authz.NewGrant(authz.NewGenericAuthorization(typeURL), someDate.Add(time.Hour))
+	require.NoError(t, err)
+
+	msgGrant := authz.MsgGrant{Granter: mockGranter, Grantee: mockGrantee, Grant: grant}
+	msgGrantBytes := json.RawMessage(sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msgGrant)))
+	err = authzcodec.ModuleCdc.UnmarshalJSON(msgGrantBytes, &mockMsgGrant)
+	require.NoError(t, err)
+
+	// Authz: Revoke Msg
+	msgRevoke := authz.MsgRevoke{Granter: mockGranter, Grantee: mockGrantee, MsgTypeUrl: typeURL}
+	msgRevokeByte := json.RawMessage(sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msgRevoke)))
+	err = authzcodec.ModuleCdc.UnmarshalJSON(msgRevokeByte, &mockMsgRevoke)
+	require.NoError(t, err)
+
+	// Authz: Exec Msg
+	msgAny, err := cdctypes.NewAnyWithValue(msg)
+	require.NoError(t, err)
+	msgExec := authz.MsgExec{Grantee: mockGrantee, Msgs: []*cdctypes.Any{msgAny}}
+	execMsgByte := json.RawMessage(sdk.MustSortJSON(authzcodec.ModuleCdc.MustMarshalJSON(&msgExec)))
+	err = authzcodec.ModuleCdc.UnmarshalJSON(execMsgByte, &mockMsgExec)
+	require.NoError(t, err)
+	require.Equal(t, msgExec.Msgs[0].Value, mockMsgExec.Msgs[0].Value)
+}
+
+func GenerateTestAddrs() (string, string) {
+	pk1 := ed25519.GenPrivKey().PubKey()
+	validAddr := sdk.AccAddress(pk1.Address()).String()
+	invalidAddr := sdk.AccAddress("invalid").String()
+	return validAddr, invalidAddr
+}

+ 16 - 0
wormchain/app/params/const.go

@@ -0,0 +1,16 @@
+package params
+
+const (
+	// Name defines the application name of Wormchain.
+	Name = "uworm"
+
+	// BondDenom defines the native staking token denomination.
+	BondDenom = "uworm"
+
+	// DisplayDenom defines the name, symbol, and display value of the worm token.
+	DisplayDenom = "WORM"
+
+	// DefaultGasLimit - set to the same value as cosmos-sdk flags.DefaultGasLimit
+	// this value is currently only used in tests.
+	DefaultGasLimit = 200000
+)

+ 14 - 0
wormchain/app/params/weights.go

@@ -0,0 +1,14 @@
+package params
+
+// Simulation parameter constants
+const (
+	StakePerAccount           = "stake_per_account"
+	InitiallyBondedValidators = "initially_bonded_validators"
+
+	DefaultWeightMsgCreateDenom      int = 100
+	DefaultWeightMsgMint             int = 100
+	DefaultWeightMsgBurn             int = 100
+	DefaultWeightMsgChangeAdmin      int = 100
+	DefaultWeightMsgSetDenomMetadata int = 100
+	DefaultWeightMsgForceTransfer    int = 100
+)

+ 1 - 1
wormchain/go.mod

@@ -14,6 +14,7 @@ require (
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
 	github.com/grpc-ecosystem/grpc-gateway v1.16.0
 	github.com/holiman/uint256 v1.2.1
 	github.com/holiman/uint256 v1.2.1
 	github.com/prometheus/client_golang v1.14.0
 	github.com/prometheus/client_golang v1.14.0
+	github.com/regen-network/cosmos-proto v0.3.1
 	github.com/spf13/cast v1.5.0
 	github.com/spf13/cast v1.5.0
 	github.com/spf13/cobra v1.6.0
 	github.com/spf13/cobra v1.6.0
 	github.com/stretchr/testify v1.8.1
 	github.com/stretchr/testify v1.8.1
@@ -108,7 +109,6 @@ require (
 	github.com/prometheus/procfs v0.8.0 // indirect
 	github.com/prometheus/procfs v0.8.0 // indirect
 	github.com/rakyll/statik v0.1.7 // indirect
 	github.com/rakyll/statik v0.1.7 // indirect
 	github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
 	github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
-	github.com/regen-network/cosmos-proto v0.3.1 // indirect
 	github.com/rs/cors v1.8.2 // indirect
 	github.com/rs/cors v1.8.2 // indirect
 	github.com/rs/zerolog v1.27.0 // indirect
 	github.com/rs/zerolog v1.27.0 // indirect
 	github.com/sasha-s/go-deadlock v0.3.1 // indirect
 	github.com/sasha-s/go-deadlock v0.3.1 // indirect

+ 17 - 0
wormchain/proto/osmosis/tokenfactory/v1beta1/authorityMetadata.proto

@@ -0,0 +1,17 @@
+syntax = "proto3";
+package osmosis.tokenfactory.v1beta1;
+
+import "gogoproto/gogo.proto";
+import "cosmos/base/v1beta1/coin.proto";
+
+option go_package = "github.com/wormhole-foundation/wormchain/x/tokenfactory/types";
+
+// DenomAuthorityMetadata specifies metadata for addresses that have specific
+// capabilities over a token factory denom. Right now there is only one Admin
+// permission, but is planned to be extended to the future.
+message DenomAuthorityMetadata {
+  option (gogoproto.equal) = true;
+
+  // Can be empty for no admin, or a valid osmosis address
+  string admin = 1 [ (gogoproto.moretags) = "yaml:\"admin\"" ];
+}

+ 32 - 0
wormchain/proto/osmosis/tokenfactory/v1beta1/genesis.proto

@@ -0,0 +1,32 @@
+syntax = "proto3";
+package osmosis.tokenfactory.v1beta1;
+
+import "gogoproto/gogo.proto";
+import "osmosis/tokenfactory/v1beta1/authorityMetadata.proto";
+import "osmosis/tokenfactory/v1beta1/params.proto";
+
+option go_package = "github.com/wormhole-foundation/wormchain/x/tokenfactory/types";
+
+// GenesisState defines the tokenfactory module's genesis state.
+message GenesisState {
+  // params defines the paramaters of the module.
+  Params params = 1 [ (gogoproto.nullable) = false ];
+
+  repeated GenesisDenom factory_denoms = 2 [
+    (gogoproto.moretags) = "yaml:\"factory_denoms\"",
+    (gogoproto.nullable) = false
+  ];
+}
+
+// GenesisDenom defines a tokenfactory denom that is defined within genesis
+// state. The structure contains DenomAuthorityMetadata which defines the
+// denom's admin.
+message GenesisDenom {
+  option (gogoproto.equal) = true;
+
+  string denom = 1 [ (gogoproto.moretags) = "yaml:\"denom\"" ];
+  DenomAuthorityMetadata authority_metadata = 2 [
+    (gogoproto.moretags) = "yaml:\"authority_metadata\"",
+    (gogoproto.nullable) = false
+  ];
+}

+ 26 - 0
wormchain/proto/osmosis/tokenfactory/v1beta1/params.proto

@@ -0,0 +1,26 @@
+syntax = "proto3";
+package osmosis.tokenfactory.v1beta1;
+
+import "gogoproto/gogo.proto";
+import "osmosis/tokenfactory/v1beta1/authorityMetadata.proto";
+import "cosmos_proto/cosmos.proto";
+import "cosmos/base/v1beta1/coin.proto";
+
+option go_package = "github.com/wormhole-foundation/wormchain/x/tokenfactory/types";
+
+// Params defines the parameters for the tokenfactory module.
+message Params {
+  repeated cosmos.base.v1beta1.Coin denom_creation_fee = 1 [
+    (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
+    (gogoproto.moretags) = "yaml:\"denom_creation_fee\"",
+    (gogoproto.nullable) = false
+  ];
+
+  // if denom_creation_fee is an empty array, then this field is used to add more gas consumption
+  // to the base cost.
+  // https://github.com/CosmWasm/token-factory/issues/11
+  uint64 denom_creation_gas_consume = 2 [    
+    (gogoproto.moretags) = "yaml:\"denom_creation_gas_consume\"",
+    (gogoproto.nullable) = true
+  ];
+}

+ 71 - 0
wormchain/proto/osmosis/tokenfactory/v1beta1/query.proto

@@ -0,0 +1,71 @@
+syntax = "proto3";
+package osmosis.tokenfactory.v1beta1;
+
+import "gogoproto/gogo.proto";
+import "google/api/annotations.proto";
+import "cosmos/base/query/v1beta1/pagination.proto";
+import "osmosis/tokenfactory/v1beta1/authorityMetadata.proto";
+import "osmosis/tokenfactory/v1beta1/params.proto";
+
+option go_package = "github.com/wormhole-foundation/wormchain/x/tokenfactory/types";
+
+// Query defines the gRPC querier service.
+service Query {
+  // Params defines a gRPC query method that returns the tokenfactory module's
+  // parameters.
+  rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
+    option (google.api.http).get = "/osmosis/tokenfactory/v1beta1/params";
+  }
+
+  // DenomAuthorityMetadata defines a gRPC query method for fetching
+  // DenomAuthorityMetadata for a particular denom.
+  rpc DenomAuthorityMetadata(QueryDenomAuthorityMetadataRequest)
+      returns (QueryDenomAuthorityMetadataResponse) {
+    option (google.api.http).get =
+        "/osmosis/tokenfactory/v1beta1/denoms/{denom}/authority_metadata";
+  }
+
+  // DenomsFromCreator defines a gRPC query method for fetching all
+  // denominations created by a specific admin/creator.
+  rpc DenomsFromCreator(QueryDenomsFromCreatorRequest)
+      returns (QueryDenomsFromCreatorResponse) {
+    option (google.api.http).get =
+        "/osmosis/tokenfactory/v1beta1/denoms_from_creator/{creator}";
+  }
+}
+
+// QueryParamsRequest is the request type for the Query/Params RPC method.
+message QueryParamsRequest {}
+
+// QueryParamsResponse is the response type for the Query/Params RPC method.
+message QueryParamsResponse {
+  // params defines the parameters of the module.
+  Params params = 1 [ (gogoproto.nullable) = false ];
+}
+
+// QueryDenomAuthorityMetadataRequest defines the request structure for the
+// DenomAuthorityMetadata gRPC query.
+message QueryDenomAuthorityMetadataRequest {
+  string denom = 1 [ (gogoproto.moretags) = "yaml:\"denom\"" ];
+}
+
+// QueryDenomAuthorityMetadataResponse defines the response structure for the
+// DenomAuthorityMetadata gRPC query.
+message QueryDenomAuthorityMetadataResponse {
+  DenomAuthorityMetadata authority_metadata = 1 [
+    (gogoproto.moretags) = "yaml:\"authority_metadata\"",
+    (gogoproto.nullable) = false
+  ];
+}
+
+// QueryDenomsFromCreatorRequest defines the request structure for the
+// DenomsFromCreator gRPC query.
+message QueryDenomsFromCreatorRequest {
+  string creator = 1 [ (gogoproto.moretags) = "yaml:\"creator\"" ];
+}
+
+// QueryDenomsFromCreatorRequest defines the response structure for the
+// DenomsFromCreator gRPC query.
+message QueryDenomsFromCreatorResponse {
+  repeated string denoms = 1 [ (gogoproto.moretags) = "yaml:\"denoms\"" ];
+}

+ 109 - 0
wormchain/proto/osmosis/tokenfactory/v1beta1/tx.proto

@@ -0,0 +1,109 @@
+syntax = "proto3";
+package osmosis.tokenfactory.v1beta1;
+
+import "gogoproto/gogo.proto";
+import "cosmos/base/v1beta1/coin.proto";
+import "cosmos/bank/v1beta1/bank.proto";
+
+option go_package = "github.com/wormhole-foundation/wormchain/x/tokenfactory/types";
+
+// Msg defines the tokefactory module's gRPC message service.
+service Msg {
+  rpc CreateDenom(MsgCreateDenom) returns (MsgCreateDenomResponse);
+  rpc Mint(MsgMint) returns (MsgMintResponse);
+  rpc Burn(MsgBurn) returns (MsgBurnResponse);
+  rpc ChangeAdmin(MsgChangeAdmin) returns (MsgChangeAdminResponse);
+  rpc SetDenomMetadata(MsgSetDenomMetadata)
+      returns (MsgSetDenomMetadataResponse);
+  rpc ForceTransfer(MsgForceTransfer) returns (MsgForceTransferResponse);
+}
+
+// MsgCreateDenom defines the message structure for the CreateDenom gRPC service
+// method. It allows an account to create a new denom. It requires a sender
+// address and a sub denomination. The (sender_address, sub_denomination) tuple
+// must be unique and cannot be re-used.
+//
+// The resulting denom created is defined as
+// <factory/{creatorAddress}/{subdenom}>. The resulting denom's admin is
+// originally set to be the creator, but this can be changed later. The token
+// denom does not indicate the current admin.
+message MsgCreateDenom {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  // subdenom can be up to 44 "alphanumeric" characters long.
+  string subdenom = 2 [ (gogoproto.moretags) = "yaml:\"subdenom\"" ];
+}
+
+// MsgCreateDenomResponse is the return value of MsgCreateDenom
+// It returns the full string of the newly created denom
+message MsgCreateDenomResponse {
+  string new_token_denom = 1
+      [ (gogoproto.moretags) = "yaml:\"new_token_denom\"" ];
+}
+
+// MsgMint is the sdk.Msg type for allowing an admin account to mint
+// more of a token.  For now, we only support minting to the sender account
+message MsgMint {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  cosmos.base.v1beta1.Coin amount = 2 [
+    (gogoproto.moretags) = "yaml:\"amount\"",
+    (gogoproto.nullable) = false
+  ];
+  string mintToAddress = 3
+      [ (gogoproto.moretags) = "yaml:\"mint_to_address\"" ];
+}
+
+message MsgMintResponse {}
+
+// MsgBurn is the sdk.Msg type for allowing an admin account to burn
+// a token.  For now, we only support burning from the sender account.
+message MsgBurn {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  cosmos.base.v1beta1.Coin amount = 2 [
+    (gogoproto.moretags) = "yaml:\"amount\"",
+    (gogoproto.nullable) = false
+  ];
+  string burnFromAddress = 3
+      [ (gogoproto.moretags) = "yaml:\"burn_from_address\"" ];
+}
+
+message MsgBurnResponse {}
+
+// MsgChangeAdmin is the sdk.Msg type for allowing an admin account to reassign
+// adminship of a denom to a new account
+message MsgChangeAdmin {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  string denom = 2 [ (gogoproto.moretags) = "yaml:\"denom\"" ];
+  string new_admin = 3 [ (gogoproto.moretags) = "yaml:\"new_admin\"" ];
+}
+
+// MsgChangeAdminResponse defines the response structure for an executed
+// MsgChangeAdmin message.
+message MsgChangeAdminResponse {}
+
+// MsgSetDenomMetadata is the sdk.Msg type for allowing an admin account to set
+// the denom's bank metadata
+message MsgSetDenomMetadata {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  cosmos.bank.v1beta1.Metadata metadata = 2 [
+    (gogoproto.moretags) = "yaml:\"metadata\"",
+    (gogoproto.nullable) = false
+  ];
+}
+
+// MsgSetDenomMetadataResponse defines the response structure for an executed
+// MsgSetDenomMetadata message.
+message MsgSetDenomMetadataResponse {}
+
+message MsgForceTransfer {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  cosmos.base.v1beta1.Coin amount = 2 [
+    (gogoproto.moretags) = "yaml:\"amount\"",
+    (gogoproto.nullable) = false
+  ];
+  string transferFromAddress = 3
+      [ (gogoproto.moretags) = "yaml:\"transfer_from_address\"" ];
+  string transferToAddress = 4
+      [ (gogoproto.moretags) = "yaml:\"transfer_to_address\"" ];
+}
+
+message MsgForceTransferResponse {}

+ 155 - 0
wormchain/x/tokenfactory/README.md

@@ -0,0 +1,155 @@
+# Token Factory
+
+The tokenfactory module allows any account to create a new token with
+the name `factory/{creator address}/{subdenom}`. Because tokens are
+namespaced by creator address, this allows token minting to be
+permissionless, due to not needing to resolve name collisions. A single
+account can create multiple denoms, by providing a unique subdenom for each
+created denom. Once a denom is created, the original creator is given
+"admin" privileges over the asset. This allows them to:
+
+- Mint their denom to any account
+- Burn their denom from any account
+- Create a transfer of their denom between any two accounts
+- Change the admin. In the future, more admin capabilities may be added. Admins
+  can choose to share admin privileges with other accounts using the authz
+  module. The `ChangeAdmin` functionality, allows changing the master admin
+  account, or even setting it to `""`, meaning no account has admin privileges
+  of the asset.
+
+## Messages
+
+### CreateDenom
+
+Creates a denom of `factory/{creator address}/{subdenom}` given the denom creator
+address and the subdenom. Subdenoms can contain `[a-zA-Z0-9./]`.
+
+```go
+message MsgCreateDenom {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  string subdenom = 2 [ (gogoproto.moretags) = "yaml:\"subdenom\"" ];
+}
+```
+
+**State Modifications:**
+
+- Fund community pool with the denom creation fee from the creator address, set
+  in `Params`.
+- Set `DenomMetaData` via bank keeper.
+- Set `AuthorityMetadata` for the given denom to store the admin for the created
+  denom `factory/{creator address}/{subdenom}`. Admin is automatically set as the
+  Msg sender.
+- Add denom to the `CreatorPrefixStore`, where a state of denoms created per
+  creator is kept.
+
+### Mint
+
+Minting of a specific denom is only allowed for the current admin.
+Note, the current admin is defaulted to the creator of the denom.
+
+```go
+message MsgMint {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  cosmos.base.v1beta1.Coin amount = 2 [
+    (gogoproto.moretags) = "yaml:\"amount\"",
+    (gogoproto.nullable) = false
+  ];
+}
+```
+
+**State Modifications:**
+
+- Safety check the following
+  - Check that the denom minting is created via `tokenfactory` module
+  - Check that the sender of the message is the admin of the denom
+- Mint designated amount of tokens for the denom via `bank` module
+
+### Burn
+
+Burning of a specific denom is only allowed for the current admin.
+Note, the current admin is defaulted to the creator of the denom.
+
+```go
+message MsgBurn {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  cosmos.base.v1beta1.Coin amount = 2 [
+    (gogoproto.moretags) = "yaml:\"amount\"",
+    (gogoproto.nullable) = false
+  ];
+}
+```
+
+**State Modifications:**
+
+- Saftey check the following
+  - Check that the denom minting is created via `tokenfactory` module
+  - Check that the sender of the message is the admin of the denom
+- Burn designated amount of tokens for the denom via `bank` module
+
+### ChangeAdmin
+
+Change the admin of a denom. Note, this is only allowed to be called by the current admin of the denom.
+
+```go
+message MsgChangeAdmin {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  string denom = 2 [ (gogoproto.moretags) = "yaml:\"denom\"" ];
+  string newAdmin = 3 [ (gogoproto.moretags) = "yaml:\"new_admin\"" ];
+}
+```
+
+### SetDenomMetadata
+
+Setting of metadata for a specific denom is only allowed for the admin of the denom.
+It allows the overwriting of the denom metadata in the bank module.
+
+```go
+message MsgChangeAdmin {
+  string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
+  cosmos.bank.v1beta1.Metadata metadata = 2 [ (gogoproto.moretags) = "yaml:\"metadata\"", (gogoproto.nullable)   = false ];
+}
+```
+
+**State Modifications:**
+
+- Check that sender of the message is the admin of denom
+- Modify `AuthorityMetadata` state entry to change the admin of the denom
+
+## Expectations from the chain
+
+The chain's bech32 prefix for addresses can be at most 16 characters long.
+
+This comes from denoms having a 128 byte maximum length, enforced from the SDK,
+and us setting longest_subdenom to be 44 bytes.
+
+A token factory token's denom is: `factory/{creator address}/{subdenom}`
+
+Splitting up into sub-components, this has:
+
+- `len(factory) = 7`
+- `2 * len("/") = 2`
+- `len(longest_subdenom)`
+- `len(creator_address) = len(bech32(longest_addr_length, chain_addr_prefix))`.
+
+Longest addr length at the moment is `32 bytes`. Due to SDK error correction
+settings, this means `len(bech32(32, chain_addr_prefix)) = len(chain_addr_prefix) + 1 + 58`.
+Adding this all, we have a total length constraint of `128 = 7 + 2 + len(longest_subdenom) + len(longest_chain_addr_prefix) + 1 + 58`.
+Therefore `len(longest_subdenom) + len(longest_chain_addr_prefix) = 128 - (7 + 2 + 1 + 58) = 60`.
+
+The choice between how we standardized the split these 60 bytes between maxes
+from longest_subdenom and longest_chain_addr_prefix is somewhat arbitrary.
+Considerations going into this:
+
+- Per [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#bech32)
+  the technically longest HRP for a 32 byte address ('data field') is 31 bytes.
+  (Comes from encode(data) = 59 bytes, and max length = 90 bytes)
+- subdenom should be at least 32 bytes so hashes can go into it
+- longer subdenoms are very helpful for creating human readable denoms
+- chain addresses should prefer being smaller. The longest HRP in cosmos to date is 11 bytes. (`persistence`)
+
+For explicitness, its currently set to `len(longest_subdenom) = 44` and `len(longest_chain_addr_prefix) = 16`.
+
+Please note, if the SDK increases the maximum length of a denom from 128 bytes,
+these caps should increase.
+
+So please don't make code rely on these max lengths for parsing.

+ 334 - 0
wormchain/x/tokenfactory/bindings/custom_msg_test.go

@@ -0,0 +1,334 @@
+package bindings_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/CosmWasm/wasmd/x/wasm/keeper"
+	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/wormhole-foundation/wormchain/app"
+	bindings "github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings/types"
+	//"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func TestCreateDenomMsg(t *testing.T) {
+	creator := RandomAccountAddress()
+	osmosis, ctx := SetupCustomApp(t, creator)
+
+	lucky := RandomAccountAddress()
+	reflect := instantiateReflectContract(t, ctx, osmosis, lucky)
+	require.NotEmpty(t, reflect)
+
+	// Fund reflect contract with 100 base denom creation fees
+	//reflectAmount := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)))
+	//fundAccount(t, ctx, osmosis, reflect, reflectAmount)
+
+	msg := bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{
+		Subdenom: "SUN",
+	}}
+	err := executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+
+	// query the denom and see if it matches
+	/*query := bindings.TokenQuery{
+		FullDenom: &bindings.FullDenom{
+			CreatorAddr: reflect.String(),
+			Subdenom:    "SUN",
+		},
+	}
+	resp := bindings.FullDenomResponse{}
+	queryCustom(t, ctx, osmosis, reflect, query, &resp)
+
+	require.Equal(t, &resp.AuthorityMetadata.Admin, reflect.String())*/
+}
+
+func TestMintMsg(t *testing.T) {
+	creator := RandomAccountAddress()
+	osmosis, ctx := SetupCustomApp(t, creator)
+
+	lucky := RandomAccountAddress()
+	reflect := instantiateReflectContract(t, ctx, osmosis, lucky)
+	require.NotEmpty(t, reflect)
+
+	// Fund reflect contract with 100 base denom creation fees
+	//reflectAmount := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)))
+	//fundAccount(t, ctx, osmosis, reflect, reflectAmount)
+
+	// lucky was broke
+	balances := osmosis.BankKeeper.GetAllBalances(ctx, lucky)
+	require.Empty(t, balances)
+
+	// Create denom for minting
+	msg := bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{
+		Subdenom: "SUN",
+	}}
+	err := executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+	sunDenom := fmt.Sprintf("factory/%s/%s", reflect.String(), msg.CreateDenom.Subdenom)
+
+	amount, ok := sdk.NewIntFromString("808010808")
+	require.True(t, ok)
+	msg = bindings.TokenMsg{MintTokens: &bindings.MintTokens{
+		Denom:         sunDenom,
+		Amount:        amount,
+		MintToAddress: lucky.String(),
+	}}
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+
+	balances = osmosis.BankKeeper.GetAllBalances(ctx, lucky)
+	require.Len(t, balances, 1)
+	coin := balances[0]
+	require.Equal(t, amount, coin.Amount)
+	require.Contains(t, coin.Denom, "factory/")
+
+	// query the denom and see if it matches
+	/*query := bindings.TokenQuery{
+		FullDenom: &bindings.FullDenom{
+			CreatorAddr: reflect.String(),
+			Subdenom:    "SUN",
+		},
+	}
+	resp := bindings.FullDenomResponse{}
+	queryCustom(t, ctx, osmosis, reflect, query, &resp)
+
+	require.Equal(t, resp.Denom, coin.Denom)*/
+
+	// mint the same denom again
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+
+	balances = osmosis.BankKeeper.GetAllBalances(ctx, lucky)
+	require.Len(t, balances, 1)
+	coin = balances[0]
+	require.Equal(t, amount.MulRaw(2), coin.Amount)
+	require.Contains(t, coin.Denom, "factory/")
+
+	// query the denom and see if it matches
+	/*query = bindings.TokenQuery{
+		FullDenom: &bindings.FullDenom{
+			CreatorAddr: reflect.String(),
+			Subdenom:    "SUN",
+		},
+	}
+	resp = bindings.FullDenomResponse{}
+	queryCustom(t, ctx, osmosis, reflect, query, &resp)
+
+	require.Equal(t, resp.Denom, coin.Denom)*/
+
+	// now mint another amount / denom
+	// create it first
+	msg = bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{
+		Subdenom: "MOON",
+	}}
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+	moonDenom := fmt.Sprintf("factory/%s/%s", reflect.String(), msg.CreateDenom.Subdenom)
+
+	amount = amount.SubRaw(1)
+	msg = bindings.TokenMsg{MintTokens: &bindings.MintTokens{
+		Denom:         moonDenom,
+		Amount:        amount,
+		MintToAddress: lucky.String(),
+	}}
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+
+	balances = osmosis.BankKeeper.GetAllBalances(ctx, lucky)
+	require.Len(t, balances, 2)
+	coin = balances[0]
+	require.Equal(t, amount, coin.Amount)
+	require.Contains(t, coin.Denom, "factory/")
+
+	// query the denom and see if it matches
+	/*query = bindings.TokenQuery{
+		FullDenom: &bindings.FullDenom{
+			CreatorAddr: reflect.String(),
+			Subdenom:    "MOON",
+		},
+	}
+	resp = bindings.FullDenomResponse{}
+	queryCustom(t, ctx, osmosis, reflect, query, &resp)
+
+	require.Equal(t, resp.Denom, coin.Denom)*/
+
+	// and check the first denom is unchanged
+	coin = balances[1]
+	require.Equal(t, amount.AddRaw(1).MulRaw(2), coin.Amount)
+	require.Contains(t, coin.Denom, "factory/")
+
+	// query the denom and see if it matches
+	/*query = bindings.TokenQuery{
+		FullDenom: &bindings.FullDenom{
+			CreatorAddr: reflect.String(),
+			Subdenom:    "SUN",
+		},
+	}
+	resp = bindings.FullDenomResponse{}
+	queryCustom(t, ctx, osmosis, reflect, query, &resp)
+
+	require.Equal(t, resp.Denom, coin.Denom)*/
+}
+
+// Capability is disabled
+/*func TestForceTransfer(t *testing.T) {
+	creator := RandomAccountAddress()
+	osmosis, ctx := SetupCustomApp(t, creator)
+
+	lucky := RandomAccountAddress()
+	rcpt := RandomAccountAddress()
+	reflect := instantiateReflectContract(t, ctx, osmosis, lucky)
+	require.NotEmpty(t, reflect)
+
+	// Fund reflect contract with 100 base denom creation fees
+	//reflectAmount := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)))
+	//fundAccount(t, ctx, osmosis, reflect, reflectAmount)
+
+	// lucky was broke
+	balances := osmosis.BankKeeper.GetAllBalances(ctx, lucky)
+	require.Empty(t, balances)
+
+	// Create denom for minting
+	msg := bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{
+		Subdenom: "SUN",
+	}}
+	err := executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+	sunDenom := fmt.Sprintf("factory/%s/%s", reflect.String(), msg.CreateDenom.Subdenom)
+
+	amount, ok := sdk.NewIntFromString("808010808")
+	require.True(t, ok)
+
+	// Mint new tokens to lucky
+	msg = bindings.TokenMsg{MintTokens: &bindings.MintTokens{
+		Denom:         sunDenom,
+		Amount:        amount,
+		MintToAddress: lucky.String(),
+	}}
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+
+	// Force move 100 tokens from lucky to rcpt
+	msg = bindings.TokenMsg{ForceTransfer: &bindings.ForceTransfer{
+		Denom:       sunDenom,
+		Amount:      sdk.NewInt(100),
+		FromAddress: lucky.String(),
+		ToAddress:   rcpt.String(),
+	}}
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+
+	// check the balance of rcpt
+	balances = osmosis.BankKeeper.GetAllBalances(ctx, rcpt)
+	require.Len(t, balances, 1)
+	coin := balances[0]
+	require.Equal(t, sdk.NewInt(100), coin.Amount)
+}*/
+
+func TestBurnMsg(t *testing.T) {
+	creator := RandomAccountAddress()
+	osmosis, ctx := SetupCustomApp(t, creator)
+
+	lucky := RandomAccountAddress()
+	reflect := instantiateReflectContract(t, ctx, osmosis, lucky)
+	require.NotEmpty(t, reflect)
+
+	// Fund reflect contract with 100 base denom creation fees
+	//reflectAmount := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)))
+	//fundAccount(t, ctx, osmosis, reflect, reflectAmount)
+
+	// lucky was broke
+	balances := osmosis.BankKeeper.GetAllBalances(ctx, lucky)
+	require.Empty(t, balances)
+
+	// Create denom for minting
+	msg := bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{
+		Subdenom: "SUN",
+	}}
+	err := executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+	sunDenom := fmt.Sprintf("factory/%s/%s", reflect.String(), msg.CreateDenom.Subdenom)
+
+	amount, ok := sdk.NewIntFromString("808010809")
+	require.True(t, ok)
+
+	msg = bindings.TokenMsg{MintTokens: &bindings.MintTokens{
+		Denom:         sunDenom,
+		Amount:        amount,
+		MintToAddress: lucky.String(),
+	}}
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)
+
+	// can burn from different address with burnFrom
+	// Capability is disabled
+	/*amt, ok := sdk.NewIntFromString("1")
+	require.True(t, ok)
+	msg = bindings.TokenMsg{BurnTokens: &bindings.BurnTokens{
+		Denom:           sunDenom,
+		Amount:          amt,
+		BurnFromAddress: lucky.String(),
+	}}
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)*/
+
+	// lucky needs to send balance to reflect contract to burn it
+	// Capability is disabled
+	/*luckyBalance := osmosis.BankKeeper.GetAllBalances(ctx, lucky)
+	err = osmosis.BankKeeper.SendCoins(ctx, lucky, reflect, luckyBalance)
+	require.NoError(t, err)
+
+	msg = bindings.TokenMsg{BurnTokens: &bindings.BurnTokens{
+		Denom:           sunDenom,
+		Amount:          amount.Abs().Sub(sdk.NewInt(1)),
+		BurnFromAddress: reflect.String(),
+	}}
+	err = executeCustom(t, ctx, osmosis, reflect, lucky, msg, sdk.Coin{})
+	require.NoError(t, err)*/
+}
+
+type ReflectExec struct {
+	ReflectMsg    *ReflectMsgs    `json:"reflect_msg,omitempty"`
+	ReflectSubMsg *ReflectSubMsgs `json:"reflect_sub_msg,omitempty"`
+}
+
+type ReflectMsgs struct {
+	Msgs []wasmvmtypes.CosmosMsg `json:"msgs"`
+}
+
+type ReflectSubMsgs struct {
+	Msgs []wasmvmtypes.SubMsg `json:"msgs"`
+}
+
+func executeCustom(t *testing.T, ctx sdk.Context, osmosis *app.App, contract sdk.AccAddress, sender sdk.AccAddress, msg bindings.TokenMsg, funds sdk.Coin) error {
+	wrapped := bindings.TokenFactoryMsg{
+		Token: &msg,
+	}
+	customBz, err := json.Marshal(wrapped)
+	require.NoError(t, err)
+
+	reflectMsg := ReflectExec{
+		ReflectMsg: &ReflectMsgs{
+			Msgs: []wasmvmtypes.CosmosMsg{{
+				Custom: customBz,
+			}},
+		},
+	}
+	reflectBz, err := json.Marshal(reflectMsg)
+	require.NoError(t, err)
+
+	// no funds sent if amount is 0
+	var coins sdk.Coins
+	if !funds.Amount.IsNil() {
+		coins = sdk.Coins{funds}
+	}
+
+	contractKeeper := keeper.NewDefaultPermissionKeeper(osmosis.GetWasmKeeper())
+	_, err = contractKeeper.Execute(ctx, contract, sender, reflectBz, coins)
+	return err
+}

+ 74 - 0
wormchain/x/tokenfactory/bindings/custom_query_test.go

@@ -0,0 +1,74 @@
+package bindings_test
+
+import (
+	"encoding/json"
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/wormhole-foundation/wormchain/app"
+	bindings "github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings/types"
+)
+
+/*func TestQueryFullDenom(t *testing.T) {
+	actor := RandomAccountAddress()
+	tokenz, ctx := SetupCustomApp(t, actor)
+
+	reflect := instantiateReflectContract(t, ctx, tokenz, actor)
+	require.NotEmpty(t, reflect)
+
+	// query full denom
+	query := bindings.TokenQuery{
+		FullDenom: &bindings.FullDenom{
+			CreatorAddr: reflect.String(),
+			Subdenom:    "ustart",
+		},
+	}
+	resp := bindings.FullDenomResponse{}
+	queryCustom(t, ctx, tokenz, reflect, query, &resp)
+
+	expected := fmt.Sprintf("factory/%s/ustart", reflect.String())
+	require.EqualValues(t, expected, resp.Denom)
+}*/
+
+type ReflectQuery struct {
+	Chain *ChainRequest `json:"chain,omitempty"`
+}
+
+type ChainRequest struct {
+	Request wasmvmtypes.QueryRequest `json:"request"`
+}
+
+type ChainResponse struct {
+	Data []byte `json:"data"`
+}
+
+func queryCustom(t *testing.T, ctx sdk.Context, tokenz *app.App, contract sdk.AccAddress, request bindings.TokenQuery, response interface{}) {
+	wrapped := bindings.TokenFactoryQuery{
+		Token: &request,
+	}
+	msgBz, err := json.Marshal(wrapped)
+	require.NoError(t, err)
+	fmt.Println(string(msgBz))
+
+	query := ReflectQuery{
+		Chain: &ChainRequest{
+			Request: wasmvmtypes.QueryRequest{Custom: msgBz},
+		},
+	}
+	queryBz, err := json.Marshal(query)
+	require.NoError(t, err)
+	fmt.Println(string(queryBz))
+
+	resBz, err := tokenz.GetWasmKeeper().QuerySmart(ctx, contract, queryBz)
+	require.NoError(t, err)
+	var resp ChainResponse
+	err = json.Unmarshal(resBz, &resp)
+	require.NoError(t, err)
+	err = json.Unmarshal(resp.Data, response)
+	require.NoError(t, err)
+}

+ 93 - 0
wormchain/x/tokenfactory/bindings/helpers_test.go

@@ -0,0 +1,93 @@
+package bindings_test
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/tendermint/tendermint/crypto"
+	"github.com/tendermint/tendermint/crypto/ed25519"
+	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+
+	"github.com/cosmos/cosmos-sdk/simapp"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/CosmWasm/wasmd/x/wasm/keeper"
+	"github.com/wormhole-foundation/wormchain/app"
+	"github.com/wormhole-foundation/wormchain/app/apptesting"
+)
+
+func CreateTestInput(t *testing.T) (*app.App, sdk.Context) {
+	osmosis := apptesting.Setup(t, true, 0)
+	ctx := osmosis.BaseApp.NewContext(false, tmproto.Header{Height: 1, ChainID: "osmosis-1", Time: time.Now().UTC()})
+	return osmosis, ctx
+}
+
+func FundAccount(t *testing.T, ctx sdk.Context, osmosis *app.App, acct sdk.AccAddress) {
+	err := simapp.FundAccount(osmosis.BankKeeper, ctx, acct, sdk.NewCoins(
+		sdk.NewCoin("uosmo", sdk.NewInt(10000000000)),
+	))
+	require.NoError(t, err)
+}
+
+// we need to make this deterministic (same every test run), as content might affect gas costs
+func keyPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) {
+	key := ed25519.GenPrivKey()
+	pub := key.PubKey()
+	addr := sdk.AccAddress(pub.Address())
+	return key, pub, addr
+}
+
+func RandomAccountAddress() sdk.AccAddress {
+	_, _, addr := keyPubAddr()
+	return addr
+}
+
+func RandomBech32AccountAddress() string {
+	return RandomAccountAddress().String()
+}
+
+func storeReflectCode(t *testing.T, ctx sdk.Context, tokenz *app.App, addr sdk.AccAddress) uint64 {
+	wasmCode, err := os.ReadFile("./testdata/token_reflect.wasm")
+	require.NoError(t, err)
+
+	contractKeeper := keeper.NewDefaultPermissionKeeper(tokenz.GetWasmKeeper())
+	codeID, _, err := contractKeeper.Create(ctx, addr, wasmCode, nil)
+	require.NoError(t, err)
+
+	return codeID
+}
+
+func instantiateReflectContract(t *testing.T, ctx sdk.Context, tokenz *app.App, funder sdk.AccAddress) sdk.AccAddress {
+	initMsgBz := []byte("{}")
+	contractKeeper := keeper.NewDefaultPermissionKeeper(tokenz.GetWasmKeeper())
+	codeID := uint64(1)
+	addr, _, err := contractKeeper.Instantiate(ctx, codeID, funder, funder, initMsgBz, "demo contract", nil)
+	require.NoError(t, err)
+
+	return addr
+}
+
+func fundAccount(t *testing.T, ctx sdk.Context, tokenz *app.App, addr sdk.AccAddress, coins sdk.Coins) {
+	err := simapp.FundAccount(
+		tokenz.BankKeeper,
+		ctx,
+		addr,
+		coins,
+	)
+	require.NoError(t, err)
+}
+
+func SetupCustomApp(t *testing.T, addr sdk.AccAddress) (*app.App, sdk.Context) {
+	tokenz, ctx := CreateTestInput(t)
+	wasmKeeper := tokenz.GetWasmKeeper()
+
+	storeReflectCode(t, ctx, tokenz, addr)
+
+	cInfo := wasmKeeper.GetCodeInfo(ctx, 1)
+	require.NotNil(t, cInfo)
+
+	return tokenz, ctx
+}

+ 369 - 0
wormchain/x/tokenfactory/bindings/message_plugin.go

@@ -0,0 +1,369 @@
+package bindings
+
+import (
+	"encoding/json"
+
+	wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
+	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+	bindingstypes "github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings/types"
+	tokenfactorykeeper "github.com/wormhole-foundation/wormchain/x/tokenfactory/keeper"
+	tokenfactorytypes "github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+// CustomMessageDecorator returns decorator for custom CosmWasm bindings messages
+func CustomMessageDecorator(bank *bankkeeper.BaseKeeper, tokenFactory *tokenfactorykeeper.Keeper) func(wasmkeeper.Messenger) wasmkeeper.Messenger {
+	return func(old wasmkeeper.Messenger) wasmkeeper.Messenger {
+		return &CustomMessenger{
+			wrapped:      old,
+			bank:         bank,
+			tokenFactory: tokenFactory,
+		}
+	}
+}
+
+type CustomMessenger struct {
+	wrapped      wasmkeeper.Messenger
+	bank         *bankkeeper.BaseKeeper
+	tokenFactory *tokenfactorykeeper.Keeper
+}
+
+var _ wasmkeeper.Messenger = (*CustomMessenger)(nil)
+
+// DispatchMsg executes on the contractMsg.
+func (m *CustomMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddress, contractIBCPortID string, msg wasmvmtypes.CosmosMsg) ([]sdk.Event, [][]byte, error) {
+	if msg.Custom != nil {
+		// only handle the happy path where this is really creating / minting / swapping ...
+		// leave everything else for the wrapped version
+		var contractMsg bindingstypes.TokenFactoryMsg
+		if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil {
+			return nil, nil, sdkerrors.Wrap(err, "token factory msg")
+		}
+		if contractMsg.Token == nil {
+			return nil, nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "nil token field")
+		}
+		tokenMsg := contractMsg.Token
+
+		if tokenMsg.CreateDenom != nil {
+			return m.createDenom(ctx, contractAddr, tokenMsg.CreateDenom)
+		}
+		if tokenMsg.MintTokens != nil {
+			return m.mintTokens(ctx, contractAddr, tokenMsg.MintTokens)
+		}
+		if tokenMsg.ChangeAdmin != nil {
+			return m.changeAdmin(ctx, contractAddr, tokenMsg.ChangeAdmin)
+		}
+		if tokenMsg.BurnTokens != nil {
+			return m.burnTokens(ctx, contractAddr, tokenMsg.BurnTokens)
+		}
+		if tokenMsg.SetMetadata != nil {
+			return m.setMetadata(ctx, contractAddr, tokenMsg.SetMetadata)
+		}
+		if tokenMsg.ForceTransfer != nil {
+			return m.forceTransfer(ctx, contractAddr, tokenMsg.ForceTransfer)
+		}
+	}
+	return m.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg)
+}
+
+// createDenom creates a new token denom
+func (m *CustomMessenger) createDenom(ctx sdk.Context, contractAddr sdk.AccAddress, createDenom *bindingstypes.CreateDenom) ([]sdk.Event, [][]byte, error) {
+	bz, err := PerformCreateDenom(m.tokenFactory, m.bank, ctx, contractAddr, createDenom)
+	if err != nil {
+		return nil, nil, sdkerrors.Wrap(err, "perform create denom")
+	}
+	return nil, [][]byte{bz}, nil
+}
+
+// PerformCreateDenom is used with createDenom to create a token denom; validates the msgCreateDenom.
+func PerformCreateDenom(f *tokenfactorykeeper.Keeper, b *bankkeeper.BaseKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, createDenom *bindingstypes.CreateDenom) ([]byte, error) {
+	if createDenom == nil {
+		return nil, wasmvmtypes.InvalidRequest{Err: "create denom null create denom"}
+	}
+
+	msgServer := tokenfactorykeeper.NewMsgServerImpl(*f)
+
+	msgCreateDenom := tokenfactorytypes.NewMsgCreateDenom(contractAddr.String(), createDenom.Subdenom)
+
+	if err := msgCreateDenom.ValidateBasic(); err != nil {
+		return nil, sdkerrors.Wrap(err, "failed validating MsgCreateDenom")
+	}
+
+	// Create denom
+	resp, err := msgServer.CreateDenom(
+		sdk.WrapSDKContext(ctx),
+		msgCreateDenom,
+	)
+	if err != nil {
+		return nil, sdkerrors.Wrap(err, "creating denom")
+	}
+
+	if createDenom.Metadata != nil {
+		newDenom := resp.NewTokenDenom
+		err := PerformSetMetadata(f, b, ctx, contractAddr, newDenom, *createDenom.Metadata)
+		if err != nil {
+			return nil, sdkerrors.Wrap(err, "setting metadata")
+		}
+	}
+
+	return resp.Marshal()
+}
+
+// mintTokens mints tokens of a specified denom to an address.
+func (m *CustomMessenger) mintTokens(ctx sdk.Context, contractAddr sdk.AccAddress, mint *bindingstypes.MintTokens) ([]sdk.Event, [][]byte, error) {
+	err := PerformMint(m.tokenFactory, m.bank, ctx, contractAddr, mint)
+	if err != nil {
+		return nil, nil, sdkerrors.Wrap(err, "perform mint")
+	}
+	return nil, nil, nil
+}
+
+// PerformMint used with mintTokens to validate the mint message and mint through token factory.
+func PerformMint(f *tokenfactorykeeper.Keeper, b *bankkeeper.BaseKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, mint *bindingstypes.MintTokens) error {
+	if mint == nil {
+		return wasmvmtypes.InvalidRequest{Err: "mint token null mint"}
+	}
+	rcpt, err := parseAddress(mint.MintToAddress)
+	if err != nil {
+		return err
+	}
+
+	coin := sdk.Coin{Denom: mint.Denom, Amount: mint.Amount}
+	sdkMsg := tokenfactorytypes.NewMsgMint(contractAddr.String(), coin)
+
+	if err = sdkMsg.ValidateBasic(); err != nil {
+		return err
+	}
+
+	// Mint through token factory / message server
+	msgServer := tokenfactorykeeper.NewMsgServerImpl(*f)
+	_, err = msgServer.Mint(sdk.WrapSDKContext(ctx), sdkMsg)
+	if err != nil {
+		return sdkerrors.Wrap(err, "minting coins from message")
+	}
+
+	if b.BlockedAddr(rcpt) {
+		return sdkerrors.Wrapf(err, "minting coins to blocked address %s", rcpt.String())
+	}
+
+	err = b.SendCoins(ctx, contractAddr, rcpt, sdk.NewCoins(coin))
+	if err != nil {
+		return sdkerrors.Wrap(err, "sending newly minted coins from message")
+	}
+	return nil
+}
+
+// changeAdmin changes the admin.
+func (m *CustomMessenger) changeAdmin(ctx sdk.Context, contractAddr sdk.AccAddress, changeAdmin *bindingstypes.ChangeAdmin) ([]sdk.Event, [][]byte, error) {
+	err := ChangeAdmin(m.tokenFactory, ctx, contractAddr, changeAdmin)
+	if err != nil {
+		return nil, nil, sdkerrors.Wrap(err, "failed to change admin")
+	}
+	return nil, nil, nil
+}
+
+// ChangeAdmin is used with changeAdmin to validate changeAdmin messages and to dispatch.
+func ChangeAdmin(f *tokenfactorykeeper.Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, changeAdmin *bindingstypes.ChangeAdmin) error {
+	if changeAdmin == nil {
+		return wasmvmtypes.InvalidRequest{Err: "changeAdmin is nil"}
+	}
+	newAdminAddr, err := parseAddress(changeAdmin.NewAdminAddress)
+	if err != nil {
+		return err
+	}
+
+	changeAdminMsg := tokenfactorytypes.NewMsgChangeAdmin(contractAddr.String(), changeAdmin.Denom, newAdminAddr.String())
+	if err := changeAdminMsg.ValidateBasic(); err != nil {
+		return err
+	}
+
+	msgServer := tokenfactorykeeper.NewMsgServerImpl(*f)
+	_, err = msgServer.ChangeAdmin(sdk.WrapSDKContext(ctx), changeAdminMsg)
+	if err != nil {
+		return sdkerrors.Wrap(err, "failed changing admin from message")
+	}
+	return nil
+}
+
+// burnTokens burns tokens.
+func (m *CustomMessenger) burnTokens(ctx sdk.Context, contractAddr sdk.AccAddress, burn *bindingstypes.BurnTokens) ([]sdk.Event, [][]byte, error) {
+	err := PerformBurn(m.tokenFactory, ctx, contractAddr, burn)
+	if err != nil {
+		return nil, nil, sdkerrors.Wrap(err, "perform burn")
+	}
+	return nil, nil, nil
+}
+
+// PerformBurn performs token burning after validating tokenBurn message.
+func PerformBurn(f *tokenfactorykeeper.Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, burn *bindingstypes.BurnTokens) error {
+	if burn == nil {
+		return wasmvmtypes.InvalidRequest{Err: "burn token null mint"}
+	}
+
+	coin := sdk.Coin{Denom: burn.Denom, Amount: burn.Amount}
+	sdkMsg := tokenfactorytypes.NewMsgBurn(contractAddr.String(), coin)
+	if burn.BurnFromAddress != "" {
+		sdkMsg = tokenfactorytypes.NewMsgBurnFrom(contractAddr.String(), coin, burn.BurnFromAddress)
+	}
+
+	if err := sdkMsg.ValidateBasic(); err != nil {
+		return err
+	}
+
+	// Burn through token factory / message server
+	msgServer := tokenfactorykeeper.NewMsgServerImpl(*f)
+	_, err := msgServer.Burn(sdk.WrapSDKContext(ctx), sdkMsg)
+	if err != nil {
+		return sdkerrors.Wrap(err, "burning coins from message")
+	}
+	return nil
+}
+
+// forceTransfer moves tokens.
+func (m *CustomMessenger) forceTransfer(ctx sdk.Context, contractAddr sdk.AccAddress, forcetransfer *bindingstypes.ForceTransfer) ([]sdk.Event, [][]byte, error) {
+	err := PerformForceTransfer(m.tokenFactory, ctx, contractAddr, forcetransfer)
+	if err != nil {
+		return nil, nil, sdkerrors.Wrap(err, "perform force transfer")
+	}
+	return nil, nil, nil
+}
+
+// PerformForceTransfer performs token moving after validating tokenForceTransfer message.
+func PerformForceTransfer(f *tokenfactorykeeper.Keeper, ctx sdk.Context, contractAddr sdk.AccAddress, forcetransfer *bindingstypes.ForceTransfer) error {
+	if forcetransfer == nil {
+		return wasmvmtypes.InvalidRequest{Err: "force transfer null"}
+	}
+
+	_, err := parseAddress(forcetransfer.FromAddress)
+	if err != nil {
+		return err
+	}
+
+	_, err = parseAddress(forcetransfer.ToAddress)
+	if err != nil {
+		return err
+	}
+
+	coin := sdk.Coin{Denom: forcetransfer.Denom, Amount: forcetransfer.Amount}
+	sdkMsg := tokenfactorytypes.NewMsgForceTransfer(contractAddr.String(), coin, forcetransfer.FromAddress, forcetransfer.ToAddress)
+
+	if err := sdkMsg.ValidateBasic(); err != nil {
+		return err
+	}
+
+	// Transfer through token factory / message server
+	msgServer := tokenfactorykeeper.NewMsgServerImpl(*f)
+	_, err = msgServer.ForceTransfer(sdk.WrapSDKContext(ctx), sdkMsg)
+	if err != nil {
+		return sdkerrors.Wrap(err, "force transferring from message")
+	}
+	return nil
+}
+
+// createDenom creates a new token denom
+func (m *CustomMessenger) setMetadata(ctx sdk.Context, contractAddr sdk.AccAddress, setMetadata *bindingstypes.SetMetadata) ([]sdk.Event, [][]byte, error) {
+	err := PerformSetMetadata(m.tokenFactory, m.bank, ctx, contractAddr, setMetadata.Denom, setMetadata.Metadata)
+	if err != nil {
+		return nil, nil, sdkerrors.Wrap(err, "perform create denom")
+	}
+	return nil, nil, nil
+}
+
+// PerformSetMetadata is used with setMetadata to add new metadata
+// It also is called inside CreateDenom if optional metadata field is set
+func PerformSetMetadata(f *tokenfactorykeeper.Keeper, b *bankkeeper.BaseKeeper, ctx sdk.Context, contractAddr sdk.AccAddress, denom string, metadata bindingstypes.Metadata) error {
+	// ensure contract address is admin of denom
+	auth, err := f.GetAuthorityMetadata(ctx, denom)
+	if err != nil {
+		return err
+	}
+	if auth.Admin != contractAddr.String() {
+		return wasmvmtypes.InvalidRequest{Err: "only admin can set metadata"}
+	}
+
+	// ensure we are setting proper denom metadata (bank uses Base field, fill it if missing)
+	if metadata.Base == "" {
+		metadata.Base = denom
+	} else if metadata.Base != denom {
+		// this is the key that we set
+		return wasmvmtypes.InvalidRequest{Err: "Base must be the same as denom"}
+	}
+
+	// Create and validate the metadata
+	bankMetadata := WasmMetadataToSdk(metadata)
+	if err := bankMetadata.Validate(); err != nil {
+		return err
+	}
+
+	b.SetDenomMetaData(ctx, bankMetadata)
+	return nil
+}
+
+// GetFullDenom is a function, not method, so the message_plugin can use it
+func GetFullDenom(contract string, subDenom string) (string, error) {
+	// Address validation
+	if _, err := parseAddress(contract); err != nil {
+		return "", err
+	}
+	fullDenom, err := tokenfactorytypes.GetTokenDenom(contract, subDenom)
+	if err != nil {
+		return "", sdkerrors.Wrap(err, "validate sub-denom")
+	}
+
+	return fullDenom, nil
+}
+
+// parseAddress parses address from bech32 string and verifies its format.
+func parseAddress(addr string) (sdk.AccAddress, error) {
+	parsed, err := sdk.AccAddressFromBech32(addr)
+	if err != nil {
+		return nil, sdkerrors.Wrap(err, "address from bech32")
+	}
+	err = sdk.VerifyAddressFormat(parsed)
+	if err != nil {
+		return nil, sdkerrors.Wrap(err, "verify address format")
+	}
+	return parsed, nil
+}
+
+func WasmMetadataToSdk(metadata bindingstypes.Metadata) banktypes.Metadata {
+	denoms := []*banktypes.DenomUnit{}
+	for _, unit := range metadata.DenomUnits {
+		denoms = append(denoms, &banktypes.DenomUnit{
+			Denom:    unit.Denom,
+			Exponent: unit.Exponent,
+			Aliases:  unit.Aliases,
+		})
+	}
+	return banktypes.Metadata{
+		Description: metadata.Description,
+		Display:     metadata.Display,
+		Base:        metadata.Base,
+		Name:        metadata.Name,
+		Symbol:      metadata.Symbol,
+		DenomUnits:  denoms,
+	}
+}
+
+func SdkMetadataToWasm(metadata banktypes.Metadata) *bindingstypes.Metadata {
+	denoms := []bindingstypes.DenomUnit{}
+	for _, unit := range metadata.DenomUnits {
+		denoms = append(denoms, bindingstypes.DenomUnit{
+			Denom:    unit.Denom,
+			Exponent: unit.Exponent,
+			Aliases:  unit.Aliases,
+		})
+	}
+	return &bindingstypes.Metadata{
+		Description: metadata.Description,
+		Display:     metadata.Display,
+		Base:        metadata.Base,
+		Name:        metadata.Name,
+		Symbol:      metadata.Symbol,
+		DenomUnits:  denoms,
+	}
+}

+ 57 - 0
wormchain/x/tokenfactory/bindings/queries.go

@@ -0,0 +1,57 @@
+package bindings
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+
+	bindingstypes "github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings/types"
+	tokenfactorykeeper "github.com/wormhole-foundation/wormchain/x/tokenfactory/keeper"
+)
+
+type QueryPlugin struct {
+	bankKeeper         *bankkeeper.BaseKeeper
+	tokenFactoryKeeper *tokenfactorykeeper.Keeper
+}
+
+// NewQueryPlugin returns a reference to a new QueryPlugin.
+func NewQueryPlugin(b *bankkeeper.BaseKeeper, tfk *tokenfactorykeeper.Keeper) *QueryPlugin {
+	return &QueryPlugin{
+		bankKeeper:         b,
+		tokenFactoryKeeper: tfk,
+	}
+}
+
+// GetDenomAdmin is a query to get denom admin.
+func (qp QueryPlugin) GetDenomAdmin(ctx sdk.Context, denom string) (*bindingstypes.AdminResponse, error) {
+	metadata, err := qp.tokenFactoryKeeper.GetAuthorityMetadata(ctx, denom)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get admin for denom: %s", denom)
+	}
+	return &bindingstypes.AdminResponse{Admin: metadata.Admin}, nil
+}
+
+func (qp QueryPlugin) GetDenomsByCreator(ctx sdk.Context, creator string) (*bindingstypes.DenomsByCreatorResponse, error) {
+	// TODO: validate creator address
+	denoms := qp.tokenFactoryKeeper.GetDenomsFromCreator(ctx, creator)
+	return &bindingstypes.DenomsByCreatorResponse{Denoms: denoms}, nil
+}
+
+func (qp QueryPlugin) GetMetadata(ctx sdk.Context, denom string) (*bindingstypes.MetadataResponse, error) {
+	metadata, found := qp.bankKeeper.GetDenomMetaData(ctx, denom)
+	var parsed *bindingstypes.Metadata
+	if found {
+		parsed = SdkMetadataToWasm(metadata)
+	}
+	return &bindingstypes.MetadataResponse{Metadata: parsed}, nil
+}
+
+func (qp QueryPlugin) GetParams(ctx sdk.Context) (*bindingstypes.ParamsResponse, error) {
+	params := qp.tokenFactoryKeeper.GetParams(ctx)
+	return &bindingstypes.ParamsResponse{
+		Params: bindingstypes.Params{
+			DenomCreationFee: ConvertSdkCoinsToWasmCoins(params.DenomCreationFee),
+		},
+	}, nil
+}

+ 122 - 0
wormchain/x/tokenfactory/bindings/query_plugin.go

@@ -0,0 +1,122 @@
+package bindings
+
+import (
+	"encoding/json"
+	"fmt"
+
+	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+
+	bindingstypes "github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings/types"
+)
+
+// CustomQuerier dispatches custom CosmWasm bindings queries.
+func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessage) ([]byte, error) {
+	return func(ctx sdk.Context, request json.RawMessage) ([]byte, error) {
+		var contractQuery bindingstypes.TokenFactoryQuery
+		if err := json.Unmarshal(request, &contractQuery); err != nil {
+			return nil, sdkerrors.Wrap(err, "osmosis query")
+		}
+		if contractQuery.Token == nil {
+			return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "nil token field")
+		}
+		tokenQuery := contractQuery.Token
+
+		switch {
+		case tokenQuery.FullDenom != nil:
+			creator := tokenQuery.FullDenom.CreatorAddr
+			subdenom := tokenQuery.FullDenom.Subdenom
+
+			fullDenom, err := GetFullDenom(creator, subdenom)
+			if err != nil {
+				return nil, sdkerrors.Wrap(err, "osmo full denom query")
+			}
+
+			res := bindingstypes.FullDenomResponse{
+				Denom: fullDenom,
+			}
+
+			bz, err := json.Marshal(res)
+			if err != nil {
+				return nil, sdkerrors.Wrap(err, "failed to marshal FullDenomResponse")
+			}
+
+			return bz, nil
+
+		case tokenQuery.Admin != nil:
+			res, err := qp.GetDenomAdmin(ctx, tokenQuery.Admin.Denom)
+			if err != nil {
+				return nil, err
+			}
+
+			bz, err := json.Marshal(res)
+			if err != nil {
+				return nil, fmt.Errorf("failed to JSON marshal AdminResponse: %w", err)
+			}
+
+			return bz, nil
+
+		case tokenQuery.Metadata != nil:
+			res, err := qp.GetMetadata(ctx, tokenQuery.Metadata.Denom)
+			if err != nil {
+				return nil, err
+			}
+
+			bz, err := json.Marshal(res)
+			if err != nil {
+				return nil, fmt.Errorf("failed to JSON marshal MetadataResponse: %w", err)
+			}
+
+			return bz, nil
+
+		case tokenQuery.DenomsByCreator != nil:
+			res, err := qp.GetDenomsByCreator(ctx, tokenQuery.DenomsByCreator.Creator)
+			if err != nil {
+				return nil, err
+			}
+
+			bz, err := json.Marshal(res)
+			if err != nil {
+				return nil, fmt.Errorf("failed to JSON marshal DenomsByCreatorResponse: %w", err)
+			}
+
+			return bz, nil
+
+		case tokenQuery.Params != nil:
+			res, err := qp.GetParams(ctx)
+			if err != nil {
+				return nil, err
+			}
+
+			bz, err := json.Marshal(res)
+			if err != nil {
+				return nil, fmt.Errorf("failed to JSON marshal ParamsResponse: %w", err)
+			}
+
+			return bz, nil
+
+		default:
+			return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown token query variant"}
+		}
+	}
+}
+
+// ConvertSdkCoinsToWasmCoins converts sdk type coins to wasm vm type coins
+func ConvertSdkCoinsToWasmCoins(coins []sdk.Coin) wasmvmtypes.Coins {
+	var toSend wasmvmtypes.Coins
+	for _, coin := range coins {
+		c := ConvertSdkCoinToWasmCoin(coin)
+		toSend = append(toSend, c)
+	}
+	return toSend
+}
+
+// ConvertSdkCoinToWasmCoin converts a sdk type coin to a wasm vm type coin
+func ConvertSdkCoinToWasmCoin(coin sdk.Coin) wasmvmtypes.Coin {
+	return wasmvmtypes.Coin{
+		Denom: coin.Denom,
+		// Note: tokenfactory tokens have 18 decimal places, so 10^22 is common, no longer in u64 range
+		Amount: coin.Amount.String(),
+	}
+}

+ 19 - 0
wormchain/x/tokenfactory/bindings/testdata/download_releases.sh

@@ -0,0 +1,19 @@
+#!/bin/bash
+set -o errexit -o nounset -o pipefail
+command -v shellcheck > /dev/null && shellcheck "$0"
+
+if [ $# -ne 1 ]; then
+  echo "Usage: ./download_releases.sh RELEASE_TAG"
+  exit 1
+fi
+
+tag="$1"
+
+# From CosmosContracts/token-bindings
+
+url="https://github.com/CosmWasm/token-bindings/releases/download/$tag/token_reflect.wasm"
+echo "Downloading $url ..."
+wget -O "token_reflect.wasm" "$url"
+
+rm -f version.txt
+echo "$tag" >version.txt

BIN
wormchain/x/tokenfactory/bindings/testdata/token_reflect.wasm


+ 1 - 0
wormchain/x/tokenfactory/bindings/testdata/version.txt

@@ -0,0 +1 @@
+v0.9.0

+ 68 - 0
wormchain/x/tokenfactory/bindings/types/msg.go

@@ -0,0 +1,68 @@
+package types
+
+import sdk "github.com/cosmos/cosmos-sdk/types"
+
+type TokenFactoryMsg struct {
+	Token *TokenMsg `json:"token,omitempty"`
+}
+
+type TokenMsg struct {
+	/// Contracts can create denoms, namespaced under the contract's address.
+	/// A contract may create any number of independent sub-denoms.
+	CreateDenom *CreateDenom `json:"create_denom,omitempty"`
+	/// Contracts can change the admin of a denom that they are the admin of.
+	ChangeAdmin *ChangeAdmin `json:"change_admin,omitempty"`
+	/// Contracts can mint native tokens for an existing factory denom
+	/// that they are the admin of.
+	MintTokens *MintTokens `json:"mint_tokens,omitempty"`
+	/// Contracts can burn native tokens for an existing factory denom
+	/// that they are the admin of.
+	/// Currently, the burn from address must be the admin contract.
+	BurnTokens *BurnTokens `json:"burn_tokens,omitempty"`
+	/// Sets the metadata on a denom which the contract controls
+	SetMetadata *SetMetadata `json:"set_metadata,omitempty"`
+	/// Forces a transfer of tokens from one address to another.
+	ForceTransfer *ForceTransfer `json:"force_transfer,omitempty"`
+}
+
+// CreateDenom creates a new factory denom, of denomination:
+// factory/{creating contract address}/{Subdenom}
+// Subdenom can be of length at most 44 characters, in [0-9a-zA-Z./]
+// The (creating contract address, subdenom) pair must be unique.
+// The created denom's admin is the creating contract address,
+// but this admin can be changed using the ChangeAdmin binding.
+type CreateDenom struct {
+	Subdenom string    `json:"subdenom"`
+	Metadata *Metadata `json:"metadata,omitempty"`
+}
+
+// ChangeAdmin changes the admin for a factory denom.
+// If the NewAdminAddress is empty, the denom has no admin.
+type ChangeAdmin struct {
+	Denom           string `json:"denom"`
+	NewAdminAddress string `json:"new_admin_address"`
+}
+
+type MintTokens struct {
+	Denom         string  `json:"denom"`
+	Amount        sdk.Int `json:"amount"`
+	MintToAddress string  `json:"mint_to_address"`
+}
+
+type BurnTokens struct {
+	Denom           string  `json:"denom"`
+	Amount          sdk.Int `json:"amount"`
+	BurnFromAddress string  `json:"burn_from_address"`
+}
+
+type SetMetadata struct {
+	Denom    string   `json:"denom"`
+	Metadata Metadata `json:"metadata"`
+}
+
+type ForceTransfer struct {
+	Denom       string  `json:"denom"`
+	Amount      sdk.Int `json:"amount"`
+	FromAddress string  `json:"from_address"`
+	ToAddress   string  `json:"to_address"`
+}

+ 59 - 0
wormchain/x/tokenfactory/bindings/types/query.go

@@ -0,0 +1,59 @@
+package types
+
+type TokenFactoryQuery struct {
+	Token *TokenQuery `json:"token,omitempty"`
+}
+
+// See https://github.com/CosmWasm/token-bindings/blob/main/packages/bindings/src/query.rs
+type TokenQuery struct {
+	/// Given a subdenom minted by a contract via `OsmosisMsg::MintTokens`,
+	/// returns the full denom as used by `BankMsg::Send`.
+	FullDenom       *FullDenom       `json:"full_denom,omitempty"`
+	Admin           *DenomAdmin      `json:"admin,omitempty"`
+	Metadata        *GetMetadata     `json:"metadata,omitempty"`
+	DenomsByCreator *DenomsByCreator `json:"denoms_by_creator,omitempty"`
+	Params          *GetParams       `json:"params,omitempty"`
+}
+
+// query types
+
+type FullDenom struct {
+	CreatorAddr string `json:"creator_addr"`
+	Subdenom    string `json:"subdenom"`
+}
+
+type GetMetadata struct {
+	Denom string `json:"denom"`
+}
+
+type DenomAdmin struct {
+	Denom string `json:"denom"`
+}
+
+type DenomsByCreator struct {
+	Creator string `json:"creator"`
+}
+
+type GetParams struct{}
+
+// responses
+
+type FullDenomResponse struct {
+	Denom string `json:"denom"`
+}
+
+type AdminResponse struct {
+	Admin string `json:"admin"`
+}
+
+type MetadataResponse struct {
+	Metadata *Metadata `json:"metadata,omitempty"`
+}
+
+type DenomsByCreatorResponse struct {
+	Denoms []string `json:"denoms"`
+}
+
+type ParamsResponse struct {
+	Params Params `json:"params"`
+}

+ 37 - 0
wormchain/x/tokenfactory/bindings/types/types.go

@@ -0,0 +1,37 @@
+package types
+
+import (
+	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
+)
+
+type Metadata struct {
+	Description string `json:"description"`
+	// DenomUnits represents the list of DenomUnit's for a given coin
+	DenomUnits []DenomUnit `json:"denom_units"`
+	// Base represents the base denom (should be the DenomUnit with exponent = 0).
+	Base string `json:"base"`
+	// Display indicates the suggested denom that should be displayed in clients.
+	Display string `json:"display"`
+	// Name defines the name of the token (eg: Cosmos Atom)
+	Name string `json:"name"`
+	// Symbol is the token symbol usually shown on exchanges (eg: ATOM).
+	// This can be the same as the display.
+	Symbol string `json:"symbol"`
+}
+
+type DenomUnit struct {
+	// Denom represents the string name of the given denom unit (e.g uatom).
+	Denom string `json:"denom"`
+	// Exponent represents power of 10 exponent that one must
+	// raise the base_denom to in order to equal the given DenomUnit's denom
+	// 1 denom = 1^exponent base_denom
+	// (e.g. with a base_denom of uatom, one can create a DenomUnit of 'atom' with
+	// exponent = 6, thus: 1 atom = 10^6 uatom).
+	Exponent uint32 `json:"exponent"`
+	// Aliases is a list of string aliases for the given denom
+	Aliases []string `json:"aliases"`
+}
+
+type Params struct {
+	DenomCreationFee []wasmvmtypes.Coin `json:"denom_creation_fee"`
+}

+ 452 - 0
wormchain/x/tokenfactory/bindings/validate_msg_test.go

@@ -0,0 +1,452 @@
+package bindings_test
+
+import (
+	"fmt"
+	"testing"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+
+	wasmbinding "github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings"
+	bindings "github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings/types"
+
+	//"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+
+	"github.com/stretchr/testify/require"
+)
+
+func TestCreateDenom(t *testing.T) {
+	actor := RandomAccountAddress()
+	tokenz, ctx := SetupCustomApp(t, actor)
+
+	// Fund actor with 100 base denom creation fees
+	//actorAmount := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)))
+	//fundAccount(t, ctx, tokenz, actor, actorAmount)
+
+	specs := map[string]struct {
+		createDenom *bindings.CreateDenom
+		expErr      bool
+	}{
+		"valid sub-denom": {
+			createDenom: &bindings.CreateDenom{
+				Subdenom: "MOON",
+			},
+		},
+		"empty sub-denom": {
+			createDenom: &bindings.CreateDenom{
+				Subdenom: "",
+			},
+			expErr: false,
+		},
+		"invalid sub-denom": {
+			createDenom: &bindings.CreateDenom{
+				Subdenom: "sub-denom_2",
+			},
+			expErr: true,
+		},
+		"null create denom": {
+			createDenom: nil,
+			expErr:      true,
+		},
+	}
+	for name, spec := range specs {
+		t.Run(name, func(t *testing.T) {
+			// when
+
+			var bankBaseKeeper bankkeeper.BaseKeeper
+			bankBaseKeeper, ok := tokenz.BankKeeper.(bankkeeper.BaseKeeper)
+			if !ok {
+				panic("Cannot cast bank keeper to bank basekeeper")
+			}
+			_, gotErr := wasmbinding.PerformCreateDenom(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, actor, spec.createDenom)
+			// then
+			if spec.expErr {
+				require.Error(t, gotErr)
+				return
+			}
+			require.NoError(t, gotErr)
+		})
+	}
+}
+
+func TestChangeAdmin(t *testing.T) {
+	const validDenom = "validdenom"
+
+	tokenCreator := RandomAccountAddress()
+
+	specs := map[string]struct {
+		actor       sdk.AccAddress
+		changeAdmin *bindings.ChangeAdmin
+
+		expErrMsg string
+	}{
+		"valid": {
+			changeAdmin: &bindings.ChangeAdmin{
+				Denom:           fmt.Sprintf("factory/%s/%s", tokenCreator.String(), validDenom),
+				NewAdminAddress: RandomBech32AccountAddress(),
+			},
+			actor: tokenCreator,
+		},
+		"typo in factory in denom name": {
+			changeAdmin: &bindings.ChangeAdmin{
+				Denom:           fmt.Sprintf("facory/%s/%s", tokenCreator.String(), validDenom),
+				NewAdminAddress: RandomBech32AccountAddress(),
+			},
+			actor:     tokenCreator,
+			expErrMsg: "denom prefix is incorrect. Is: facory.  Should be: factory: invalid denom",
+		},
+		"invalid address in denom": {
+			changeAdmin: &bindings.ChangeAdmin{
+				Denom:           fmt.Sprintf("factory/%s/%s", RandomBech32AccountAddress(), validDenom),
+				NewAdminAddress: RandomBech32AccountAddress(),
+			},
+			actor:     tokenCreator,
+			expErrMsg: "failed changing admin from message: unauthorized account",
+		},
+		"other denom name in 3 part name": {
+			changeAdmin: &bindings.ChangeAdmin{
+				Denom:           fmt.Sprintf("factory/%s/%s", tokenCreator.String(), "invalid denom"),
+				NewAdminAddress: RandomBech32AccountAddress(),
+			},
+			actor:     tokenCreator,
+			expErrMsg: fmt.Sprintf("invalid denom: factory/%s/invalid denom", tokenCreator.String()),
+		},
+		"empty denom": {
+			changeAdmin: &bindings.ChangeAdmin{
+				Denom:           "",
+				NewAdminAddress: RandomBech32AccountAddress(),
+			},
+			actor:     tokenCreator,
+			expErrMsg: "invalid denom: ",
+		},
+		"empty address": {
+			changeAdmin: &bindings.ChangeAdmin{
+				Denom:           fmt.Sprintf("factory/%s/%s", tokenCreator.String(), validDenom),
+				NewAdminAddress: "",
+			},
+			actor:     tokenCreator,
+			expErrMsg: "address from bech32: empty address string is not allowed",
+		},
+		"creator is a different address": {
+			changeAdmin: &bindings.ChangeAdmin{
+				Denom:           fmt.Sprintf("factory/%s/%s", tokenCreator.String(), validDenom),
+				NewAdminAddress: RandomBech32AccountAddress(),
+			},
+			actor:     RandomAccountAddress(),
+			expErrMsg: "failed changing admin from message: unauthorized account",
+		},
+		"change to the same address": {
+			changeAdmin: &bindings.ChangeAdmin{
+				Denom:           fmt.Sprintf("factory/%s/%s", tokenCreator.String(), validDenom),
+				NewAdminAddress: tokenCreator.String(),
+			},
+			actor: tokenCreator,
+		},
+		"nil binding": {
+			actor:     tokenCreator,
+			expErrMsg: "invalid request: changeAdmin is nil - original request: ",
+		},
+	}
+	for name, spec := range specs {
+		t.Run(name, func(t *testing.T) {
+			// Setup
+			tokenz, ctx := SetupCustomApp(t, tokenCreator)
+
+			// Fund actor with 100 base denom creation fees
+			//actorAmount := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)))
+			//fundAccount(t, ctx, tokenz, tokenCreator, actorAmount)
+
+			var bankBaseKeeper bankkeeper.BaseKeeper
+			bankBaseKeeper, ok := tokenz.BankKeeper.(bankkeeper.BaseKeeper)
+			if !ok {
+				panic("Cannot cast bank keeper to bank basekeeper")
+			}
+			_, err := wasmbinding.PerformCreateDenom(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, tokenCreator, &bindings.CreateDenom{
+				Subdenom: validDenom,
+			})
+			require.NoError(t, err)
+
+			err = wasmbinding.ChangeAdmin(&tokenz.TokenFactoryKeeper, ctx, spec.actor, spec.changeAdmin)
+			if len(spec.expErrMsg) > 0 {
+				require.Error(t, err)
+				actualErrMsg := err.Error()
+				require.Equal(t, spec.expErrMsg, actualErrMsg)
+				return
+			}
+			require.NoError(t, err)
+		})
+	}
+}
+
+func TestMint(t *testing.T) {
+	creator := RandomAccountAddress()
+	tokenz, ctx := SetupCustomApp(t, creator)
+
+	// Fund actor with 100 base denom creation fees
+	//tokenCreationFeeAmt := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)))
+	//fundAccount(t, ctx, tokenz, creator, tokenCreationFeeAmt)
+
+	// Create denoms for valid mint tests
+	validDenom := bindings.CreateDenom{
+		Subdenom: "MOON",
+	}
+
+	var bankBaseKeeper bankkeeper.BaseKeeper
+	bankBaseKeeper, ok := tokenz.BankKeeper.(bankkeeper.BaseKeeper)
+	if !ok {
+		panic("Cannot cast bank keeper to bank basekeeper")
+	}
+	_, err := wasmbinding.PerformCreateDenom(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, creator, &validDenom)
+	require.NoError(t, err)
+
+	emptyDenom := bindings.CreateDenom{
+		Subdenom: "",
+	}
+	_, err = wasmbinding.PerformCreateDenom(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, creator, &emptyDenom)
+	require.NoError(t, err)
+
+	validDenomStr := fmt.Sprintf("factory/%s/%s", creator.String(), validDenom.Subdenom)
+	emptyDenomStr := fmt.Sprintf("factory/%s/%s", creator.String(), emptyDenom.Subdenom)
+
+	lucky := RandomAccountAddress()
+
+	// lucky was broke
+	balances := tokenz.BankKeeper.GetAllBalances(ctx, lucky)
+	require.Empty(t, balances)
+
+	amount, ok := sdk.NewIntFromString("8080")
+	require.True(t, ok)
+
+	specs := map[string]struct {
+		mint   *bindings.MintTokens
+		expErr bool
+	}{
+		"valid mint": {
+			mint: &bindings.MintTokens{
+				Denom:         validDenomStr,
+				Amount:        amount,
+				MintToAddress: lucky.String(),
+			},
+		},
+		"empty sub-denom": {
+			mint: &bindings.MintTokens{
+				Denom:         emptyDenomStr,
+				Amount:        amount,
+				MintToAddress: lucky.String(),
+			},
+			expErr: false,
+		},
+		"nonexistent sub-denom": {
+			mint: &bindings.MintTokens{
+				Denom:         fmt.Sprintf("factory/%s/%s", creator.String(), "SUN"),
+				Amount:        amount,
+				MintToAddress: lucky.String(),
+			},
+			expErr: true,
+		},
+		"invalid sub-denom": {
+			mint: &bindings.MintTokens{
+				Denom:         "sub-denom_2",
+				Amount:        amount,
+				MintToAddress: lucky.String(),
+			},
+			expErr: true,
+		},
+		"zero amount": {
+			mint: &bindings.MintTokens{
+				Denom:         validDenomStr,
+				Amount:        sdk.ZeroInt(),
+				MintToAddress: lucky.String(),
+			},
+			expErr: true,
+		},
+		"negative amount": {
+			mint: &bindings.MintTokens{
+				Denom:         validDenomStr,
+				Amount:        amount.Neg(),
+				MintToAddress: lucky.String(),
+			},
+			expErr: true,
+		},
+		"empty recipient": {
+			mint: &bindings.MintTokens{
+				Denom:         validDenomStr,
+				Amount:        amount,
+				MintToAddress: "",
+			},
+			expErr: true,
+		},
+		"invalid recipient": {
+			mint: &bindings.MintTokens{
+				Denom:         validDenomStr,
+				Amount:        amount,
+				MintToAddress: "invalid",
+			},
+			expErr: true,
+		},
+		"null mint": {
+			mint:   nil,
+			expErr: true,
+		},
+	}
+	for name, spec := range specs {
+		t.Run(name, func(t *testing.T) {
+			// when
+
+			var bankBaseKeeper bankkeeper.BaseKeeper
+			bankBaseKeeper, ok := tokenz.BankKeeper.(bankkeeper.BaseKeeper)
+			if !ok {
+				panic("Cannot cast bank keeper to bank basekeeper")
+			}
+			gotErr := wasmbinding.PerformMint(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, creator, spec.mint)
+			// then
+			if spec.expErr {
+				require.Error(t, gotErr)
+				return
+			}
+			require.NoError(t, gotErr)
+		})
+	}
+}
+
+// Capability not enabled as all test use the burn from capability
+/*func TestBurn(t *testing.T) {
+	creator := RandomAccountAddress()
+	tokenz, ctx := SetupCustomApp(t, creator)
+
+	// Fund actor with 100 base denom creation fees
+	//tokenCreationFeeAmt := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)))
+	//fundAccount(t, ctx, tokenz, creator, tokenCreationFeeAmt)
+
+	// Create denoms for valid burn tests
+	validDenom := bindings.CreateDenom{
+		Subdenom: "MOON",
+	}
+
+	var bankBaseKeeper bankkeeper.BaseKeeper
+	bankBaseKeeper, ok := tokenz.BankKeeper.(bankkeeper.BaseKeeper)
+	if !ok {
+		panic("Cannot cast bank keeper to bank basekeeper")
+	}
+	_, err := wasmbinding.PerformCreateDenom(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, creator, &validDenom)
+	require.NoError(t, err)
+
+	emptyDenom := bindings.CreateDenom{
+		Subdenom: "",
+	}
+	_, err = wasmbinding.PerformCreateDenom(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, creator, &emptyDenom)
+	require.NoError(t, err)
+
+	lucky := RandomAccountAddress()
+
+	// lucky was broke
+	balances := tokenz.BankKeeper.GetAllBalances(ctx, lucky)
+	require.Empty(t, balances)
+
+	validDenomStr := fmt.Sprintf("factory/%s/%s", creator.String(), validDenom.Subdenom)
+	emptyDenomStr := fmt.Sprintf("factory/%s/%s", creator.String(), emptyDenom.Subdenom)
+	mintAmount, ok := sdk.NewIntFromString("8080")
+	require.True(t, ok)
+
+	specs := map[string]struct {
+		burn   *bindings.BurnTokens
+		expErr bool
+	}{
+		"valid burn": {
+			burn: &bindings.BurnTokens{
+				Denom:           validDenomStr,
+				Amount:          mintAmount,
+				BurnFromAddress: creator.String(),
+			},
+			expErr: false,
+		},
+		"non admin address": {
+			burn: &bindings.BurnTokens{
+				Denom:           validDenomStr,
+				Amount:          mintAmount,
+				BurnFromAddress: lucky.String(),
+			},
+			expErr: true,
+		},
+		"empty sub-denom": {
+			burn: &bindings.BurnTokens{
+				Denom:           emptyDenomStr,
+				Amount:          mintAmount,
+				BurnFromAddress: creator.String(),
+			},
+			expErr: false,
+		},
+		"invalid sub-denom": {
+			burn: &bindings.BurnTokens{
+				Denom:           "sub-denom_2",
+				Amount:          mintAmount,
+				BurnFromAddress: creator.String(),
+			},
+			expErr: true,
+		},
+		"non-minted denom": {
+			burn: &bindings.BurnTokens{
+				Denom:           fmt.Sprintf("factory/%s/%s", creator.String(), "SUN"),
+				Amount:          mintAmount,
+				BurnFromAddress: creator.String(),
+			},
+			expErr: true,
+		},
+		"zero amount": {
+			burn: &bindings.BurnTokens{
+				Denom:           validDenomStr,
+				Amount:          sdk.ZeroInt(),
+				BurnFromAddress: creator.String(),
+			},
+			expErr: true,
+		},
+		"negative amount": {
+			burn:   nil,
+			expErr: true,
+		},
+		"null burn": {
+			burn: &bindings.BurnTokens{
+				Denom:           validDenomStr,
+				Amount:          mintAmount.Neg(),
+				BurnFromAddress: creator.String(),
+			},
+			expErr: true,
+		},
+	}
+
+	for name, spec := range specs {
+		t.Run(name, func(t *testing.T) {
+			// Mint valid denom str and empty denom string for burn test
+			mintBinding := &bindings.MintTokens{
+				Denom:         validDenomStr,
+				Amount:        mintAmount,
+				MintToAddress: creator.String(),
+			}
+
+			var bankBaseKeeper bankkeeper.BaseKeeper
+			bankBaseKeeper, ok := tokenz.BankKeeper.(bankkeeper.BaseKeeper)
+			if !ok {
+				panic("Cannot cast bank keeper to bank basekeeper")
+			}
+			err := wasmbinding.PerformMint(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, creator, mintBinding)
+			require.NoError(t, err)
+
+			emptyDenomMintBinding := &bindings.MintTokens{
+				Denom:         emptyDenomStr,
+				Amount:        mintAmount,
+				MintToAddress: creator.String(),
+			}
+			err = wasmbinding.PerformMint(&tokenz.TokenFactoryKeeper, &bankBaseKeeper, ctx, creator, emptyDenomMintBinding)
+			require.NoError(t, err)
+
+			// when
+			gotErr := wasmbinding.PerformBurn(&tokenz.TokenFactoryKeeper, ctx, creator, spec.burn)
+			// then
+			if spec.expErr {
+				require.Error(t, gotErr)
+				return
+			}
+			require.NoError(t, gotErr)
+		})
+	}
+}*/

+ 121 - 0
wormchain/x/tokenfactory/bindings/validate_queries_test.go

@@ -0,0 +1,121 @@
+package bindings_test
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+
+	wasmbinding "github.com/wormhole-foundation/wormchain/x/tokenfactory/bindings"
+)
+
+func TestFullDenom(t *testing.T) {
+	actor := RandomAccountAddress()
+
+	specs := map[string]struct {
+		addr         string
+		subdenom     string
+		expFullDenom string
+		expErr       bool
+	}{
+		"valid address": {
+			addr:         actor.String(),
+			subdenom:     "subDenom1",
+			expFullDenom: fmt.Sprintf("factory/%s/subDenom1", actor.String()),
+		},
+		"empty address": {
+			addr:     "",
+			subdenom: "subDenom1",
+			expErr:   true,
+		},
+		"invalid address": {
+			addr:     "invalid",
+			subdenom: "subDenom1",
+			expErr:   true,
+		},
+		"empty sub-denom": {
+			addr:         actor.String(),
+			subdenom:     "",
+			expFullDenom: fmt.Sprintf("factory/%s/", actor.String()),
+		},
+		"invalid sub-denom (contains underscore)": {
+			addr:     actor.String(),
+			subdenom: "sub_denom",
+			expErr:   true,
+		},
+	}
+	for name, spec := range specs {
+		t.Run(name, func(t *testing.T) {
+			// when
+			gotFullDenom, gotErr := wasmbinding.GetFullDenom(spec.addr, spec.subdenom)
+			// then
+			if spec.expErr {
+				require.Error(t, gotErr)
+				return
+			}
+			require.NoError(t, gotErr)
+			assert.Equal(t, spec.expFullDenom, gotFullDenom, "exp %s but got %s", spec.expFullDenom, gotFullDenom)
+		})
+	}
+}
+
+func TestDenomAdmin(t *testing.T) {
+	addr := RandomAccountAddress()
+	app, ctx := SetupCustomApp(t, addr)
+
+	// set token creation fee to zero to make testing easier
+	tfParams := app.TokenFactoryKeeper.GetParams(ctx)
+	tfParams.DenomCreationFee = sdk.NewCoins()
+	app.TokenFactoryKeeper.SetParams(ctx, tfParams)
+
+	// create a subdenom via the token factory
+	admin := sdk.AccAddress([]byte("addr1_______________"))
+	tfDenom, err := app.TokenFactoryKeeper.CreateDenom(ctx, admin.String(), "subdenom")
+	require.NoError(t, err)
+	require.NotEmpty(t, tfDenom)
+
+	var bankBaseKeeper bankkeeper.BaseKeeper
+	bankBaseKeeper, ok := app.BankKeeper.(bankkeeper.BaseKeeper)
+	if !ok {
+		panic("Cannot cast bank keeper to bank basekeeper")
+	}
+	queryPlugin := wasmbinding.NewQueryPlugin(&bankBaseKeeper, &app.TokenFactoryKeeper)
+
+	testCases := []struct {
+		name        string
+		denom       string
+		expectErr   bool
+		expectAdmin string
+	}{
+		{
+			name:        "valid token factory denom",
+			denom:       tfDenom,
+			expectAdmin: admin.String(),
+		},
+		{
+			name:        "invalid token factory denom",
+			denom:       "uosmo",
+			expectErr:   false,
+			expectAdmin: "",
+		},
+	}
+
+	for _, tc := range testCases {
+		tc := tc
+
+		t.Run(tc.name, func(t *testing.T) {
+			resp, err := queryPlugin.GetDenomAdmin(ctx, tc.denom)
+			if tc.expectErr {
+				require.Error(t, err)
+			} else {
+				require.NoError(t, err)
+				require.NotNil(t, resp)
+				require.Equal(t, tc.expectAdmin, resp.Admin)
+			}
+		})
+	}
+}

+ 30 - 0
wormchain/x/tokenfactory/bindings/wasm.go

@@ -0,0 +1,30 @@
+package bindings
+
+import (
+	"github.com/CosmWasm/wasmd/x/wasm"
+
+	wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
+
+	bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
+	tokenfactorykeeper "github.com/wormhole-foundation/wormchain/x/tokenfactory/keeper"
+)
+
+func RegisterCustomPlugins(
+	bank *bankkeeper.BaseKeeper,
+	tokenFactory *tokenfactorykeeper.Keeper,
+) []wasmkeeper.Option {
+	// Disabling tokenfactory custom querier because wormchain custom querier exists
+	//wasmQueryPlugin := NewQueryPlugin(bank, tokenFactory)
+
+	//queryPluginOpt := wasmkeeper.WithQueryPlugins(&wasmkeeper.QueryPlugins{
+	//	Custom: CustomQuerier(wasmQueryPlugin),
+	//})
+	messengerDecoratorOpt := wasmkeeper.WithMessageHandlerDecorator(
+		CustomMessageDecorator(bank, tokenFactory),
+	)
+
+	return []wasm.Option{
+		//	queryPluginOpt,
+		messengerDecoratorOpt,
+	}
+}

+ 116 - 0
wormchain/x/tokenfactory/client/cli/query.go

@@ -0,0 +1,116 @@
+package cli
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+// GetQueryCmd returns the cli query commands for this module
+func GetQueryCmd() *cobra.Command {
+	// Group tokenfactory queries under a subcommand
+	cmd := &cobra.Command{
+		Use:                        types.ModuleName,
+		Short:                      fmt.Sprintf("Querying commands for the %s module", types.ModuleName),
+		DisableFlagParsing:         true,
+		SuggestionsMinimumDistance: 2,
+		RunE:                       client.ValidateCmd,
+	}
+
+	cmd.AddCommand(
+		GetParams(),
+		GetCmdDenomAuthorityMetadata(),
+		GetCmdDenomsFromCreator(),
+	)
+
+	return cmd
+}
+
+// GetParams returns the params for the module
+func GetParams() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "params [flags]",
+		Short: "Get the params for the x/tokenfactory module",
+		Args:  cobra.ExactArgs(0),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+			queryClient := types.NewQueryClient(clientCtx)
+
+			res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{})
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+
+	return cmd
+}
+
+// GetCmdDenomAuthorityMetadata returns the authority metadata for a queried denom
+func GetCmdDenomAuthorityMetadata() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "denom-authority-metadata [denom] [flags]",
+		Short: "Get the authority metadata for a specific denom",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+			queryClient := types.NewQueryClient(clientCtx)
+
+			res, err := queryClient.DenomAuthorityMetadata(cmd.Context(), &types.QueryDenomAuthorityMetadataRequest{
+				Denom: args[0],
+			})
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+
+	return cmd
+}
+
+// GetCmdDenomsFromCreator a command to get a list of all tokens created by a specific creator address
+func GetCmdDenomsFromCreator() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "denoms-from-creator [creator address] [flags]",
+		Short: "Returns a list of all tokens created by a specific creator address",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientQueryContext(cmd)
+			if err != nil {
+				return err
+			}
+			queryClient := types.NewQueryClient(clientCtx)
+
+			res, err := queryClient.DenomsFromCreator(cmd.Context(), &types.QueryDenomsFromCreatorRequest{
+				Creator: args[0],
+			})
+			if err != nil {
+				return err
+			}
+
+			return clientCtx.PrintProto(res)
+		},
+	}
+
+	flags.AddQueryFlagsToCmd(cmd)
+
+	return cmd
+}

+ 220 - 0
wormchain/x/tokenfactory/client/cli/tx.go

@@ -0,0 +1,220 @@
+package cli
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+
+	"github.com/spf13/cobra"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/client/flags"
+	"github.com/cosmos/cosmos-sdk/client/tx"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/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,
+	}
+
+	cmd.AddCommand(
+		NewCreateDenomCmd(),
+		NewMintCmd(),
+		NewBurnCmd(),
+		NewChangeAdminCmd(),
+		NewModifyDenomMetadataCmd(),
+	)
+
+	return cmd
+}
+
+// NewCreateDenomCmd broadcast MsgCreateDenom
+func NewCreateDenomCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "create-denom [subdenom] [flags]",
+		Short: "create a new denom from an account",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever)
+
+			msg := types.NewMsgCreateDenom(
+				clientCtx.GetFromAddress().String(),
+				args[0],
+			)
+
+			return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+	return cmd
+}
+
+// NewMintCmd broadcast MsgMint
+func NewMintCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "mint [amount] [flags]",
+		Short: "Mint a denom to your address. Must have admin authority to do so.",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever)
+
+			amount, err := sdk.ParseCoinNormalized(args[0])
+			if err != nil {
+				return err
+			}
+
+			msg := types.NewMsgMint(
+				clientCtx.GetFromAddress().String(),
+				amount,
+			)
+
+			return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+	return cmd
+}
+
+// NewBurnCmd broadcast MsgBurn
+func NewBurnCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "burn [amount] [flags]",
+		Short: "Burn tokens from an address. Must have admin authority to do so.",
+		Args:  cobra.ExactArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever)
+
+			amount, err := sdk.ParseCoinNormalized(args[0])
+			if err != nil {
+				return err
+			}
+
+			msg := types.NewMsgBurn(
+				clientCtx.GetFromAddress().String(),
+				amount,
+			)
+
+			return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+	return cmd
+}
+
+// NewChangeAdminCmd broadcast MsgChangeAdmin
+func NewChangeAdminCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "change-admin [denom] [new-admin-address] [flags]",
+		Short: "Changes the admin address for a factory-created denom. Must have admin authority to do so.",
+		Args:  cobra.ExactArgs(2),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever)
+
+			msg := types.NewMsgChangeAdmin(
+				clientCtx.GetFromAddress().String(),
+				args[0],
+				args[1],
+			)
+
+			return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+	return cmd
+}
+
+// NewModifyDenomMetadataCmd broadcast a Bank Metadata modification transaction
+func NewModifyDenomMetadataCmd() *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "modify-metadata [denom] [ticker-symbol] [description] [exponent] [flags]",
+		Short: "Changes the base data for frontends to query the data of.",
+		Args:  cobra.ExactArgs(4),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			clientCtx, err := client.GetClientTxContext(cmd)
+			if err != nil {
+				return err
+			}
+
+			txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever)
+
+			fullDenom, ticker, desc := args[0], strings.ToUpper(args[1]), args[2]
+
+			if !strings.HasPrefix(fullDenom, "factory/") {
+				return fmt.Errorf("denom must start with factory/")
+			}
+
+			if len(ticker) == 0 {
+				return fmt.Errorf("ticker cannot be empty")
+			}
+
+			// Exponent Checks
+			exponent, err := strconv.ParseUint(args[3], 10, 32)
+			if err != nil {
+				return err
+			}
+
+			bankMetadata := banktypes.Metadata{
+				Description: desc,
+				Display:     fullDenom,
+				Symbol:      ticker,
+				Name:        fullDenom,
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    fullDenom,
+						Exponent: 0, // must be 0 for the base denom
+						Aliases:  []string{ticker},
+					},
+					{
+						Denom:    ticker,
+						Exponent: uint32(exponent),
+						Aliases:  []string{fullDenom},
+					},
+				},
+				Base: fullDenom,
+			}
+
+			msg := types.NewMsgSetDenomMetadata(
+				clientCtx.GetFromAddress().String(),
+				bankMetadata,
+			)
+
+			return tx.GenerateOrBroadcastTxWithFactory(clientCtx, txf, msg)
+		},
+	}
+
+	flags.AddTxFlagsToCmd(cmd)
+	return cmd
+}

+ 49 - 0
wormchain/x/tokenfactory/keeper/admins.go

@@ -0,0 +1,49 @@
+package keeper
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/gogo/protobuf/proto"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+// GetAuthorityMetadata returns the authority metadata for a specific denom
+func (k Keeper) GetAuthorityMetadata(ctx sdk.Context, denom string) (types.DenomAuthorityMetadata, error) {
+	bz := k.GetDenomPrefixStore(ctx, denom).Get([]byte(types.DenomAuthorityMetadataKey))
+
+	metadata := types.DenomAuthorityMetadata{}
+	err := proto.Unmarshal(bz, &metadata)
+	if err != nil {
+		return types.DenomAuthorityMetadata{}, err
+	}
+	return metadata, nil
+}
+
+// setAuthorityMetadata stores authority metadata for a specific denom
+func (k Keeper) setAuthorityMetadata(ctx sdk.Context, denom string, metadata types.DenomAuthorityMetadata) error {
+	err := metadata.Validate()
+	if err != nil {
+		return err
+	}
+
+	store := k.GetDenomPrefixStore(ctx, denom)
+
+	bz, err := proto.Marshal(&metadata)
+	if err != nil {
+		return err
+	}
+
+	store.Set([]byte(types.DenomAuthorityMetadataKey), bz)
+	return nil
+}
+
+func (k Keeper) setAdmin(ctx sdk.Context, denom string, admin string) error {
+	metadata, err := k.GetAuthorityMetadata(ctx, denom)
+	if err != nil {
+		return err
+	}
+
+	metadata.Admin = admin
+
+	return k.setAuthorityMetadata(ctx, denom, metadata)
+}

+ 525 - 0
wormchain/x/tokenfactory/keeper/admins_test.go

@@ -0,0 +1,525 @@
+package keeper_test
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	//banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func (suite *KeeperTestSuite) TestAdminMsgs() {
+	addr0bal := int64(0)
+	addr1bal := int64(0)
+
+	bankKeeper := suite.App.BankKeeper
+
+	suite.CreateDefaultDenom()
+	// Make sure that the admin is set correctly
+	queryRes, err := suite.queryClient.DenomAuthorityMetadata(suite.Ctx.Context(), &types.QueryDenomAuthorityMetadataRequest{
+		Denom: suite.defaultDenom,
+	})
+	suite.Require().NoError(err)
+	suite.Require().Equal(suite.TestAccs[0].String(), queryRes.AuthorityMetadata.Admin)
+
+	// Test minting to admins own account
+	_, err = suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMint(suite.TestAccs[0].String(), sdk.NewInt64Coin(suite.defaultDenom, 10)))
+	addr0bal += 10
+	suite.Require().NoError(err)
+	suite.Require().True(bankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], suite.defaultDenom).Amount.Int64() == addr0bal, bankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], suite.defaultDenom))
+
+	// Test minting to a different account
+	_, err = suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMintTo(suite.TestAccs[0].String(), sdk.NewInt64Coin(suite.defaultDenom, 10), suite.TestAccs[1].String()))
+	addr1bal += 10
+	suite.Require().NoError(err)
+	suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], suite.defaultDenom).Amount.Int64() == addr1bal, suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], suite.defaultDenom))
+
+	// Test force transferring
+	// Capability is disabled
+	/*_, err = suite.msgServer.ForceTransfer(sdk.WrapSDKContext(suite.Ctx), types.NewMsgForceTransfer(suite.TestAccs[0].String(), sdk.NewInt64Coin(suite.defaultDenom, 5), suite.TestAccs[1].String(), suite.TestAccs[0].String()))
+	addr1bal -= 5
+	addr0bal += 5
+	suite.Require().NoError(err)
+	suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], suite.defaultDenom).Amount.Int64() == addr0bal, suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], suite.defaultDenom))
+	suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], suite.defaultDenom).Amount.Int64() == addr1bal, suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], suite.defaultDenom))*/
+
+	// Test burning from own account
+	_, err = suite.msgServer.Burn(sdk.WrapSDKContext(suite.Ctx), types.NewMsgBurn(suite.TestAccs[0].String(), sdk.NewInt64Coin(suite.defaultDenom, 5)))
+	suite.Require().NoError(err)
+	suite.Require().True(bankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], suite.defaultDenom).Amount.Int64() == addr1bal)
+
+	// Test Change Admin
+	_, err = suite.msgServer.ChangeAdmin(sdk.WrapSDKContext(suite.Ctx), types.NewMsgChangeAdmin(suite.TestAccs[0].String(), suite.defaultDenom, suite.TestAccs[1].String()))
+	suite.Require().NoError(err)
+	queryRes, err = suite.queryClient.DenomAuthorityMetadata(suite.Ctx.Context(), &types.QueryDenomAuthorityMetadataRequest{
+		Denom: suite.defaultDenom,
+	})
+	suite.Require().NoError(err)
+	suite.Require().Equal(suite.TestAccs[1].String(), queryRes.AuthorityMetadata.Admin)
+
+	// Make sure old admin can no longer do actions
+	_, err = suite.msgServer.Burn(sdk.WrapSDKContext(suite.Ctx), types.NewMsgBurn(suite.TestAccs[0].String(), sdk.NewInt64Coin(suite.defaultDenom, 5)))
+	suite.Require().Error(err)
+
+	// Make sure the new admin works
+	_, err = suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMint(suite.TestAccs[1].String(), sdk.NewInt64Coin(suite.defaultDenom, 5)))
+	addr1bal += 5
+	suite.Require().NoError(err)
+	suite.Require().True(bankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], suite.defaultDenom).Amount.Int64() == addr1bal)
+
+	// Try setting admin to empty
+	_, err = suite.msgServer.ChangeAdmin(sdk.WrapSDKContext(suite.Ctx), types.NewMsgChangeAdmin(suite.TestAccs[1].String(), suite.defaultDenom, ""))
+	suite.Require().NoError(err)
+	queryRes, err = suite.queryClient.DenomAuthorityMetadata(suite.Ctx.Context(), &types.QueryDenomAuthorityMetadataRequest{
+		Denom: suite.defaultDenom,
+	})
+	suite.Require().NoError(err)
+	suite.Require().Equal("", queryRes.AuthorityMetadata.Admin)
+}
+
+// TestMintDenom ensures the following properties of the MintMessage:
+// * Noone can mint tokens for a denom that doesn't exist
+// * Only the admin of a denom can mint tokens for it
+// * The admin of a denom can mint tokens for it
+func (suite *KeeperTestSuite) TestMintDenom() {
+	balances := make(map[string]int64)
+	for _, acc := range suite.TestAccs {
+		balances[acc.String()] = 0
+	}
+
+	// Create a denom
+	suite.CreateDefaultDenom()
+
+	for _, tc := range []struct {
+		desc       string
+		mintMsg    types.MsgMint
+		expectPass bool
+	}{
+		{
+			desc: "denom does not exist",
+			mintMsg: *types.NewMsgMint(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin("factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/evmos", 10),
+			),
+			expectPass: false,
+		},
+		{
+			desc: "mint is not by the admin",
+			mintMsg: *types.NewMsgMintTo(
+				suite.TestAccs[1].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10),
+				suite.TestAccs[0].String(),
+			),
+			expectPass: false,
+		},
+		{
+			desc: "success case - mint to self",
+			mintMsg: *types.NewMsgMint(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10),
+			),
+			expectPass: true,
+		},
+		{
+			desc: "success case - mint to another address",
+			mintMsg: *types.NewMsgMintTo(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10),
+				suite.TestAccs[1].String(),
+			),
+			expectPass: true,
+		},
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			_, err := suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), &tc.mintMsg)
+			if tc.expectPass {
+				suite.Require().NoError(err)
+				balances[tc.mintMsg.MintToAddress] += tc.mintMsg.Amount.Amount.Int64()
+			} else {
+				suite.Require().Error(err)
+			}
+
+			mintToAddr, _ := sdk.AccAddressFromBech32(tc.mintMsg.MintToAddress)
+			bal := suite.App.BankKeeper.GetBalance(suite.Ctx, mintToAddr, suite.defaultDenom).Amount
+			suite.Require().Equal(bal.Int64(), balances[tc.mintMsg.MintToAddress])
+		})
+	}
+}
+
+func (suite *KeeperTestSuite) TestBurnDenom() {
+	// Create a denom.
+	suite.CreateDefaultDenom()
+
+	// mint 1000 default token for all testAccs
+	balances := make(map[string]int64)
+	for _, acc := range suite.TestAccs {
+		_, err := suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMintTo(suite.TestAccs[0].String(), sdk.NewInt64Coin(suite.defaultDenom, 1000), acc.String()))
+		suite.Require().NoError(err)
+		balances[acc.String()] = 1000
+	}
+
+	for _, tc := range []struct {
+		desc       string
+		burnMsg    types.MsgBurn
+		expectPass bool
+	}{
+		{
+			desc: "denom does not exist",
+			burnMsg: *types.NewMsgBurn(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin("factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/evmos", 10),
+			),
+			expectPass: false,
+		},
+		{
+			desc: "burn is not by the admin",
+			burnMsg: *types.NewMsgBurnFrom(
+				suite.TestAccs[1].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10),
+				suite.TestAccs[0].String(),
+			),
+			expectPass: false,
+		},
+		{
+			desc: "burn more than balance",
+			burnMsg: *types.NewMsgBurn(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10000),
+			),
+			expectPass: false,
+		},
+		{
+			desc: "success case - burn from self",
+			burnMsg: *types.NewMsgBurn(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10),
+			),
+			expectPass: true,
+		},
+		/*{
+			desc: "success case - burn from another address",
+			burnMsg: *types.NewMsgBurnFrom(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10),
+				suite.TestAccs[1].String(),
+			),
+			expectPass: true,
+		},*/
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			_, err := suite.msgServer.Burn(sdk.WrapSDKContext(suite.Ctx), &tc.burnMsg)
+			if tc.expectPass {
+				suite.Require().NoError(err)
+				balances[tc.burnMsg.BurnFromAddress] -= tc.burnMsg.Amount.Amount.Int64()
+			} else {
+				suite.Require().Error(err)
+			}
+
+			burnFromAddr, _ := sdk.AccAddressFromBech32(tc.burnMsg.BurnFromAddress)
+			bal := suite.App.BankKeeper.GetBalance(suite.Ctx, burnFromAddr, suite.defaultDenom).Amount
+			suite.Require().Equal(bal.Int64(), balances[tc.burnMsg.BurnFromAddress])
+		})
+	}
+}
+
+// Capability is disabled
+/*func (suite *KeeperTestSuite) TestForceTransferDenom() {
+	// Create a denom.
+	suite.CreateDefaultDenom()
+
+	// mint 1000 default token for all testAccs
+	balances := make(map[string]int64)
+	for _, acc := range suite.TestAccs {
+		_, err := suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMintTo(suite.TestAccs[0].String(), sdk.NewInt64Coin(suite.defaultDenom, 1000), acc.String()))
+		suite.Require().NoError(err)
+		balances[acc.String()] = 1000
+	}
+
+	for _, tc := range []struct {
+		desc             string
+		forceTransferMsg types.MsgForceTransfer
+		expectPass       bool
+	}{
+		{
+			desc: "valid force transfer",
+			forceTransferMsg: *types.NewMsgForceTransfer(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10),
+				suite.TestAccs[1].String(),
+				suite.TestAccs[2].String(),
+			),
+			expectPass: true,
+		},
+		{
+			desc: "denom does not exist",
+			forceTransferMsg: *types.NewMsgForceTransfer(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin("factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/evmos", 10),
+				suite.TestAccs[1].String(),
+				suite.TestAccs[2].String(),
+			),
+			expectPass: false,
+		},
+		{
+			desc: "forceTransfer is not by the admin",
+			forceTransferMsg: *types.NewMsgForceTransfer(
+				suite.TestAccs[1].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10),
+				suite.TestAccs[1].String(),
+				suite.TestAccs[2].String(),
+			),
+			expectPass: false,
+		},
+		{
+			desc: "forceTransfer is greater than the balance of",
+			forceTransferMsg: *types.NewMsgForceTransfer(
+				suite.TestAccs[0].String(),
+				sdk.NewInt64Coin(suite.defaultDenom, 10000),
+				suite.TestAccs[1].String(),
+				suite.TestAccs[2].String(),
+			),
+			expectPass: false,
+		},
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			_, err := suite.msgServer.ForceTransfer(sdk.WrapSDKContext(suite.Ctx), &tc.forceTransferMsg)
+			if tc.expectPass {
+				suite.Require().NoError(err)
+
+				balances[tc.forceTransferMsg.TransferFromAddress] -= tc.forceTransferMsg.Amount.Amount.Int64()
+				balances[tc.forceTransferMsg.TransferToAddress] += tc.forceTransferMsg.Amount.Amount.Int64()
+			} else {
+				suite.Require().Error(err)
+			}
+
+			fromAddr, err := sdk.AccAddressFromBech32(tc.forceTransferMsg.TransferFromAddress)
+			suite.Require().NoError(err)
+			fromBal := suite.App.BankKeeper.GetBalance(suite.Ctx, fromAddr, suite.defaultDenom).Amount
+			suite.Require().True(fromBal.Int64() == balances[tc.forceTransferMsg.TransferFromAddress])
+
+			toAddr, err := sdk.AccAddressFromBech32(tc.forceTransferMsg.TransferToAddress)
+			suite.Require().NoError(err)
+			toBal := suite.App.BankKeeper.GetBalance(suite.Ctx, toAddr, suite.defaultDenom).Amount
+			suite.Require().True(toBal.Int64() == balances[tc.forceTransferMsg.TransferToAddress])
+		})
+	}
+}*/
+
+func (suite *KeeperTestSuite) TestChangeAdminDenom() {
+	for _, tc := range []struct {
+		desc                    string
+		msgChangeAdmin          func(denom string) *types.MsgChangeAdmin
+		expectedChangeAdminPass bool
+		expectedAdminIndex      int
+		msgMint                 func(denom string) *types.MsgMint
+		expectedMintPass        bool
+	}{
+		{
+			desc: "creator admin can't mint after setting to '' ",
+			msgChangeAdmin: func(denom string) *types.MsgChangeAdmin {
+				return types.NewMsgChangeAdmin(suite.TestAccs[0].String(), denom, "")
+			},
+			expectedChangeAdminPass: true,
+			expectedAdminIndex:      -1,
+			msgMint: func(denom string) *types.MsgMint {
+				return types.NewMsgMint(suite.TestAccs[0].String(), sdk.NewInt64Coin(denom, 5))
+			},
+			expectedMintPass: false,
+		},
+		{
+			desc: "non-admins can't change the existing admin",
+			msgChangeAdmin: func(denom string) *types.MsgChangeAdmin {
+				return types.NewMsgChangeAdmin(suite.TestAccs[1].String(), denom, suite.TestAccs[2].String())
+			},
+			expectedChangeAdminPass: false,
+			expectedAdminIndex:      0,
+		},
+		{
+			desc: "success change admin",
+			msgChangeAdmin: func(denom string) *types.MsgChangeAdmin {
+				return types.NewMsgChangeAdmin(suite.TestAccs[0].String(), denom, suite.TestAccs[1].String())
+			},
+			expectedAdminIndex:      1,
+			expectedChangeAdminPass: true,
+			msgMint: func(denom string) *types.MsgMint {
+				return types.NewMsgMint(suite.TestAccs[1].String(), sdk.NewInt64Coin(denom, 5))
+			},
+			expectedMintPass: true,
+		},
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			// setup test
+			suite.SetupTest()
+
+			// Create a denom and mint
+			res, err := suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
+			suite.Require().NoError(err)
+
+			testDenom := res.GetNewTokenDenom()
+
+			_, err = suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMint(suite.TestAccs[0].String(), sdk.NewInt64Coin(testDenom, 10)))
+			suite.Require().NoError(err)
+
+			_, err = suite.msgServer.ChangeAdmin(sdk.WrapSDKContext(suite.Ctx), tc.msgChangeAdmin(testDenom))
+			if tc.expectedChangeAdminPass {
+				suite.Require().NoError(err)
+			} else {
+				suite.Require().Error(err)
+			}
+
+			queryRes, err := suite.queryClient.DenomAuthorityMetadata(suite.Ctx.Context(), &types.QueryDenomAuthorityMetadataRequest{
+				Denom: testDenom,
+			})
+			suite.Require().NoError(err)
+
+			// expectedAdminIndex with negative value is assumed as admin with value of ""
+			const emptyStringAdminIndexFlag = -1
+			if tc.expectedAdminIndex == emptyStringAdminIndexFlag {
+				suite.Require().Equal("", queryRes.AuthorityMetadata.Admin)
+			} else {
+				suite.Require().Equal(suite.TestAccs[tc.expectedAdminIndex].String(), queryRes.AuthorityMetadata.Admin)
+			}
+
+			// we test mint to test if admin authority is performed properly after admin change.
+			if tc.msgMint != nil {
+				_, err := suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), tc.msgMint(testDenom))
+				if tc.expectedMintPass {
+					suite.Require().NoError(err)
+				} else {
+					suite.Require().Error(err)
+				}
+			}
+		})
+	}
+}
+
+// Capability is diabled
+/*func (suite *KeeperTestSuite) TestSetDenomMetaData() {
+	// setup test
+	suite.SetupTest()
+	suite.CreateDefaultDenom()
+
+	for _, tc := range []struct {
+		desc                string
+		msgSetDenomMetadata types.MsgSetDenomMetadata
+		expectedPass        bool
+	}{
+		{
+			desc: "successful set denom metadata",
+			msgSetDenomMetadata: *types.NewMsgSetDenomMetadata(suite.TestAccs[0].String(), banktypes.Metadata{
+				Description: "yeehaw",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    suite.defaultDenom,
+						Exponent: 0,
+					},
+					{
+						Denom:    "uosmo",
+						Exponent: 6,
+					},
+				},
+				Base:    suite.defaultDenom,
+				Display: "uosmo",
+				Name:    "OSMO",
+				Symbol:  "OSMO",
+			}),
+			expectedPass: true,
+		},
+		{
+			desc: "non existent factory denom name",
+			msgSetDenomMetadata: *types.NewMsgSetDenomMetadata(suite.TestAccs[0].String(), banktypes.Metadata{
+				Description: "yeehaw",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    fmt.Sprintf("factory/%s/litecoin", suite.TestAccs[0].String()),
+						Exponent: 0,
+					},
+					{
+						Denom:    "uosmo",
+						Exponent: 6,
+					},
+				},
+				Base:    fmt.Sprintf("factory/%s/litecoin", suite.TestAccs[0].String()),
+				Display: "uosmo",
+				Name:    "OSMO",
+				Symbol:  "OSMO",
+			}),
+			expectedPass: false,
+		},
+		{
+			desc: "non-factory denom",
+			msgSetDenomMetadata: *types.NewMsgSetDenomMetadata(suite.TestAccs[0].String(), banktypes.Metadata{
+				Description: "yeehaw",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    "uosmo",
+						Exponent: 0,
+					},
+					{
+						Denom:    "uosmoo",
+						Exponent: 6,
+					},
+				},
+				Base:    "uosmo",
+				Display: "uosmoo",
+				Name:    "OSMO",
+				Symbol:  "OSMO",
+			}),
+			expectedPass: false,
+		},
+		{
+			desc: "wrong admin",
+			msgSetDenomMetadata: *types.NewMsgSetDenomMetadata(suite.TestAccs[1].String(), banktypes.Metadata{
+				Description: "yeehaw",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    suite.defaultDenom,
+						Exponent: 0,
+					},
+					{
+						Denom:    "uosmo",
+						Exponent: 6,
+					},
+				},
+				Base:    suite.defaultDenom,
+				Display: "uosmo",
+				Name:    "OSMO",
+				Symbol:  "OSMO",
+			}),
+			expectedPass: false,
+		},
+		{
+			desc: "invalid metadata (missing display denom unit)",
+			msgSetDenomMetadata: *types.NewMsgSetDenomMetadata(suite.TestAccs[0].String(), banktypes.Metadata{
+				Description: "yeehaw",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    suite.defaultDenom,
+						Exponent: 0,
+					},
+				},
+				Base:    suite.defaultDenom,
+				Display: "uosmo",
+				Name:    "OSMO",
+				Symbol:  "OSMO",
+			}),
+			expectedPass: false,
+		},
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			bankKeeper := suite.App.BankKeeper
+			res, err := suite.msgServer.SetDenomMetadata(sdk.WrapSDKContext(suite.Ctx), &tc.msgSetDenomMetadata)
+			if tc.expectedPass {
+				suite.Require().NoError(err)
+				suite.Require().NotNil(res)
+
+				md, found := bankKeeper.GetDenomMetaData(suite.Ctx, suite.defaultDenom)
+				suite.Require().True(found)
+				suite.Require().Equal(tc.msgSetDenomMetadata.Metadata.Name, md.Name)
+			} else {
+				suite.Require().Error(err)
+			}
+		})
+	}
+}*/

+ 86 - 0
wormchain/x/tokenfactory/keeper/bankactions.go

@@ -0,0 +1,86 @@
+package keeper
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func (k Keeper) mintTo(ctx sdk.Context, amount sdk.Coin, mintTo string) error {
+	// verify that denom is an x/tokenfactory denom
+	_, _, err := types.DeconstructDenom(amount.Denom)
+	if err != nil {
+		return err
+	}
+
+	err = k.bankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins(amount))
+	if err != nil {
+		return err
+	}
+
+	addr, err := sdk.AccAddressFromBech32(mintTo)
+	if err != nil {
+		return err
+	}
+
+	if k.bankKeeper.BlockedAddr(addr) {
+		return fmt.Errorf("failed to mint to blocked address: %s", addr)
+	}
+
+	return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName,
+		addr,
+		sdk.NewCoins(amount))
+}
+
+func (k Keeper) burnFrom(ctx sdk.Context, amount sdk.Coin, burnFrom string) error {
+	// verify that denom is an x/tokenfactory denom
+	_, _, err := types.DeconstructDenom(amount.Denom)
+	if err != nil {
+		return err
+	}
+
+	addr, err := sdk.AccAddressFromBech32(burnFrom)
+	if err != nil {
+		return err
+	}
+
+	if k.bankKeeper.BlockedAddr(addr) {
+		return fmt.Errorf("failed to burn from blocked address: %s", addr)
+	}
+
+	err = k.bankKeeper.SendCoinsFromAccountToModule(ctx,
+		addr,
+		types.ModuleName,
+		sdk.NewCoins(amount))
+	if err != nil {
+		return err
+	}
+
+	return k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(amount))
+}
+
+func (k Keeper) forceTransfer(ctx sdk.Context, amount sdk.Coin, fromAddr string, toAddr string) error {
+	// verify that denom is an x/tokenfactory denom
+	_, _, err := types.DeconstructDenom(amount.Denom)
+	if err != nil {
+		return err
+	}
+
+	fromSdkAddr, err := sdk.AccAddressFromBech32(fromAddr)
+	if err != nil {
+		return err
+	}
+
+	toSdkAddr, err := sdk.AccAddressFromBech32(toAddr)
+	if err != nil {
+		return err
+	}
+
+	if k.bankKeeper.BlockedAddr(toSdkAddr) {
+		return fmt.Errorf("failed to force transfer to blocked address: %s", toSdkAddr)
+	}
+
+	return k.bankKeeper.SendCoins(ctx, fromSdkAddr, toSdkAddr, sdk.NewCoins(amount))
+}

+ 87 - 0
wormchain/x/tokenfactory/keeper/createdenom.go

@@ -0,0 +1,87 @@
+package keeper
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+// ConvertToBaseToken converts a fee amount in a whitelisted fee token to the base fee token amount
+func (k Keeper) CreateDenom(ctx sdk.Context, creatorAddr string, subdenom string) (newTokenDenom string, err error) {
+	denom, err := k.validateCreateDenom(ctx, creatorAddr, subdenom)
+	if err != nil {
+		return "", err
+	}
+
+	err = k.chargeForCreateDenom(ctx, creatorAddr, subdenom)
+	if err != nil {
+		return "", err
+	}
+
+	err = k.createDenomAfterValidation(ctx, creatorAddr, denom)
+	return denom, err
+}
+
+// Runs CreateDenom logic after the charge and all denom validation has been handled.
+// Made into a second function for genesis initialization.
+func (k Keeper) createDenomAfterValidation(ctx sdk.Context, creatorAddr string, denom string) (err error) {
+	denomMetaData := banktypes.Metadata{
+		DenomUnits: []*banktypes.DenomUnit{{
+			Denom:    denom,
+			Exponent: 0,
+		}},
+		Base: denom,
+	}
+
+	k.bankKeeper.SetDenomMetaData(ctx, denomMetaData)
+
+	authorityMetadata := types.DenomAuthorityMetadata{
+		Admin: creatorAddr,
+	}
+	err = k.setAuthorityMetadata(ctx, denom, authorityMetadata)
+	if err != nil {
+		return err
+	}
+
+	k.addDenomFromCreator(ctx, creatorAddr, denom)
+	return nil
+}
+
+func (k Keeper) validateCreateDenom(ctx sdk.Context, creatorAddr string, subdenom string) (newTokenDenom string, err error) {
+	denom, err := types.GetTokenDenom(creatorAddr, subdenom)
+	if err != nil {
+		return "", err
+	}
+
+	_, found := k.bankKeeper.GetDenomMetaData(ctx, denom)
+	if found {
+		return "", types.ErrDenomExists
+	}
+
+	return denom, nil
+}
+
+func (k Keeper) chargeForCreateDenom(ctx sdk.Context, creatorAddr string, _ string) (err error) {
+	params := k.GetParams(ctx)
+
+	// if DenomCreationFee is non-zero, transfer the tokens from the creator
+	// account to community pool
+	if params.DenomCreationFee != nil {
+		accAddr, err := sdk.AccAddressFromBech32(creatorAddr)
+		if err != nil {
+			return err
+		}
+
+		if err := k.communityPoolKeeper.FundCommunityPool(ctx, params.DenomCreationFee, accAddr); err != nil {
+			return err
+		}
+	}
+
+	// if DenomCreationGasConsume is non-zero, consume the gas
+	if params.DenomCreationGasConsume != 0 {
+		ctx.GasMeter().ConsumeGas(params.DenomCreationGasConsume, "consume denom creation gas")
+	}
+
+	return nil
+}

+ 163 - 0
wormchain/x/tokenfactory/keeper/createdenom_test.go

@@ -0,0 +1,163 @@
+package keeper_test
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	//"github.com/wormhole-foundation/wormchain/x/tokenfactory/testhelpers"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func (suite *KeeperTestSuite) TestMsgCreateDenom() {
+	//var (
+	//tokenFactoryKeeper = suite.App.TokenFactoryKeeper
+	//bankKeeper         = suite.App.BankKeeper
+	//denomCreationFee   = tokenFactoryKeeper.GetParams(suite.Ctx).DenomCreationFee
+	//)
+
+	// Get balance of acc 0 before creating a denom
+	//preCreateBalance := bankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], denomCreationFee[0].Denom)
+
+	// Creating a denom should work
+	res, err := suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
+	suite.Require().NoError(err)
+	suite.Require().NotEmpty(res.GetNewTokenDenom())
+
+	// Make sure that the admin is set correctly
+	queryRes, err := suite.queryClient.DenomAuthorityMetadata(suite.Ctx.Context(), &types.QueryDenomAuthorityMetadataRequest{
+		Denom: res.GetNewTokenDenom(),
+	})
+	suite.Require().NoError(err)
+	suite.Require().Equal(suite.TestAccs[0].String(), queryRes.AuthorityMetadata.Admin)
+
+	// Make sure that creation fee was deducted
+	//postCreateBalance := bankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], tokenFactoryKeeper.GetParams(suite.Ctx).DenomCreationFee[0].Denom)
+	//suite.Require().True(preCreateBalance.Sub(postCreateBalance).IsEqual(denomCreationFee[0]))
+
+	// Make sure that a second version of the same denom can't be recreated
+	_, err = suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
+	suite.Require().Error(err)
+
+	// Creating a second denom should work
+	res, err = suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "litecoin"))
+	suite.Require().NoError(err)
+	suite.Require().NotEmpty(res.GetNewTokenDenom())
+
+	// Try querying all the denoms created by suite.TestAccs[0]
+	queryRes2, err := suite.queryClient.DenomsFromCreator(suite.Ctx.Context(), &types.QueryDenomsFromCreatorRequest{
+		Creator: suite.TestAccs[0].String(),
+	})
+	suite.Require().NoError(err)
+	suite.Require().Len(queryRes2.Denoms, 2)
+
+	// Make sure that a second account can create a denom with the same subdenom
+	res, err = suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[1].String(), "bitcoin"))
+	suite.Require().NoError(err)
+	suite.Require().NotEmpty(res.GetNewTokenDenom())
+
+	// Make sure that an address with a "/" in it can't create denoms
+	_, err = suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom("osmosis.eth/creator", "bitcoin"))
+	suite.Require().Error(err)
+}
+
+func (suite *KeeperTestSuite) TestCreateDenom() {
+	var (
+	//primaryDenom            = types.DefaultParams().DenomCreationFee[0].Denom
+	//secondaryDenom          = testhelpers.SecondaryDenom
+	//defaultDenomCreationFee = types.Params{DenomCreationFee: sdk.NewCoins(sdk.NewCoin(primaryDenom, sdk.NewInt(50000000)))}
+	//twoDenomCreationFee     = types.Params{DenomCreationFee: sdk.NewCoins(sdk.NewCoin(primaryDenom, sdk.NewInt(50000000)), sdk.NewCoin(secondaryDenom, sdk.NewInt(50000000)))}
+	//nilCreationFee          = types.Params{DenomCreationFee: nil}
+	//largeCreationFee        = types.Params{DenomCreationFee: sdk.NewCoins(sdk.NewCoin(primaryDenom, sdk.NewInt(5000000000)))}
+	)
+
+	for _, tc := range []struct {
+		desc string
+		//denomCreationFee types.Params
+		setup    func()
+		subdenom string
+		valid    bool
+	}{
+		{
+			desc: "subdenom too long",
+			//denomCreationFee: defaultDenomCreationFee,
+			subdenom: "assadsadsadasdasdsadsadsadsadsadsadsklkadaskkkdasdasedskhanhassyeunganassfnlksdflksafjlkasd",
+			valid:    false,
+		},
+		{
+			desc: "subdenom and creator pair already exists",
+			//denomCreationFee: defaultDenomCreationFee,
+			setup: func() {
+				_, err := suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
+				suite.Require().NoError(err)
+			},
+			subdenom: "bitcoin",
+			valid:    false,
+		},
+		{
+			desc: "success case: defaultDenomCreationFee",
+			//denomCreationFee: defaultDenomCreationFee,
+			subdenom: "evmos",
+			valid:    true,
+		},
+		{
+			desc: "success case: twoDenomCreationFee",
+			//denomCreationFee: twoDenomCreationFee,
+			subdenom: "catcoin",
+			valid:    true,
+		},
+		{
+			desc: "success case: nilCreationFee",
+			//denomCreationFee: nilCreationFee,
+			subdenom: "czcoin",
+			valid:    true,
+		},
+		//{
+		//	desc:             "account doesn't have enough to pay for denom creation fee",
+		//denomCreationFee: largeCreationFee,
+		//	subdenom:         "tooexpensive",
+		//	valid:            false,
+		//},
+		{
+			desc: "subdenom having invalid characters",
+			//denomCreationFee: defaultDenomCreationFee,
+			subdenom: "bit/***///&&&/coin",
+			valid:    false,
+		},
+	} {
+		suite.SetupTest()
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			if tc.setup != nil {
+				tc.setup()
+			}
+			//tokenFactoryKeeper := suite.App.TokenFactoryKeeper
+			bankKeeper := suite.App.BankKeeper
+			// Set denom creation fee in params
+			//tokenFactoryKeeper.SetParams(suite.Ctx, tc.denomCreationFee)
+			//denomCreationFee := tokenFactoryKeeper.GetParams(suite.Ctx).DenomCreationFee
+			//suite.Require().Equal(tc.denomCreationFee.DenomCreationFee, denomCreationFee)
+
+			// note balance, create a tokenfactory denom, then note balance again
+			preCreateBalance := bankKeeper.GetAllBalances(suite.Ctx, suite.TestAccs[0])
+			res, err := suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), tc.subdenom))
+			postCreateBalance := bankKeeper.GetAllBalances(suite.Ctx, suite.TestAccs[0])
+			if tc.valid {
+				suite.Require().NoError(err)
+				//suite.Require().True(preCreateBalance.Sub(postCreateBalance).IsEqual(denomCreationFee))
+
+				// Make sure that the admin is set correctly
+				queryRes, err := suite.queryClient.DenomAuthorityMetadata(suite.Ctx.Context(), &types.QueryDenomAuthorityMetadataRequest{
+					Denom: res.GetNewTokenDenom(),
+				})
+
+				suite.Require().NoError(err)
+				suite.Require().Equal(suite.TestAccs[0].String(), queryRes.AuthorityMetadata.Admin)
+
+			} else {
+				suite.Require().Error(err)
+				// Ensure we don't charge if we expect an error
+				suite.Require().True(preCreateBalance.IsEqual(postCreateBalance))
+			}
+		})
+	}
+}

+ 29 - 0
wormchain/x/tokenfactory/keeper/creators.go

@@ -0,0 +1,29 @@
+package keeper
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+func (k Keeper) addDenomFromCreator(ctx sdk.Context, creator, denom string) {
+	store := k.GetCreatorPrefixStore(ctx, creator)
+	store.Set([]byte(denom), []byte(denom))
+}
+
+func (k Keeper) GetDenomsFromCreator(ctx sdk.Context, creator string) []string {
+	store := k.GetCreatorPrefixStore(ctx, creator)
+
+	iterator := store.Iterator(nil, nil)
+	defer iterator.Close()
+
+	denoms := []string{}
+	for ; iterator.Valid(); iterator.Next() {
+		denoms = append(denoms, string(iterator.Key()))
+	}
+	return denoms
+}
+
+func (k Keeper) GetAllDenomsIterator(ctx sdk.Context) sdk.Iterator {
+	return k.GetCreatorsPrefixStore(ctx).Iterator(nil, nil)
+}
+
+// TODO: Get all denoms a user is the admin of currently

+ 58 - 0
wormchain/x/tokenfactory/keeper/genesis.go

@@ -0,0 +1,58 @@
+package keeper
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+// InitGenesis initializes the tokenfactory module's state from a provided genesis
+// state.
+func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) {
+	k.CreateModuleAccount(ctx)
+
+	if genState.Params.DenomCreationFee == nil {
+		genState.Params.DenomCreationFee = sdk.NewCoins()
+	}
+	k.SetParams(ctx, genState.Params)
+
+	for _, genDenom := range genState.GetFactoryDenoms() {
+		creator, _, err := types.DeconstructDenom(genDenom.GetDenom())
+		if err != nil {
+			panic(err)
+		}
+		err = k.createDenomAfterValidation(ctx, creator, genDenom.GetDenom())
+		if err != nil {
+			panic(err)
+		}
+		err = k.setAuthorityMetadata(ctx, genDenom.GetDenom(), genDenom.GetAuthorityMetadata())
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+
+// ExportGenesis returns the tokenfactory module's exported genesis.
+func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState {
+	genDenoms := []types.GenesisDenom{}
+	iterator := k.GetAllDenomsIterator(ctx)
+	defer iterator.Close()
+	for ; iterator.Valid(); iterator.Next() {
+		denom := string(iterator.Value())
+
+		authorityMetadata, err := k.GetAuthorityMetadata(ctx, denom)
+		if err != nil {
+			panic(err)
+		}
+
+		genDenoms = append(genDenoms, types.GenesisDenom{
+			Denom:             denom,
+			AuthorityMetadata: authorityMetadata,
+		})
+	}
+
+	return &types.GenesisState{
+		FactoryDenoms: genDenoms,
+		Params:        k.GetParams(ctx),
+	}
+}

+ 59 - 0
wormchain/x/tokenfactory/keeper/genesis_test.go

@@ -0,0 +1,59 @@
+package keeper_test
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func (suite *KeeperTestSuite) TestGenesis() {
+	genesisState := types.GenesisState{
+		FactoryDenoms: []types.GenesisDenom{
+			{
+				Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+				AuthorityMetadata: types.DenomAuthorityMetadata{
+					Admin: "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+				},
+			},
+			{
+				Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/diff-admin",
+				AuthorityMetadata: types.DenomAuthorityMetadata{
+					Admin: "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+				},
+			},
+			{
+				Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/litecoin",
+				AuthorityMetadata: types.DenomAuthorityMetadata{
+					Admin: "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+				},
+			},
+		},
+	}
+
+	suite.SetupTestForInitGenesis()
+	app := suite.App
+
+	// Test both with bank denom metadata set, and not set.
+	for i, denom := range genesisState.FactoryDenoms {
+		// hacky, sets bank metadata to exist if i != 0, to cover both cases.
+		if i != 0 {
+			app.BankKeeper.SetDenomMetaData(suite.Ctx, banktypes.Metadata{Base: denom.GetDenom()})
+		}
+	}
+
+	// check before initGenesis that the module account is nil
+	//tokenfactoryModuleAccount := app.AccountKeeper.GetAccount(suite.Ctx, app.AccountKeeper.GetModuleAddress(types.ModuleName))
+	//suite.Require().Nil(tokenfactoryModuleAccount)
+
+	app.TokenFactoryKeeper.SetParams(suite.Ctx, types.Params{DenomCreationFee: sdk.Coins{sdk.NewInt64Coin("uosmo", 100)}})
+	app.TokenFactoryKeeper.InitGenesis(suite.Ctx, genesisState)
+
+	// check that the module account is now initialized
+	tokenfactoryModuleAccount := app.AccountKeeper.GetAccount(suite.Ctx, app.AccountKeeper.GetModuleAddress(types.ModuleName))
+	suite.Require().NotNil(tokenfactoryModuleAccount)
+
+	exportedGenesis := app.TokenFactoryKeeper.ExportGenesis(suite.Ctx)
+	suite.Require().NotNil(exportedGenesis)
+	suite.Require().Equal(genesisState, *exportedGenesis)
+}

+ 35 - 0
wormchain/x/tokenfactory/keeper/grpc_query.go

@@ -0,0 +1,35 @@
+package keeper
+
+import (
+	"context"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+var _ types.QueryServer = Keeper{}
+
+func (k Keeper) Params(ctx context.Context, _ *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
+	sdkCtx := sdk.UnwrapSDKContext(ctx)
+	params := k.GetParams(sdkCtx)
+
+	return &types.QueryParamsResponse{Params: params}, nil
+}
+
+func (k Keeper) DenomAuthorityMetadata(ctx context.Context, req *types.QueryDenomAuthorityMetadataRequest) (*types.QueryDenomAuthorityMetadataResponse, error) {
+	sdkCtx := sdk.UnwrapSDKContext(ctx)
+
+	authorityMetadata, err := k.GetAuthorityMetadata(sdkCtx, req.GetDenom())
+	if err != nil {
+		return nil, err
+	}
+
+	return &types.QueryDenomAuthorityMetadataResponse{AuthorityMetadata: authorityMetadata}, nil
+}
+
+func (k Keeper) DenomsFromCreator(ctx context.Context, req *types.QueryDenomsFromCreatorRequest) (*types.QueryDenomsFromCreatorResponse, error) {
+	sdkCtx := sdk.UnwrapSDKContext(ctx)
+	denoms := k.GetDenomsFromCreator(sdkCtx, req.GetCreator())
+	return &types.QueryDenomsFromCreatorResponse{Denoms: denoms}, nil
+}

+ 87 - 0
wormchain/x/tokenfactory/keeper/keeper.go

@@ -0,0 +1,87 @@
+package keeper
+
+import (
+	"fmt"
+
+	"github.com/tendermint/tendermint/libs/log"
+
+	"github.com/cosmos/cosmos-sdk/store/prefix"
+	storetypes "github.com/cosmos/cosmos-sdk/store/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+)
+
+type (
+	Keeper struct {
+		storeKey storetypes.StoreKey
+
+		paramSpace paramtypes.Subspace
+
+		accountKeeper       types.AccountKeeper
+		bankKeeper          types.BankKeeper
+		communityPoolKeeper types.CommunityPoolKeeper
+
+		enabledCapabilities []string
+	}
+)
+
+// NewKeeper returns a new instance of the x/tokenfactory keeper
+func NewKeeper(
+	storeKey storetypes.StoreKey,
+	paramSpace paramtypes.Subspace,
+	accountKeeper types.AccountKeeper,
+	bankKeeper types.BankKeeper,
+	communityPoolKeeper types.CommunityPoolKeeper,
+	enabledCapabilities []string,
+) Keeper {
+	if !paramSpace.HasKeyTable() {
+		paramSpace = paramSpace.WithKeyTable(types.ParamKeyTable())
+	}
+
+	return Keeper{
+		storeKey:   storeKey,
+		paramSpace: paramSpace,
+
+		accountKeeper:       accountKeeper,
+		bankKeeper:          bankKeeper,
+		communityPoolKeeper: communityPoolKeeper,
+
+		enabledCapabilities: enabledCapabilities,
+	}
+}
+
+// Logger returns a logger for the x/tokenfactory module
+func (k Keeper) Logger(ctx sdk.Context) log.Logger {
+	return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName))
+}
+
+// GetDenomPrefixStore returns the substore for a specific denom
+func (k Keeper) GetDenomPrefixStore(ctx sdk.Context, denom string) sdk.KVStore {
+	store := ctx.KVStore(k.storeKey)
+	return prefix.NewStore(store, types.GetDenomPrefixStore(denom))
+}
+
+// GetCreatorPrefixStore returns the substore for a specific creator address
+func (k Keeper) GetCreatorPrefixStore(ctx sdk.Context, creator string) sdk.KVStore {
+	store := ctx.KVStore(k.storeKey)
+	return prefix.NewStore(store, types.GetCreatorPrefix(creator))
+}
+
+// GetCreatorsPrefixStore returns the substore that contains a list of creators
+func (k Keeper) GetCreatorsPrefixStore(ctx sdk.Context) sdk.KVStore {
+	store := ctx.KVStore(k.storeKey)
+	return prefix.NewStore(store, types.GetCreatorsPrefix())
+}
+
+// CreateModuleAccount creates a module account with minting and burning capabilities
+// This account isn't intended to store any coins,
+// it purely mints and burns them on behalf of the admin of respective denoms,
+// and sends to the relevant address.
+func (k Keeper) CreateModuleAccount(ctx sdk.Context) {
+	moduleAcc := authtypes.NewEmptyModuleAccount(types.ModuleName, authtypes.Minter, authtypes.Burner)
+	k.accountKeeper.SetModuleAccount(ctx, moduleAcc)
+}

+ 66 - 0
wormchain/x/tokenfactory/keeper/keeper_test.go

@@ -0,0 +1,66 @@
+package keeper_test
+
+import (
+	"testing"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/stretchr/testify/suite"
+	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
+
+	"github.com/wormhole-foundation/wormchain/app/apptesting"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/keeper"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/testhelpers"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+type KeeperTestSuite struct {
+	apptesting.KeeperTestHelper
+
+	queryClient types.QueryClient
+	msgServer   types.MsgServer
+	// defaultDenom is on the suite, as it depends on the creator test address.
+	defaultDenom string
+}
+
+func TestKeeperTestSuite(t *testing.T) {
+	suite.Run(t, new(KeeperTestSuite))
+}
+
+func (suite *KeeperTestSuite) SetupTest() {
+	suite.Setup()
+	// There is no denom creation fee
+	fundAccsAmount := sdk.NewCoins(sdk.NewCoin(testhelpers.SecondaryDenom, testhelpers.SecondaryAmount))
+	// Fund every TestAcc with two denoms, one of which is the denom creation fee
+	//fundAccsAmount := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100)), sdk.NewCoin(testhelpers.SecondaryDenom, testhelpers.SecondaryAmount))
+	for _, acc := range suite.TestAccs {
+		suite.FundAcc(acc, fundAccsAmount)
+	}
+
+	suite.queryClient = types.NewQueryClient(suite.QueryHelper)
+	suite.msgServer = keeper.NewMsgServerImpl(suite.App.TokenFactoryKeeper)
+}
+
+func (suite *KeeperTestSuite) CreateDefaultDenom() {
+	res, _ := suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
+	suite.defaultDenom = res.GetNewTokenDenom()
+}
+
+func (suite *KeeperTestSuite) TestCreateModuleAccount() {
+	app := suite.App
+
+	// remove module account
+	tokenfactoryModuleAccount := app.AccountKeeper.GetAccount(suite.Ctx, app.AccountKeeper.GetModuleAddress(types.ModuleName))
+	app.AccountKeeper.RemoveAccount(suite.Ctx, tokenfactoryModuleAccount)
+
+	// ensure module account was removed
+	suite.Ctx = app.BaseApp.NewContext(false, tmproto.Header{})
+	tokenfactoryModuleAccount = app.AccountKeeper.GetAccount(suite.Ctx, app.AccountKeeper.GetModuleAddress(types.ModuleName))
+	suite.Require().Nil(tokenfactoryModuleAccount)
+
+	// create module account
+	app.TokenFactoryKeeper.CreateModuleAccount(suite.Ctx)
+
+	// check that the module account is now initialized
+	tokenfactoryModuleAccount = app.AccountKeeper.GetAccount(suite.Ctx, app.AccountKeeper.GetModuleAddress(types.ModuleName))
+	suite.Require().NotNil(tokenfactoryModuleAccount)
+}

+ 209 - 0
wormchain/x/tokenfactory/keeper/msg_server.go

@@ -0,0 +1,209 @@
+package keeper
+
+import (
+	"context"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+type msgServer struct {
+	Keeper
+}
+
+// NewMsgServerImpl returns an implementation of the MsgServer interface
+// for the provided Keeper.
+func NewMsgServerImpl(keeper Keeper) types.MsgServer {
+	return &msgServer{Keeper: keeper}
+}
+
+var _ types.MsgServer = msgServer{}
+
+func (server msgServer) CreateDenom(goCtx context.Context, msg *types.MsgCreateDenom) (*types.MsgCreateDenomResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	denom, err := server.Keeper.CreateDenom(ctx, msg.Sender, msg.Subdenom)
+	if err != nil {
+		return nil, err
+	}
+
+	ctx.EventManager().EmitEvents(sdk.Events{
+		sdk.NewEvent(
+			types.TypeMsgCreateDenom,
+			sdk.NewAttribute(types.AttributeCreator, msg.Sender),
+			sdk.NewAttribute(types.AttributeNewTokenDenom, denom),
+		),
+	})
+
+	return &types.MsgCreateDenomResponse{
+		NewTokenDenom: denom,
+	}, nil
+}
+
+func (server msgServer) Mint(goCtx context.Context, msg *types.MsgMint) (*types.MsgMintResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	// pay some extra gas cost to give a better error here.
+	_, denomExists := server.bankKeeper.GetDenomMetaData(ctx, msg.Amount.Denom)
+	if !denomExists {
+		return nil, types.ErrDenomDoesNotExist.Wrapf("denom: %s", msg.Amount.Denom)
+	}
+
+	authorityMetadata, err := server.Keeper.GetAuthorityMetadata(ctx, msg.Amount.GetDenom())
+	if err != nil {
+		return nil, err
+	}
+
+	if msg.Sender != authorityMetadata.GetAdmin() {
+		return nil, types.ErrUnauthorized
+	}
+
+	if msg.MintToAddress == "" {
+		msg.MintToAddress = msg.Sender
+	}
+
+	err = server.Keeper.mintTo(ctx, msg.Amount, msg.MintToAddress)
+	if err != nil {
+		return nil, err
+	}
+
+	ctx.EventManager().EmitEvents(sdk.Events{
+		sdk.NewEvent(
+			types.TypeMsgMint,
+			sdk.NewAttribute(types.AttributeMintToAddress, msg.Sender),
+			sdk.NewAttribute(types.AttributeAmount, msg.Amount.String()),
+		),
+	})
+
+	return &types.MsgMintResponse{}, nil
+}
+
+func (server msgServer) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.MsgBurnResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	authorityMetadata, err := server.Keeper.GetAuthorityMetadata(ctx, msg.Amount.GetDenom())
+	if err != nil {
+		return nil, err
+	}
+
+	if msg.Sender != authorityMetadata.GetAdmin() {
+		return nil, types.ErrUnauthorized
+	}
+
+	if msg.BurnFromAddress == "" {
+		msg.BurnFromAddress = msg.Sender
+	} else if !types.IsCapabilityEnabled(server.Keeper.enabledCapabilities, types.EnableBurnFrom) {
+		return nil, types.ErrCapabilityNotEnabled
+	}
+
+	err = server.Keeper.burnFrom(ctx, msg.Amount, msg.BurnFromAddress)
+	if err != nil {
+		return nil, err
+	}
+
+	ctx.EventManager().EmitEvents(sdk.Events{
+		sdk.NewEvent(
+			types.TypeMsgBurn,
+			sdk.NewAttribute(types.AttributeBurnFromAddress, msg.Sender),
+			sdk.NewAttribute(types.AttributeAmount, msg.Amount.String()),
+		),
+	})
+
+	return &types.MsgBurnResponse{}, nil
+}
+
+func (server msgServer) ForceTransfer(goCtx context.Context, msg *types.MsgForceTransfer) (*types.MsgForceTransferResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	if !types.IsCapabilityEnabled(server.Keeper.enabledCapabilities, types.EnableForceTransfer) {
+		return nil, types.ErrCapabilityNotEnabled
+	}
+
+	authorityMetadata, err := server.Keeper.GetAuthorityMetadata(ctx, msg.Amount.GetDenom())
+	if err != nil {
+		return nil, err
+	}
+
+	if msg.Sender != authorityMetadata.GetAdmin() {
+		return nil, types.ErrUnauthorized
+	}
+
+	err = server.Keeper.forceTransfer(ctx, msg.Amount, msg.TransferFromAddress, msg.TransferToAddress)
+	if err != nil {
+		return nil, err
+	}
+
+	ctx.EventManager().EmitEvents(sdk.Events{
+		sdk.NewEvent(
+			types.TypeMsgForceTransfer,
+			sdk.NewAttribute(types.AttributeTransferFromAddress, msg.TransferFromAddress),
+			sdk.NewAttribute(types.AttributeTransferToAddress, msg.TransferToAddress),
+			sdk.NewAttribute(types.AttributeAmount, msg.Amount.String()),
+		),
+	})
+
+	return &types.MsgForceTransferResponse{}, nil
+}
+
+func (server msgServer) ChangeAdmin(goCtx context.Context, msg *types.MsgChangeAdmin) (*types.MsgChangeAdminResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	authorityMetadata, err := server.Keeper.GetAuthorityMetadata(ctx, msg.Denom)
+	if err != nil {
+		return nil, err
+	}
+
+	if msg.Sender != authorityMetadata.GetAdmin() {
+		return nil, types.ErrUnauthorized
+	}
+
+	err = server.Keeper.setAdmin(ctx, msg.Denom, msg.NewAdmin)
+	if err != nil {
+		return nil, err
+	}
+	ctx.EventManager().EmitEvents(sdk.Events{
+		sdk.NewEvent(
+			types.TypeMsgChangeAdmin,
+			sdk.NewAttribute(types.AttributeDenom, msg.GetDenom()),
+			sdk.NewAttribute(types.AttributeNewAdmin, msg.NewAdmin),
+		),
+	})
+
+	return &types.MsgChangeAdminResponse{}, nil
+}
+
+func (server msgServer) SetDenomMetadata(goCtx context.Context, msg *types.MsgSetDenomMetadata) (*types.MsgSetDenomMetadataResponse, error) {
+	ctx := sdk.UnwrapSDKContext(goCtx)
+
+	if !types.IsCapabilityEnabled(server.Keeper.enabledCapabilities, types.EnableSetMetadata) {
+		return nil, types.ErrCapabilityNotEnabled
+	}
+
+	// Defense in depth validation of metadata
+	err := msg.Metadata.Validate()
+	if err != nil {
+		return nil, err
+	}
+
+	authorityMetadata, err := server.Keeper.GetAuthorityMetadata(ctx, msg.Metadata.Base)
+	if err != nil {
+		return nil, err
+	}
+
+	if msg.Sender != authorityMetadata.GetAdmin() {
+		return nil, types.ErrUnauthorized
+	}
+
+	server.Keeper.bankKeeper.SetDenomMetaData(ctx, msg.Metadata)
+
+	ctx.EventManager().EmitEvents(sdk.Events{
+		sdk.NewEvent(
+			types.TypeMsgSetDenomMetadata,
+			sdk.NewAttribute(types.AttributeDenom, msg.Metadata.Base),
+			sdk.NewAttribute(types.AttributeDenomMetadata, msg.Metadata.String()),
+		),
+	})
+
+	return &types.MsgSetDenomMetadataResponse{}, nil
+}

+ 248 - 0
wormchain/x/tokenfactory/keeper/msg_server_test.go

@@ -0,0 +1,248 @@
+package keeper_test
+
+import (
+	"fmt"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	//banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+)
+
+// TestMintDenomMsg tests TypeMsgMint message is emitted on a successful mint
+func (suite *KeeperTestSuite) TestMintDenomMsg() {
+	// Create a denom
+	suite.CreateDefaultDenom()
+
+	for _, tc := range []struct {
+		desc                  string
+		amount                int64
+		mintDenom             string
+		admin                 string
+		valid                 bool
+		expectedMessageEvents int
+	}{
+		{
+			desc:      "denom does not exist",
+			amount:    10,
+			mintDenom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/evmos",
+			admin:     suite.TestAccs[0].String(),
+			valid:     false,
+		},
+		{
+			desc:                  "success case",
+			amount:                10,
+			mintDenom:             suite.defaultDenom,
+			admin:                 suite.TestAccs[0].String(),
+			valid:                 true,
+			expectedMessageEvents: 1,
+		},
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
+			suite.Require().Equal(0, len(ctx.EventManager().Events()))
+			// Test mint message
+			suite.msgServer.Mint(sdk.WrapSDKContext(ctx), types.NewMsgMint(tc.admin, sdk.NewInt64Coin(tc.mintDenom, 10))) //nolint:errcheck
+			// Ensure current number and type of event is emitted
+			suite.AssertEventEmitted(ctx, types.TypeMsgMint, tc.expectedMessageEvents)
+		})
+	}
+}
+
+// TestBurnDenomMsg tests TypeMsgBurn message is emitted on a successful burn
+func (suite *KeeperTestSuite) TestBurnDenomMsg() {
+	// Create a denom.
+	suite.CreateDefaultDenom()
+	// mint 10 default token for testAcc[0]
+	suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMint(suite.TestAccs[0].String(), sdk.NewInt64Coin(suite.defaultDenom, 10))) //nolint:errcheck
+
+	for _, tc := range []struct {
+		desc                  string
+		amount                int64
+		burnDenom             string
+		admin                 string
+		valid                 bool
+		expectedMessageEvents int
+	}{
+		{
+			desc:      "denom does not exist",
+			burnDenom: "factory/osmo1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44/evmos",
+			admin:     suite.TestAccs[0].String(),
+			valid:     false,
+		},
+		{
+			desc:                  "success case",
+			burnDenom:             suite.defaultDenom,
+			admin:                 suite.TestAccs[0].String(),
+			valid:                 true,
+			expectedMessageEvents: 1,
+		},
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
+			suite.Require().Equal(0, len(ctx.EventManager().Events()))
+			// Test burn message
+			suite.msgServer.Burn(sdk.WrapSDKContext(ctx), types.NewMsgBurn(tc.admin, sdk.NewInt64Coin(tc.burnDenom, 10))) //nolint:errcheck
+			// Ensure current number and type of event is emitted
+			suite.AssertEventEmitted(ctx, types.TypeMsgBurn, tc.expectedMessageEvents)
+		})
+	}
+}
+
+// TestCreateDenomMsg tests TypeMsgCreateDenom message is emitted on a successful denom creation
+func (suite *KeeperTestSuite) TestCreateDenomMsg() {
+	//defaultDenomCreationFee := types.Params{DenomCreationFee: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(50000000)))}
+	for _, tc := range []struct {
+		desc string
+		//denomCreationFee      types.Params
+		subdenom              string
+		valid                 bool
+		expectedMessageEvents int
+	}{
+		{
+			desc: "subdenom too long",
+			//denomCreationFee: defaultDenomCreationFee,
+			subdenom: "assadsadsadasdasdsadsadsadsadsadsadsklkadaskkkdasdasedskhanhassyeunganassfnlksdflksafjlkasd",
+			valid:    false,
+		},
+		{
+			desc: "success case: defaultDenomCreationFee",
+			//denomCreationFee:      defaultDenomCreationFee,
+			subdenom:              "evmos",
+			valid:                 true,
+			expectedMessageEvents: 1,
+		},
+	} {
+		suite.SetupTest()
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			//tokenFactoryKeeper := suite.App.TokenFactoryKeeper
+			ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
+			suite.Require().Equal(0, len(ctx.EventManager().Events()))
+			// Set denom creation fee in params
+			//tokenFactoryKeeper.SetParams(suite.Ctx, tc.denomCreationFee)
+			// Test create denom message
+			suite.msgServer.CreateDenom(sdk.WrapSDKContext(ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), tc.subdenom)) //nolint:errcheck
+			// Ensure current number and type of event is emitted
+			suite.AssertEventEmitted(ctx, types.TypeMsgCreateDenom, tc.expectedMessageEvents)
+		})
+	}
+}
+
+// TestChangeAdminDenomMsg tests TypeMsgChangeAdmin message is emitted on a successful admin change
+func (suite *KeeperTestSuite) TestChangeAdminDenomMsg() {
+	for _, tc := range []struct {
+		desc                    string
+		msgChangeAdmin          func(denom string) *types.MsgChangeAdmin
+		expectedChangeAdminPass bool
+		expectedAdminIndex      int
+		msgMint                 func(denom string) *types.MsgMint
+		expectedMintPass        bool
+		expectedMessageEvents   int
+	}{
+		{
+			desc: "non-admins can't change the existing admin",
+			msgChangeAdmin: func(denom string) *types.MsgChangeAdmin {
+				return types.NewMsgChangeAdmin(suite.TestAccs[1].String(), denom, suite.TestAccs[2].String())
+			},
+			expectedChangeAdminPass: false,
+			expectedAdminIndex:      0,
+		},
+		{
+			desc: "success change admin",
+			msgChangeAdmin: func(denom string) *types.MsgChangeAdmin {
+				return types.NewMsgChangeAdmin(suite.TestAccs[0].String(), denom, suite.TestAccs[1].String())
+			},
+			expectedAdminIndex:      1,
+			expectedChangeAdminPass: true,
+			expectedMessageEvents:   1,
+			msgMint: func(denom string) *types.MsgMint {
+				return types.NewMsgMint(suite.TestAccs[1].String(), sdk.NewInt64Coin(denom, 5))
+			},
+			expectedMintPass: true,
+		},
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			// setup test
+			suite.SetupTest()
+			ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
+			suite.Require().Equal(0, len(ctx.EventManager().Events()))
+			// Create a denom and mint
+			res, err := suite.msgServer.CreateDenom(sdk.WrapSDKContext(ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
+			suite.Require().NoError(err)
+			testDenom := res.GetNewTokenDenom()
+			suite.msgServer.Mint(sdk.WrapSDKContext(ctx), types.NewMsgMint(suite.TestAccs[0].String(), sdk.NewInt64Coin(testDenom, 10))) //nolint:errcheck
+			// Test change admin message
+			suite.msgServer.ChangeAdmin(sdk.WrapSDKContext(ctx), tc.msgChangeAdmin(testDenom)) //nolint:errcheck
+			// Ensure current number and type of event is emitted
+			suite.AssertEventEmitted(ctx, types.TypeMsgChangeAdmin, tc.expectedMessageEvents)
+		})
+	}
+}
+
+// TestSetDenomMetaDataMsg tests TypeMsgSetDenomMetadata message is emitted on a successful denom metadata change
+// Capability disabled
+/*func (suite *KeeperTestSuite) TestSetDenomMetaDataMsg() {
+	// setup test
+	suite.SetupTest()
+	suite.CreateDefaultDenom()
+
+	for _, tc := range []struct {
+		desc                  string
+		msgSetDenomMetadata   types.MsgSetDenomMetadata
+		expectedPass          bool
+		expectedMessageEvents int
+	}{
+		{
+			desc: "successful set denom metadata",
+			msgSetDenomMetadata: *types.NewMsgSetDenomMetadata(suite.TestAccs[0].String(), banktypes.Metadata{
+				Description: "yeehaw",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    suite.defaultDenom,
+						Exponent: 0,
+					},
+					{
+						Denom:    "uosmo",
+						Exponent: 6,
+					},
+				},
+				Base:    suite.defaultDenom,
+				Display: "uosmo",
+				Name:    "OSMO",
+				Symbol:  "OSMO",
+			}),
+			expectedPass:          false,
+			expectedMessageEvents: 1,
+		},
+		{
+			desc: "non existent factory denom name",
+			msgSetDenomMetadata: *types.NewMsgSetDenomMetadata(suite.TestAccs[0].String(), banktypes.Metadata{
+				Description: "yeehaw",
+				DenomUnits: []*banktypes.DenomUnit{
+					{
+						Denom:    fmt.Sprintf("factory/%s/litecoin", suite.TestAccs[0].String()),
+						Exponent: 0,
+					},
+					{
+						Denom:    "uosmo",
+						Exponent: 6,
+					},
+				},
+				Base:    fmt.Sprintf("factory/%s/litecoin", suite.TestAccs[0].String()),
+				Display: "uosmo",
+				Name:    "OSMO",
+				Symbol:  "OSMO",
+			}),
+			expectedPass: false,
+		},
+	} {
+		suite.Run(fmt.Sprintf("Case %s", tc.desc), func() {
+			ctx := suite.Ctx.WithEventManager(sdk.NewEventManager())
+			suite.Require().Equal(0, len(ctx.EventManager().Events()))
+			// Test set denom metadata message
+			suite.msgServer.SetDenomMetadata(sdk.WrapSDKContext(ctx), &tc.msgSetDenomMetadata) //nolint:errcheck
+			// Ensure current number and type of event is emitted
+			suite.AssertEventEmitted(ctx, types.TypeMsgSetDenomMetadata, tc.expectedMessageEvents)
+		})
+	}
+}*/

+ 18 - 0
wormchain/x/tokenfactory/keeper/params.go

@@ -0,0 +1,18 @@
+package keeper
+
+import (
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+// GetParams returns the total set params.
+func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) {
+	k.paramSpace.GetParamSet(ctx, &params)
+	return params
+}
+
+// SetParams sets the total set of params.
+func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
+	k.paramSpace.SetParamSet(ctx, &params)
+}

+ 229 - 0
wormchain/x/tokenfactory/module.go

@@ -0,0 +1,229 @@
+/*
+The tokenfactory module allows any account to create a new token with
+the name `factory/{creator address}/{subdenom}`.
+
+- Mint and burn user denom to and form any account
+- Create a transfer of their denom between any two accounts
+- Change the admin. In the future, more admin capabilities may be added.
+*/
+package tokenfactory
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/client"
+	"github.com/cosmos/cosmos-sdk/codec"
+	cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+	"github.com/gorilla/mux"
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"github.com/spf13/cobra"
+	abci "github.com/tendermint/tendermint/abci/types"
+
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+	simulation "github.com/wormhole-foundation/wormchain/x/tokenfactory/simulation"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/client/cli"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/keeper"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+var (
+	_ module.AppModule      = AppModule{}
+	_ module.AppModuleBasic = AppModuleBasic{}
+)
+
+// ----------------------------------------------------------------------------
+// AppModuleBasic
+// ----------------------------------------------------------------------------
+
+// AppModuleBasic implements the AppModuleBasic interface for the capability module.
+type AppModuleBasic struct{}
+
+func NewAppModuleBasic() AppModuleBasic {
+	return AppModuleBasic{}
+}
+
+// Name returns the x/tokenfactory module's name.
+func (AppModuleBasic) Name() string {
+	return types.ModuleName
+}
+
+func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
+	types.RegisterCodec(cdc)
+}
+
+// RegisterInterfaces registers the module's interface types
+func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) {
+	types.RegisterInterfaces(reg)
+}
+
+// DefaultGenesis returns the x/tokenfactory module's default genesis state.
+func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
+	return cdc.MustMarshalJSON(types.DefaultGenesis())
+}
+
+// ValidateGenesis performs genesis state validation for the x/tokenfactory module.
+func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
+	var genState types.GenesisState
+	if err := cdc.UnmarshalJSON(bz, &genState); err != nil {
+		return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
+	}
+
+	return genState.Validate()
+}
+
+// RegisterRESTRoutes registers the capability module's REST service handlers.
+func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {
+}
+
+// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module.
+func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
+	types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)) //nolint:errcheck
+}
+
+// GetTxCmd returns the x/tokenfactory module's root tx command.
+func (a AppModuleBasic) GetTxCmd() *cobra.Command {
+	return cli.GetTxCmd()
+}
+
+// GetQueryCmd returns the x/tokenfactory module's root query command.
+func (AppModuleBasic) GetQueryCmd() *cobra.Command {
+	return cli.GetQueryCmd()
+}
+
+// ----------------------------------------------------------------------------
+// AppModule
+// ----------------------------------------------------------------------------
+
+// AppModule implements the AppModule interface for the capability module.
+type AppModule struct {
+	AppModuleBasic
+
+	keeper        keeper.Keeper
+	accountKeeper types.AccountKeeper
+	bankKeeper    types.BankKeeper
+}
+
+func NewAppModule(
+	keeper keeper.Keeper,
+	accountKeeper types.AccountKeeper,
+	bankKeeper types.BankKeeper,
+) AppModule {
+	return AppModule{
+		AppModuleBasic: NewAppModuleBasic(),
+		keeper:         keeper,
+		accountKeeper:  accountKeeper,
+		bankKeeper:     bankKeeper,
+	}
+}
+
+// Name returns the x/tokenfactory module's name.
+func (am AppModule) Name() string {
+	return am.AppModuleBasic.Name()
+}
+
+// Route returns the x/tokenfactory module's message routing key.
+func (am AppModule) Route() sdk.Route {
+	return sdk.Route{}
+}
+
+// QuerierRoute returns the x/tokenfactory module's query routing key.
+func (AppModule) QuerierRoute() string { return types.QuerierRoute }
+
+// LegacyQuerierHandler returns the x/tokenfactory module's Querier.
+func (am AppModule) LegacyQuerierHandler(_ *codec.LegacyAmino) sdk.Querier {
+	return nil
+}
+
+// RegisterServices registers a GRPC query service to respond to the
+// module-specific GRPC queries.
+func (am AppModule) RegisterServices(cfg module.Configurator) {
+	types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
+	types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
+}
+
+// RegisterInvariants registers the x/tokenfactory module's invariants.
+func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
+
+// InitGenesis performs the x/tokenfactory module's genesis initialization. It
+// returns no validator updates.
+func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate {
+	var genState types.GenesisState
+	cdc.MustUnmarshalJSON(gs, &genState)
+
+	am.keeper.InitGenesis(ctx, genState)
+
+	return []abci.ValidatorUpdate{}
+}
+
+// ExportGenesis returns the x/tokenfactory module's exported genesis state as raw
+// JSON bytes.
+func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
+	genState := am.keeper.ExportGenesis(ctx)
+	return cdc.MustMarshalJSON(genState)
+}
+
+// ConsensusVersion implements ConsensusVersion.
+func (AppModule) ConsensusVersion() uint64 { return 1 }
+
+// BeginBlock executes all ABCI BeginBlock logic respective to the tokenfactory module.
+func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
+
+// EndBlock executes all ABCI EndBlock logic respective to the tokenfactory module. It
+// returns no validator updates.
+func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate {
+	return []abci.ValidatorUpdate{}
+}
+
+// ___________________________________________________________________________
+
+// AppModuleSimulationV2 functions
+
+// // GenerateGenesisState creates a randomized GenState of the tokenfactory module.
+// func (am AppModule) SimulatorGenesisState(simState *module.SimulationState, s *simtypes.SimCtx) {
+// 	tfDefaultGen := types.DefaultGenesis()
+// 	tfDefaultGen.Params.DenomCreationFee = sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(10000000)))
+// 	tfDefaultGenJson := simState.Cdc.MustMarshalJSON(tfDefaultGen)
+// 	simState.GenState[types.ModuleName] = tfDefaultGenJson
+// }
+
+// // WeightedOperations returns the all the lockup module operations with their respective weights.
+// func (am AppModule) Actions() []simtypes.Action {
+// 	return []simtypes.Action{
+// 		simtypes.NewMsgBasedAction("create token factory token", am.keeper, simulation.RandomMsgCreateDenom),
+// 		simtypes.NewMsgBasedAction("mint token factory token", am.keeper, simulation.RandomMsgMintDenom),
+// 		simtypes.NewMsgBasedAction("burn token factory token", am.keeper, simulation.RandomMsgBurnDenom),
+// 		simtypes.NewMsgBasedAction("change admin token factory token", am.keeper, simulation.RandomMsgChangeAdmin),
+// 	}
+// }
+
+// ____________________________________________________________________________
+
+// AppModuleSimulation functions
+func (AppModule) GenerateGenesisState(simState *module.SimulationState) {
+	simulation.RandomizedGenState(simState)
+}
+
+// GenerateGenesisState creates a randomized GenState of the bank module.
+func (am AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalContent {
+	return nil
+}
+
+// RandomizedParams creates randomized bank param changes for the simulator.
+func (am AppModule) RandomizedParams(r *rand.Rand) []simtypes.ParamChange {
+	return simulation.ParamChanges(r)
+}
+
+// RegisterStoreDecoder registers a decoder for supply module's types
+func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) {
+}
+
+// WeightedOperations returns the all the gov module operations with their respective weights.
+func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation {
+	return simulation.WeightedOperations(&simState, am.keeper, am.accountKeeper, am.bankKeeper)
+}

+ 25 - 0
wormchain/x/tokenfactory/simulation/genesis.go

@@ -0,0 +1,25 @@
+package simulation
+
+import (
+	"math/rand"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func RandDenomCreationFeeParam(r *rand.Rand) sdk.Coins {
+	amount := r.Int63n(10_000_000)
+	return sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(amount)))
+}
+
+func RandomizedGenState(simstate *module.SimulationState) {
+	tfGenesis := types.DefaultGenesis()
+
+	_, err := simstate.Cdc.MarshalJSON(tfGenesis)
+	if err != nil {
+		panic(err)
+	}
+
+	simstate.GenState[types.ModuleName] = simstate.Cdc.MustMarshalJSON(tfGenesis)
+}

+ 411 - 0
wormchain/x/tokenfactory/simulation/operations.go

@@ -0,0 +1,411 @@
+package simulation
+
+import (
+	"math/rand"
+
+	"github.com/cosmos/cosmos-sdk/baseapp"
+	simappparams "github.com/cosmos/cosmos-sdk/simapp/params"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/cosmos/cosmos-sdk/types/module"
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/cosmos/cosmos-sdk/x/simulation"
+	"github.com/wormhole-foundation/wormchain/app/params"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+// Simulation operation weights constants
+//
+//nolint:gosec
+const (
+	OpWeightMsgCreateDenom      = "op_weight_msg_create_denom"
+	OpWeightMsgMint             = "op_weight_msg_mint"
+	OpWeightMsgBurn             = "op_weight_msg_burn"
+	OpWeightMsgChangeAdmin      = "op_weight_msg_change_admin"
+	OpWeightMsgSetDenomMetadata = "op_weight_msg_set_denom_metadata"
+	OpWeightMsgForceTransfer    = "op_weight_msg_force_transfer"
+)
+
+type TokenfactoryKeeper interface {
+	GetParams(ctx sdk.Context) (params types.Params)
+	GetAuthorityMetadata(ctx sdk.Context, denom string) (types.DenomAuthorityMetadata, error)
+	GetAllDenomsIterator(ctx sdk.Context) sdk.Iterator
+	GetDenomsFromCreator(ctx sdk.Context, creator string) []string
+}
+
+type BankKeeper interface {
+	simulation.BankKeeper
+	GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
+	GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
+}
+
+func WeightedOperations(
+	simstate *module.SimulationState,
+	tfKeeper TokenfactoryKeeper,
+	ak types.AccountKeeper,
+	bk BankKeeper,
+) simulation.WeightedOperations {
+	var (
+		weightMsgCreateDenom      int
+		weightMsgMint             int
+		weightMsgBurn             int
+		weightMsgChangeAdmin      int
+		weightMsgSetDenomMetadata int
+		weightMsgForceTransfer    int
+	)
+
+	simstate.AppParams.GetOrGenerate(simstate.Cdc, OpWeightMsgCreateDenom, &weightMsgCreateDenom, nil,
+		func(_ *rand.Rand) {
+			weightMsgCreateDenom = params.DefaultWeightMsgCreateDenom
+		},
+	)
+	simstate.AppParams.GetOrGenerate(simstate.Cdc, OpWeightMsgMint, &weightMsgMint, nil,
+		func(_ *rand.Rand) {
+			weightMsgMint = params.DefaultWeightMsgMint
+		},
+	)
+	simstate.AppParams.GetOrGenerate(simstate.Cdc, OpWeightMsgBurn, &weightMsgBurn, nil,
+		func(_ *rand.Rand) {
+			weightMsgBurn = params.DefaultWeightMsgBurn
+		},
+	)
+	simstate.AppParams.GetOrGenerate(simstate.Cdc, OpWeightMsgChangeAdmin, &weightMsgChangeAdmin, nil,
+		func(_ *rand.Rand) {
+			weightMsgChangeAdmin = params.DefaultWeightMsgChangeAdmin
+		},
+	)
+	simstate.AppParams.GetOrGenerate(simstate.Cdc, OpWeightMsgSetDenomMetadata, &weightMsgSetDenomMetadata, nil,
+		func(_ *rand.Rand) {
+			weightMsgSetDenomMetadata = params.DefaultWeightMsgSetDenomMetadata
+		},
+	)
+	simstate.AppParams.GetOrGenerate(simstate.Cdc, OpWeightMsgForceTransfer, &weightMsgForceTransfer, nil,
+		func(_ *rand.Rand) {
+			weightMsgForceTransfer = params.DefaultWeightMsgForceTransfer
+		},
+	)
+
+	return simulation.WeightedOperations{
+		simulation.NewWeightedOperation(
+			weightMsgCreateDenom,
+			SimulateMsgCreateDenom(
+				tfKeeper,
+				ak,
+				bk,
+			),
+		),
+		simulation.NewWeightedOperation(
+			weightMsgMint,
+			SimulateMsgMint(
+				tfKeeper,
+				ak,
+				bk,
+				DefaultSimulationDenomSelector,
+			),
+		),
+		simulation.NewWeightedOperation(
+			weightMsgBurn,
+			SimulateMsgBurn(
+				tfKeeper,
+				ak,
+				bk,
+				DefaultSimulationDenomSelector,
+			),
+		),
+		simulation.NewWeightedOperation(
+			weightMsgChangeAdmin,
+			SimulateMsgChangeAdmin(
+				tfKeeper,
+				ak,
+				bk,
+				DefaultSimulationDenomSelector,
+			),
+		),
+		simulation.NewWeightedOperation(
+			weightMsgSetDenomMetadata,
+			SimulateMsgSetDenomMetadata(
+				tfKeeper,
+				ak,
+				bk,
+				DefaultSimulationDenomSelector,
+			),
+		),
+	}
+}
+
+type DenomSelector = func(*rand.Rand, sdk.Context, TokenfactoryKeeper, string) (string, bool)
+
+func DefaultSimulationDenomSelector(r *rand.Rand, ctx sdk.Context, tfKeeper TokenfactoryKeeper, creator string) (string, bool) {
+	denoms := tfKeeper.GetDenomsFromCreator(ctx, creator)
+	if len(denoms) == 0 {
+		return "", false
+	}
+	randPos := r.Intn(len(denoms))
+
+	return denoms[randPos], true
+}
+
+func SimulateMsgSetDenomMetadata(
+	tfKeeper TokenfactoryKeeper,
+	ak types.AccountKeeper,
+	bk BankKeeper,
+	denomSelector DenomSelector,
+) simtypes.Operation {
+	return func(
+		r *rand.Rand,
+		app *baseapp.BaseApp,
+		ctx sdk.Context,
+		accs []simtypes.Account,
+		chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		// Get create denom account
+		createdDenomAccount, _ := simtypes.RandomAcc(r, accs)
+
+		// Get demon
+		denom, hasDenom := denomSelector(r, ctx, tfKeeper, createdDenomAccount.Address.String())
+		if !hasDenom {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgSetDenomMetadata{}.Type(), "sim account have no denom created"), nil, nil
+		}
+
+		// Get admin of the denom
+		authData, err := tfKeeper.GetAuthorityMetadata(ctx, denom)
+		if err != nil {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgSetDenomMetadata{}.Type(), "err authority metadata"), nil, err
+		}
+		adminAccount, found := simtypes.FindAccount(accs, sdk.MustAccAddressFromBech32(authData.Admin))
+		if !found {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgSetDenomMetadata{}.Type(), "admin account not found"), nil, nil
+		}
+
+		metadata := banktypes.Metadata{
+			Description: simtypes.RandStringOfLength(r, 10),
+			DenomUnits: []*banktypes.DenomUnit{{
+				Denom:    denom,
+				Exponent: 0,
+			}},
+			Base:    denom,
+			Display: denom,
+			Name:    simtypes.RandStringOfLength(r, 10),
+			Symbol:  simtypes.RandStringOfLength(r, 10),
+		}
+
+		msg := types.MsgSetDenomMetadata{
+			Sender:   adminAccount.Address.String(),
+			Metadata: metadata,
+		}
+
+		txCtx := BuildOperationInput(r, app, ctx, &msg, adminAccount, ak, bk, nil)
+		return simulation.GenAndDeliverTxWithRandFees(txCtx)
+	}
+}
+
+func SimulateMsgChangeAdmin(
+	tfKeeper TokenfactoryKeeper,
+	ak types.AccountKeeper,
+	bk BankKeeper,
+	denomSelector DenomSelector,
+) simtypes.Operation {
+	return func(
+		r *rand.Rand,
+		app *baseapp.BaseApp,
+		ctx sdk.Context,
+		accs []simtypes.Account,
+		chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		// Get create denom account
+		createdDenomAccount, _ := simtypes.RandomAcc(r, accs)
+
+		// Get demon
+		denom, hasDenom := denomSelector(r, ctx, tfKeeper, createdDenomAccount.Address.String())
+		if !hasDenom {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgChangeAdmin{}.Type(), "sim account have no denom created"), nil, nil
+		}
+
+		// Get admin of the denom
+		authData, err := tfKeeper.GetAuthorityMetadata(ctx, denom)
+		if err != nil {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgChangeAdmin{}.Type(), "err authority metadata"), nil, err
+		}
+		curAdminAccount, found := simtypes.FindAccount(accs, sdk.MustAccAddressFromBech32(authData.Admin))
+		if !found {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgChangeAdmin{}.Type(), "admin account not found"), nil, nil
+		}
+
+		// Rand new admin account
+		newAdmin, _ := simtypes.RandomAcc(r, accs)
+		if newAdmin.Address.String() == curAdminAccount.Address.String() {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgChangeAdmin{}.Type(), "new admin cannot be the same as current admin"), nil, nil
+		}
+
+		// Create msg
+		msg := types.MsgChangeAdmin{
+			Sender:   curAdminAccount.Address.String(),
+			Denom:    denom,
+			NewAdmin: newAdmin.Address.String(),
+		}
+
+		txCtx := BuildOperationInput(r, app, ctx, &msg, curAdminAccount, ak, bk, nil)
+		return simulation.GenAndDeliverTxWithRandFees(txCtx)
+	}
+}
+
+func SimulateMsgBurn(
+	tfKeeper TokenfactoryKeeper,
+	ak types.AccountKeeper,
+	bk BankKeeper,
+	denomSelector DenomSelector,
+) simtypes.Operation {
+	return func(
+		r *rand.Rand,
+		app *baseapp.BaseApp,
+		ctx sdk.Context,
+		accs []simtypes.Account,
+		chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		// Get create denom account
+		createdDenomAccount, _ := simtypes.RandomAcc(r, accs)
+
+		// Get demon
+		denom, hasDenom := denomSelector(r, ctx, tfKeeper, createdDenomAccount.Address.String())
+		if !hasDenom {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgBurn{}.Type(), "sim account have no denom created"), nil, nil
+		}
+
+		// Get admin of the denom
+		authData, err := tfKeeper.GetAuthorityMetadata(ctx, denom)
+		if err != nil {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgBurn{}.Type(), "err authority metadata"), nil, err
+		}
+		adminAccount, found := simtypes.FindAccount(accs, sdk.MustAccAddressFromBech32(authData.Admin))
+		if !found {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgBurn{}.Type(), "admin account not found"), nil, nil
+		}
+
+		// Check if admin account balance = 0
+		accountBalance := bk.GetBalance(ctx, adminAccount.Address, denom)
+		if accountBalance.Amount.LTE(sdk.ZeroInt()) {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgBurn{}.Type(), "sim account have no balance"), nil, nil
+		}
+
+		// Rand burn amount
+		amount, _ := simtypes.RandPositiveInt(r, accountBalance.Amount)
+		burnAmount := sdk.NewCoin(denom, amount)
+
+		// Create msg
+		msg := types.MsgBurn{
+			Sender: adminAccount.Address.String(),
+			Amount: burnAmount,
+		}
+
+		txCtx := BuildOperationInput(r, app, ctx, &msg, adminAccount, ak, bk, sdk.NewCoins(burnAmount))
+		return simulation.GenAndDeliverTxWithRandFees(txCtx)
+	}
+}
+
+// Simulate msg mint denom
+func SimulateMsgMint(
+	tfKeeper TokenfactoryKeeper,
+	ak types.AccountKeeper,
+	bk BankKeeper,
+	denomSelector DenomSelector,
+) simtypes.Operation {
+	return func(
+		r *rand.Rand,
+		app *baseapp.BaseApp,
+		ctx sdk.Context,
+		accs []simtypes.Account,
+		chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		// Get create denom account
+		createdDenomAccount, _ := simtypes.RandomAcc(r, accs)
+
+		// Get demon
+		denom, hasDenom := denomSelector(r, ctx, tfKeeper, createdDenomAccount.Address.String())
+		if !hasDenom {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgMint{}.Type(), "sim account have no denom created"), nil, nil
+		}
+
+		// Get admin of the denom
+		authData, err := tfKeeper.GetAuthorityMetadata(ctx, denom)
+		if err != nil {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgMint{}.Type(), "err authority metadata"), nil, err
+		}
+		adminAccount, found := simtypes.FindAccount(accs, sdk.MustAccAddressFromBech32(authData.Admin))
+		if !found {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgMint{}.Type(), "admin account not found"), nil, nil
+		}
+
+		// Rand mint amount
+		mintAmount, _ := simtypes.RandPositiveInt(r, sdk.NewIntFromUint64(100_000_000))
+
+		// Create msg mint
+		msg := types.MsgMint{
+			Sender: adminAccount.Address.String(),
+			Amount: sdk.NewCoin(denom, mintAmount),
+		}
+
+		txCtx := BuildOperationInput(r, app, ctx, &msg, adminAccount, ak, bk, nil)
+		return simulation.GenAndDeliverTxWithRandFees(txCtx)
+	}
+}
+
+// Simulate msg create denom
+func SimulateMsgCreateDenom(tfKeeper TokenfactoryKeeper, ak types.AccountKeeper, bk BankKeeper) simtypes.Operation {
+	return func(
+		r *rand.Rand,
+		app *baseapp.BaseApp,
+		ctx sdk.Context,
+		accs []simtypes.Account,
+		chainID string,
+	) (simtypes.OperationMsg, []simtypes.FutureOperation, error) {
+		// Get sims account
+		simAccount, _ := simtypes.RandomAcc(r, accs)
+
+		// Check if sims account enough create fee
+		createFee := tfKeeper.GetParams(ctx).DenomCreationFee
+		balances := bk.GetAllBalances(ctx, simAccount.Address)
+		_, hasNeg := balances.SafeSub(createFee)
+		if hasNeg {
+			return simtypes.NoOpMsg(types.ModuleName, types.MsgCreateDenom{}.Type(), "Creator not enough creation fee"), nil, nil
+		}
+
+		// Create msg create denom
+		msg := types.MsgCreateDenom{
+			Sender:   simAccount.Address.String(),
+			Subdenom: simtypes.RandStringOfLength(r, 10),
+		}
+
+		txCtx := BuildOperationInput(r, app, ctx, &msg, simAccount, ak, bk, createFee)
+		return simulation.GenAndDeliverTxWithRandFees(txCtx)
+	}
+}
+
+// BuildOperationInput helper to build object
+func BuildOperationInput(
+	r *rand.Rand,
+	app *baseapp.BaseApp,
+	ctx sdk.Context,
+	msg interface {
+		sdk.Msg
+		Type() string
+	},
+	simAccount simtypes.Account,
+	ak types.AccountKeeper,
+	bk BankKeeper,
+	deposit sdk.Coins,
+) simulation.OperationInput {
+	return simulation.OperationInput{
+		R:               r,
+		App:             app,
+		TxGen:           simappparams.MakeTestEncodingConfig().TxConfig,
+		Cdc:             nil,
+		Msg:             msg,
+		MsgType:         msg.Type(),
+		Context:         ctx,
+		SimAccount:      simAccount,
+		AccountKeeper:   ak,
+		Bankkeeper:      bk,
+		ModuleName:      types.ModuleName,
+		CoinsSpentInMsg: deposit,
+	}
+}

+ 23 - 0
wormchain/x/tokenfactory/simulation/params.go

@@ -0,0 +1,23 @@
+package simulation
+
+import (
+	"fmt"
+	"math/rand"
+
+	simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
+	"github.com/cosmos/cosmos-sdk/x/simulation"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func ParamChanges(_ *rand.Rand) []simtypes.ParamChange {
+	return []simtypes.ParamChange{
+		simulation.NewSimParamChange(
+			types.ModuleName,
+			string(types.KeyDenomCreationFee),
+			func(r *rand.Rand) string {
+				amount := RandDenomCreationFeeParam(r)
+				return fmt.Sprintf("[{\"denom\":\"%v\",\"amount\":\"%v\"}]", amount[0].Denom, amount[0].Amount)
+			},
+		),
+	}
+}

+ 8 - 0
wormchain/x/tokenfactory/testhelpers/consts.go

@@ -0,0 +1,8 @@
+package testhelpers
+
+import sdk "github.com/cosmos/cosmos-sdk/types"
+
+var (
+	SecondaryDenom  = "uion"
+	SecondaryAmount = sdk.NewInt(100000000)
+)

+ 66 - 0
wormchain/x/tokenfactory/testhelpers/suite.go

@@ -0,0 +1,66 @@
+package testhelpers
+
+import (
+	"encoding/json"
+	"testing"
+	"time"
+
+	"github.com/cosmos/cosmos-sdk/x/authz"
+	"github.com/stretchr/testify/require"
+
+	"github.com/cosmos/cosmos-sdk/codec"
+	cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
+	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+var (
+	Amino          = codec.NewLegacyAmino()
+	AuthzModuleCdc = codec.NewAminoCodec(Amino)
+)
+
+func init() {
+	cryptocodec.RegisterCrypto(Amino)
+	codec.RegisterEvidences(Amino)
+	sdk.RegisterLegacyAminoCodec(Amino)
+}
+
+func TestMessageAuthzSerialization(t *testing.T, msg sdk.Msg) {
+	someDate := time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)
+	const (
+		mockGranter string = "cosmos1abc"
+		mockGrantee string = "cosmos1xyz"
+	)
+
+	var (
+		mockMsgGrant  authz.MsgGrant
+		mockMsgRevoke authz.MsgRevoke
+		mockMsgExec   authz.MsgExec
+	)
+
+	// Authz: Grant Msg
+	typeURL := sdk.MsgTypeURL(msg)
+	later := someDate.Add(time.Hour)
+	grant, err := authz.NewGrant(authz.NewGenericAuthorization(typeURL), later)
+	require.NoError(t, err)
+
+	msgGrant := authz.MsgGrant{Granter: mockGranter, Grantee: mockGrantee, Grant: grant}
+	msgGrantBytes := json.RawMessage(sdk.MustSortJSON(AuthzModuleCdc.MustMarshalJSON(&msgGrant)))
+	err = AuthzModuleCdc.UnmarshalJSON(msgGrantBytes, &mockMsgGrant)
+	require.NoError(t, err)
+
+	// Authz: Revoke Msg
+	msgRevoke := authz.MsgRevoke{Granter: mockGranter, Grantee: mockGrantee, MsgTypeUrl: typeURL}
+	msgRevokeByte := json.RawMessage(sdk.MustSortJSON(AuthzModuleCdc.MustMarshalJSON(&msgRevoke)))
+	err = AuthzModuleCdc.UnmarshalJSON(msgRevokeByte, &mockMsgRevoke)
+	require.NoError(t, err)
+
+	// Authz: Exec Msg
+	msgAny, err := cdctypes.NewAnyWithValue(msg)
+	require.NoError(t, err)
+	msgExec := authz.MsgExec{Grantee: mockGrantee, Msgs: []*cdctypes.Any{msgAny}}
+	execMsgByte := json.RawMessage(sdk.MustSortJSON(AuthzModuleCdc.MustMarshalJSON(&msgExec)))
+	err = AuthzModuleCdc.UnmarshalJSON(execMsgByte, &mockMsgExec)
+	require.NoError(t, err)
+	require.Equal(t, msgExec.Msgs[0].Value, mockMsgExec.Msgs[0].Value)
+}

+ 15 - 0
wormchain/x/tokenfactory/types/authorityMetadata.go

@@ -0,0 +1,15 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+func (metadata DenomAuthorityMetadata) Validate() error {
+	if metadata.Admin != "" {
+		_, err := sdk.AccAddressFromBech32(metadata.Admin)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}

+ 352 - 0
wormchain/x/tokenfactory/types/authorityMetadata.pb.go

@@ -0,0 +1,352 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: osmosis/tokenfactory/v1beta1/authorityMetadata.proto
+
+package types
+
+import (
+	fmt "fmt"
+	_ "github.com/cosmos/cosmos-sdk/types"
+	_ "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
+
+// DenomAuthorityMetadata specifies metadata for addresses that have specific
+// capabilities over a token factory denom. Right now there is only one Admin
+// permission, but is planned to be extended to the future.
+type DenomAuthorityMetadata struct {
+	// Can be empty for no admin, or a valid osmosis address
+	Admin string `protobuf:"bytes,1,opt,name=admin,proto3" json:"admin,omitempty" yaml:"admin"`
+}
+
+func (m *DenomAuthorityMetadata) Reset()         { *m = DenomAuthorityMetadata{} }
+func (m *DenomAuthorityMetadata) String() string { return proto.CompactTextString(m) }
+func (*DenomAuthorityMetadata) ProtoMessage()    {}
+func (*DenomAuthorityMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_99435de88ae175f7, []int{0}
+}
+func (m *DenomAuthorityMetadata) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *DenomAuthorityMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_DenomAuthorityMetadata.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 *DenomAuthorityMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_DenomAuthorityMetadata.Merge(m, src)
+}
+func (m *DenomAuthorityMetadata) XXX_Size() int {
+	return m.Size()
+}
+func (m *DenomAuthorityMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_DenomAuthorityMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DenomAuthorityMetadata proto.InternalMessageInfo
+
+func (m *DenomAuthorityMetadata) GetAdmin() string {
+	if m != nil {
+		return m.Admin
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*DenomAuthorityMetadata)(nil), "osmosis.tokenfactory.v1beta1.DenomAuthorityMetadata")
+}
+
+func init() {
+	proto.RegisterFile("osmosis/tokenfactory/v1beta1/authorityMetadata.proto", fileDescriptor_99435de88ae175f7)
+}
+
+var fileDescriptor_99435de88ae175f7 = []byte{
+	// 252 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0xc9, 0x2f, 0xce, 0xcd,
+	0x2f, 0xce, 0x2c, 0xd6, 0x2f, 0xc9, 0xcf, 0x4e, 0xcd, 0x4b, 0x4b, 0x4c, 0x2e, 0xc9, 0x2f, 0xaa,
+	0xd4, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0x2c, 0x2d, 0xc9, 0xc8, 0x2f, 0xca,
+	0x2c, 0xa9, 0xf4, 0x4d, 0x2d, 0x49, 0x4c, 0x49, 0x2c, 0x49, 0xd4, 0x2b, 0x28, 0xca, 0x2f, 0xc9,
+	0x17, 0x92, 0x81, 0xea, 0xd2, 0x43, 0xd6, 0xa5, 0x07, 0xd5, 0x25, 0x25, 0x92, 0x9e, 0x9f, 0x9e,
+	0x0f, 0x56, 0xa8, 0x0f, 0x62, 0x41, 0xf4, 0x48, 0xc9, 0x25, 0x83, 0x35, 0xe9, 0x27, 0x25, 0x16,
+	0xa7, 0xc2, 0x2d, 0x48, 0xce, 0xcf, 0xcc, 0x83, 0xc8, 0x2b, 0xb9, 0x71, 0x89, 0xb9, 0xa4, 0xe6,
+	0xe5, 0xe7, 0x3a, 0xa2, 0xdb, 0x29, 0xa4, 0xc6, 0xc5, 0x9a, 0x98, 0x92, 0x9b, 0x99, 0x27, 0xc1,
+	0xa8, 0xc0, 0xa8, 0xc1, 0xe9, 0x24, 0xf0, 0xe9, 0x9e, 0x3c, 0x4f, 0x65, 0x62, 0x6e, 0x8e, 0x95,
+	0x12, 0x58, 0x58, 0x29, 0x08, 0x22, 0x6d, 0xc5, 0xf2, 0x62, 0x81, 0x3c, 0xa3, 0x53, 0xf8, 0x89,
+	0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, 0x1c, 0xc3,
+	0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xd9, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26,
+	0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x97, 0xe7, 0x17, 0xe5, 0x66, 0xe4, 0xe7, 0xa4, 0xea, 0xa6, 0xe5,
+	0x97, 0xe6, 0xa5, 0x24, 0x96, 0x64, 0xe6, 0xe7, 0x81, 0xc5, 0x92, 0x33, 0x12, 0x33, 0xf3, 0xf4,
+	0x2b, 0x50, 0x83, 0xa3, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0xec, 0x4e, 0x63, 0x40, 0x00,
+	0x00, 0x00, 0xff, 0xff, 0xca, 0xa6, 0x48, 0xf8, 0x33, 0x01, 0x00, 0x00,
+}
+
+func (this *DenomAuthorityMetadata) Equal(that interface{}) bool {
+	if that == nil {
+		return this == nil
+	}
+
+	that1, ok := that.(*DenomAuthorityMetadata)
+	if !ok {
+		that2, ok := that.(DenomAuthorityMetadata)
+		if ok {
+			that1 = &that2
+		} else {
+			return false
+		}
+	}
+	if that1 == nil {
+		return this == nil
+	} else if this == nil {
+		return false
+	}
+	if this.Admin != that1.Admin {
+		return false
+	}
+	return true
+}
+func (m *DenomAuthorityMetadata) 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 *DenomAuthorityMetadata) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *DenomAuthorityMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Admin) > 0 {
+		i -= len(m.Admin)
+		copy(dAtA[i:], m.Admin)
+		i = encodeVarintAuthorityMetadata(dAtA, i, uint64(len(m.Admin)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintAuthorityMetadata(dAtA []byte, offset int, v uint64) int {
+	offset -= sovAuthorityMetadata(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *DenomAuthorityMetadata) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Admin)
+	if l > 0 {
+		n += 1 + l + sovAuthorityMetadata(uint64(l))
+	}
+	return n
+}
+
+func sovAuthorityMetadata(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozAuthorityMetadata(x uint64) (n int) {
+	return sovAuthorityMetadata(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *DenomAuthorityMetadata) 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 ErrIntOverflowAuthorityMetadata
+			}
+			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: DenomAuthorityMetadata: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: DenomAuthorityMetadata: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Admin", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAuthorityMetadata
+				}
+				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 ErrInvalidLengthAuthorityMetadata
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthAuthorityMetadata
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Admin = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipAuthorityMetadata(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthAuthorityMetadata
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipAuthorityMetadata(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowAuthorityMetadata
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowAuthorityMetadata
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowAuthorityMetadata
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthAuthorityMetadata
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupAuthorityMetadata
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthAuthorityMetadata
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthAuthorityMetadata        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowAuthorityMetadata          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupAuthorityMetadata = fmt.Errorf("proto: unexpected end of group")
+)

+ 24 - 0
wormchain/x/tokenfactory/types/authzcodec/codec.go

@@ -0,0 +1,24 @@
+package authzcodec
+
+// Note: this file is a copy from authz/codec in 0.46 so we can be compatible with 0.45
+
+import (
+	"github.com/cosmos/cosmos-sdk/codec"
+	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+)
+
+var (
+	Amino     = codec.NewLegacyAmino()
+	ModuleCdc = codec.NewAminoCodec(Amino)
+)
+
+func init() {
+	// Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be
+	// used to properly serialize MsgGrant and MsgExec instances
+	sdk.RegisterLegacyAminoCodec(Amino)
+	cryptocodec.RegisterCrypto(Amino)
+	codec.RegisterEvidences(Amino)
+
+	Amino.Seal()
+}

+ 21 - 0
wormchain/x/tokenfactory/types/capabilities.go

@@ -0,0 +1,21 @@
+package types
+
+const (
+	EnableSetMetadata   = "enable_metadata"
+	EnableForceTransfer = "enable_force_transfer"
+	EnableBurnFrom      = "enable_burn_from"
+)
+
+func IsCapabilityEnabled(enabledCapabilities []string, capability string) bool {
+	if len(enabledCapabilities) == 0 {
+		return false
+	}
+
+	for _, v := range enabledCapabilities {
+		if v == capability {
+			return true
+		}
+	}
+
+	return false
+}

+ 48 - 0
wormchain/x/tokenfactory/types/codec.go

@@ -0,0 +1,48 @@
+package types
+
+import (
+	"github.com/cosmos/cosmos-sdk/codec"
+	cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
+	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	// this line is used by starport scaffolding # 1
+	"github.com/cosmos/cosmos-sdk/types/msgservice"
+)
+
+func RegisterCodec(cdc *codec.LegacyAmino) {
+	cdc.RegisterConcrete(&MsgCreateDenom{}, "osmosis/tokenfactory/create-denom", nil)
+	cdc.RegisterConcrete(&MsgMint{}, "osmosis/tokenfactory/mint", nil)
+	cdc.RegisterConcrete(&MsgBurn{}, "osmosis/tokenfactory/burn", nil)
+	cdc.RegisterConcrete(&MsgForceTransfer{}, "osmosis/tokenfactory/force-transfer", nil)
+	cdc.RegisterConcrete(&MsgChangeAdmin{}, "osmosis/tokenfactory/change-admin", nil)
+}
+
+func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
+	registry.RegisterImplementations(
+		(*sdk.Msg)(nil),
+		&MsgCreateDenom{},
+		&MsgMint{},
+		&MsgBurn{},
+		// &MsgForceTransfer{},
+		&MsgChangeAdmin{},
+	)
+	msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
+}
+
+var (
+	amino     = codec.NewLegacyAmino()
+	ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry())
+)
+
+func init() {
+	RegisterCodec(amino)
+	// Register all Amino interfaces and concrete types on the authz Amino codec so that this can later be
+	// used to properly serialize MsgGrant and MsgExec instances
+	// Note: these 3 are inlines from authz/codec in 0.46 so we can be compatible with 0.45
+	sdk.RegisterLegacyAminoCodec(amino)
+	cryptocodec.RegisterCrypto(amino)
+	codec.RegisterEvidences(amino)
+
+	amino.Seal()
+}

+ 68 - 0
wormchain/x/tokenfactory/types/denoms.go

@@ -0,0 +1,68 @@
+package types
+
+import (
+	"strings"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+)
+
+const (
+	ModuleDenomPrefix = "factory"
+	// See the TokenFactory readme for a derivation of these.
+	// TL;DR, MaxSubdenomLength + MaxHrpLength = 60 comes from SDK max denom length = 128
+	// and the structure of tokenfactory denoms.
+	MaxSubdenomLength = 44
+	MaxHrpLength      = 16
+	// MaxCreatorLength = 59 + MaxHrpLength
+	MaxCreatorLength = 59 + MaxHrpLength
+)
+
+// GetTokenDenom constructs a denom string for tokens created by tokenfactory
+// based on an input creator address and a subdenom
+// The denom constructed is factory/{creator}/{subdenom}
+func GetTokenDenom(creator, subdenom string) (string, error) {
+	if len(subdenom) > MaxSubdenomLength {
+		return "", ErrSubdenomTooLong
+	}
+	if len(creator) > MaxCreatorLength {
+		return "", ErrCreatorTooLong
+	}
+	if strings.Contains(creator, "/") {
+		return "", ErrInvalidCreator
+	}
+	denom := strings.Join([]string{ModuleDenomPrefix, creator, subdenom}, "/")
+	return denom, sdk.ValidateDenom(denom)
+}
+
+// DeconstructDenom takes a token denom string and verifies that it is a valid
+// denom of the tokenfactory module, and is of the form `factory/{creator}/{subdenom}`
+// If valid, it returns the creator address and subdenom
+func DeconstructDenom(denom string) (creator string, subdenom string, err error) {
+	err = sdk.ValidateDenom(denom)
+	if err != nil {
+		return "", "", err
+	}
+
+	strParts := strings.Split(denom, "/")
+	if len(strParts) < 3 {
+		return "", "", sdkerrors.Wrapf(ErrInvalidDenom, "not enough parts of denom %s", denom)
+	}
+
+	if strParts[0] != ModuleDenomPrefix {
+		return "", "", sdkerrors.Wrapf(ErrInvalidDenom, "denom prefix is incorrect. Is: %s.  Should be: %s", strParts[0], ModuleDenomPrefix)
+	}
+
+	creator = strParts[1]
+	creatorAddr, err := sdk.AccAddressFromBech32(creator)
+	if err != nil {
+		return "", "", sdkerrors.Wrapf(ErrInvalidDenom, "Invalid creator address (%s)", err)
+	}
+
+	// Handle the case where a denom has a slash in its subdenom. For example,
+	// when we did the split, we'd turn factory/accaddr/atomderivative/sikka into ["factory", "accaddr", "atomderivative", "sikka"]
+	// So we have to join [2:] with a "/" as the delimiter to get back the correct subdenom which should be "atomderivative/sikka"
+	subdenom = strings.Join(strParts[2:], "/")
+
+	return creatorAddr.String(), subdenom, nil
+}

+ 132 - 0
wormchain/x/tokenfactory/types/denoms_test.go

@@ -0,0 +1,132 @@
+package types_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func TestDeconstructDenom(t *testing.T) {
+	// Note: this seems to be used in osmosis to add some more checks (only 20 or 32 byte addresses),
+	// which is good, but not required for these tests as they make code less reuable
+	// appparams.SetAddressPrefixes()
+
+	for _, tc := range []struct {
+		desc             string
+		denom            string
+		expectedSubdenom string
+		err              error
+	}{
+		{
+			desc:  "empty is invalid",
+			denom: "",
+			err:   types.ErrInvalidDenom,
+		},
+		{
+			desc:             "normal",
+			denom:            "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+			expectedSubdenom: "bitcoin",
+		},
+		{
+			desc:             "multiple slashes in subdenom",
+			denom:            "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin/1",
+			expectedSubdenom: "bitcoin/1",
+		},
+		{
+			desc:             "no subdenom",
+			denom:            "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/",
+			expectedSubdenom: "",
+		},
+		{
+			desc:  "incorrect prefix",
+			denom: "ibc/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+			err:   types.ErrInvalidDenom,
+		},
+		{
+			desc:             "subdenom of only slashes",
+			denom:            "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/////",
+			expectedSubdenom: "////",
+		},
+		{
+			desc:  "too long name",
+			denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/adsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsf",
+			err:   types.ErrInvalidDenom,
+		},
+	} {
+		t.Run(tc.desc, func(t *testing.T) {
+			expectedCreator := "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8"
+			creator, subdenom, err := types.DeconstructDenom(tc.denom)
+			if tc.err != nil {
+				require.ErrorContains(t, err, tc.err.Error())
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, expectedCreator, creator)
+				require.Equal(t, tc.expectedSubdenom, subdenom)
+			}
+		})
+	}
+}
+
+func TestGetTokenDenom(t *testing.T) {
+	// appparams.SetAddressPrefixes()
+	for _, tc := range []struct {
+		desc     string
+		creator  string
+		subdenom string
+		valid    bool
+	}{
+		{
+			desc:     "normal",
+			creator:  "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+			subdenom: "bitcoin",
+			valid:    true,
+		},
+		{
+			desc:     "multiple slashes in subdenom",
+			creator:  "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+			subdenom: "bitcoin/1",
+			valid:    true,
+		},
+		{
+			desc:     "no subdenom",
+			creator:  "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+			subdenom: "",
+			valid:    true,
+		},
+		{
+			desc:     "subdenom of only slashes",
+			creator:  "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+			subdenom: "/////",
+			valid:    true,
+		},
+		{
+			desc:     "too long name",
+			creator:  "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+			subdenom: "adsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsf",
+			valid:    false,
+		},
+		{
+			desc:     "subdenom is exactly max length",
+			creator:  "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+			subdenom: "bitcoinfsadfsdfeadfsafwefsefsefsdfsdafasefsf",
+			valid:    true,
+		},
+		{
+			desc:     "creator is exactly max length",
+			creator:  "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8jhgjhgkhjklhkjhkjhgjhgjgjghelu",
+			subdenom: "bitcoin",
+			valid:    true,
+		},
+	} {
+		t.Run(tc.desc, func(t *testing.T) {
+			_, err := types.GetTokenDenom(tc.creator, tc.subdenom)
+			if tc.valid {
+				require.NoError(t, err)
+			} else {
+				require.Error(t, err)
+			}
+		})
+	}
+}

+ 23 - 0
wormchain/x/tokenfactory/types/errors.go

@@ -0,0 +1,23 @@
+package types
+
+// DONTCOVER
+
+import (
+	fmt "fmt"
+
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+)
+
+// x/tokenfactory module sentinel errors
+var (
+	ErrDenomExists              = sdkerrors.Register(ModuleName, 2, "attempting to create a denom that already exists (has bank metadata)")
+	ErrUnauthorized             = sdkerrors.Register(ModuleName, 3, "unauthorized account")
+	ErrInvalidDenom             = sdkerrors.Register(ModuleName, 4, "invalid denom")
+	ErrInvalidCreator           = sdkerrors.Register(ModuleName, 5, "invalid creator")
+	ErrInvalidAuthorityMetadata = sdkerrors.Register(ModuleName, 6, "invalid authority metadata")
+	ErrInvalidGenesis           = sdkerrors.Register(ModuleName, 7, "invalid genesis")
+	ErrSubdenomTooLong          = sdkerrors.Register(ModuleName, 8, fmt.Sprintf("subdenom too long, max length is %d bytes", MaxSubdenomLength))
+	ErrCreatorTooLong           = sdkerrors.Register(ModuleName, 9, fmt.Sprintf("creator too long, max length is %d bytes", MaxCreatorLength))
+	ErrDenomDoesNotExist        = sdkerrors.Register(ModuleName, 10, "denom does not exist")
+	ErrCapabilityNotEnabled     = sdkerrors.Register(ModuleName, 11, "this capability is not enabled on chain")
+)

+ 18 - 0
wormchain/x/tokenfactory/types/events.go

@@ -0,0 +1,18 @@
+package types
+
+// event types
+//
+//nolint:gosec
+const (
+	AttributeAmount              = "amount"
+	AttributeCreator             = "creator"
+	AttributeSubdenom            = "subdenom"
+	AttributeNewTokenDenom       = "new_token_denom"
+	AttributeMintToAddress       = "mint_to_address"
+	AttributeBurnFromAddress     = "burn_from_address"
+	AttributeTransferFromAddress = "transfer_from_address"
+	AttributeTransferToAddress   = "transfer_to_address"
+	AttributeDenom               = "denom"
+	AttributeNewAdmin            = "new_admin"
+	AttributeDenomMetadata       = "denom_metadata"
+)

+ 38 - 0
wormchain/x/tokenfactory/types/expected_keepers.go

@@ -0,0 +1,38 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+)
+
+type BankKeeper interface {
+	// Methods imported from bank should be defined here
+	GetDenomMetaData(ctx sdk.Context, denom string) (banktypes.Metadata, bool)
+	SetDenomMetaData(ctx sdk.Context, denomMetaData banktypes.Metadata)
+
+	HasSupply(ctx sdk.Context, denom string) bool
+
+	SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
+	SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
+	MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
+	BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error
+
+	SendCoins(ctx sdk.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
+	HasBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdk.Coin) bool
+	GetAllBalances(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
+	SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins
+	GetBalance(ctx sdk.Context, addr sdk.AccAddress, denom string) sdk.Coin
+
+	BlockedAddr(addr sdk.AccAddress) bool
+}
+
+type AccountKeeper interface {
+	SetModuleAccount(ctx sdk.Context, macc authtypes.ModuleAccountI)
+	GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI
+}
+
+// CommunityPoolKeeper defines the contract needed to be fulfilled for community pool interactions.
+type CommunityPoolKeeper interface {
+	FundCommunityPool(ctx sdk.Context, amount sdk.Coins, sender sdk.AccAddress) error
+}

+ 51 - 0
wormchain/x/tokenfactory/types/genesis.go

@@ -0,0 +1,51 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+)
+
+// this line is used by starport scaffolding # genesis/types/import
+
+// DefaultIndex is the default capability global index
+const DefaultIndex uint64 = 1
+
+// DefaultGenesis returns the default Capability genesis state
+func DefaultGenesis() *GenesisState {
+	return &GenesisState{
+		Params:        DefaultParams(),
+		FactoryDenoms: []GenesisDenom{},
+	}
+}
+
+// Validate performs basic genesis state validation returning an error upon any
+// failure.
+func (gs GenesisState) Validate() error {
+	err := gs.Params.Validate()
+	if err != nil {
+		return err
+	}
+
+	seenDenoms := map[string]bool{}
+
+	for _, denom := range gs.GetFactoryDenoms() {
+		if seenDenoms[denom.GetDenom()] {
+			return sdkerrors.Wrapf(ErrInvalidGenesis, "duplicate denom: %s", denom.GetDenom())
+		}
+		seenDenoms[denom.GetDenom()] = true
+
+		_, _, err := DeconstructDenom(denom.GetDenom())
+		if err != nil {
+			return err
+		}
+
+		if denom.AuthorityMetadata.Admin != "" {
+			_, err = sdk.AccAddressFromBech32(denom.AuthorityMetadata.Admin)
+			if err != nil {
+				return sdkerrors.Wrapf(ErrInvalidAuthorityMetadata, "Invalid admin address (%s)", err)
+			}
+		}
+	}
+
+	return nil
+}

+ 650 - 0
wormchain/x/tokenfactory/types/genesis.pb.go

@@ -0,0 +1,650 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: osmosis/tokenfactory/v1beta1/genesis.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
+
+// GenesisState defines the tokenfactory module's genesis state.
+type GenesisState struct {
+	// params defines the paramaters of the module.
+	Params        Params         `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
+	FactoryDenoms []GenesisDenom `protobuf:"bytes,2,rep,name=factory_denoms,json=factoryDenoms,proto3" json:"factory_denoms" yaml:"factory_denoms"`
+}
+
+func (m *GenesisState) Reset()         { *m = GenesisState{} }
+func (m *GenesisState) String() string { return proto.CompactTextString(m) }
+func (*GenesisState) ProtoMessage()    {}
+func (*GenesisState) Descriptor() ([]byte, []int) {
+	return fileDescriptor_5749c3f71850298b, []int{0}
+}
+func (m *GenesisState) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_GenesisState.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 *GenesisState) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GenesisState.Merge(m, src)
+}
+func (m *GenesisState) XXX_Size() int {
+	return m.Size()
+}
+func (m *GenesisState) XXX_DiscardUnknown() {
+	xxx_messageInfo_GenesisState.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GenesisState proto.InternalMessageInfo
+
+func (m *GenesisState) GetParams() Params {
+	if m != nil {
+		return m.Params
+	}
+	return Params{}
+}
+
+func (m *GenesisState) GetFactoryDenoms() []GenesisDenom {
+	if m != nil {
+		return m.FactoryDenoms
+	}
+	return nil
+}
+
+// GenesisDenom defines a tokenfactory denom that is defined within genesis
+// state. The structure contains DenomAuthorityMetadata which defines the
+// denom's admin.
+type GenesisDenom struct {
+	Denom             string                 `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty" yaml:"denom"`
+	AuthorityMetadata DenomAuthorityMetadata `protobuf:"bytes,2,opt,name=authority_metadata,json=authorityMetadata,proto3" json:"authority_metadata" yaml:"authority_metadata"`
+}
+
+func (m *GenesisDenom) Reset()         { *m = GenesisDenom{} }
+func (m *GenesisDenom) String() string { return proto.CompactTextString(m) }
+func (*GenesisDenom) ProtoMessage()    {}
+func (*GenesisDenom) Descriptor() ([]byte, []int) {
+	return fileDescriptor_5749c3f71850298b, []int{1}
+}
+func (m *GenesisDenom) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *GenesisDenom) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_GenesisDenom.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 *GenesisDenom) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GenesisDenom.Merge(m, src)
+}
+func (m *GenesisDenom) XXX_Size() int {
+	return m.Size()
+}
+func (m *GenesisDenom) XXX_DiscardUnknown() {
+	xxx_messageInfo_GenesisDenom.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GenesisDenom proto.InternalMessageInfo
+
+func (m *GenesisDenom) GetDenom() string {
+	if m != nil {
+		return m.Denom
+	}
+	return ""
+}
+
+func (m *GenesisDenom) GetAuthorityMetadata() DenomAuthorityMetadata {
+	if m != nil {
+		return m.AuthorityMetadata
+	}
+	return DenomAuthorityMetadata{}
+}
+
+func init() {
+	proto.RegisterType((*GenesisState)(nil), "osmosis.tokenfactory.v1beta1.GenesisState")
+	proto.RegisterType((*GenesisDenom)(nil), "osmosis.tokenfactory.v1beta1.GenesisDenom")
+}
+
+func init() {
+	proto.RegisterFile("osmosis/tokenfactory/v1beta1/genesis.proto", fileDescriptor_5749c3f71850298b)
+}
+
+var fileDescriptor_5749c3f71850298b = []byte{
+	// 378 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0xca, 0x2f, 0xce, 0xcd,
+	0x2f, 0xce, 0x2c, 0xd6, 0x2f, 0xc9, 0xcf, 0x4e, 0xcd, 0x4b, 0x4b, 0x4c, 0x2e, 0xc9, 0x2f, 0xaa,
+	0xd4, 0x2f, 0x33, 0x4c, 0x4a, 0x2d, 0x49, 0x34, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c,
+	0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x92, 0x81, 0xaa, 0xd5, 0x43, 0x56, 0xab, 0x07, 0x55,
+	0x2b, 0x25, 0x92, 0x9e, 0x9f, 0x9e, 0x0f, 0x56, 0xa8, 0x0f, 0x62, 0x41, 0xf4, 0x48, 0x99, 0xe0,
+	0x35, 0x3f, 0xb1, 0xb4, 0x24, 0x23, 0xbf, 0x28, 0xb3, 0xa4, 0xd2, 0x37, 0xb5, 0x24, 0x31, 0x25,
+	0xb1, 0x24, 0x11, 0xaa, 0x4b, 0x13, 0xaf, 0xae, 0x82, 0xc4, 0xa2, 0xc4, 0x5c, 0xa8, 0xa3, 0x94,
+	0x8e, 0x30, 0x72, 0xf1, 0xb8, 0x43, 0x9c, 0x19, 0x5c, 0x92, 0x58, 0x92, 0x2a, 0xe4, 0xc4, 0xc5,
+	0x06, 0x51, 0x20, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, 0xa4, 0xa2, 0x87, 0xcf, 0xd9, 0x7a, 0x01,
+	0x60, 0xb5, 0x4e, 0x2c, 0x27, 0xee, 0xc9, 0x33, 0x04, 0x41, 0x75, 0x0a, 0x15, 0x70, 0xf1, 0x41,
+	0xd5, 0xc5, 0xa7, 0xa4, 0xe6, 0xe5, 0xe7, 0x16, 0x4b, 0x30, 0x29, 0x30, 0x6b, 0x70, 0x1b, 0x69,
+	0xe1, 0x37, 0x0b, 0xea, 0x0e, 0x17, 0x90, 0x16, 0x27, 0x59, 0x90, 0x89, 0x9f, 0xee, 0xc9, 0x8b,
+	0x56, 0x26, 0xe6, 0xe6, 0x58, 0x29, 0xa1, 0x9a, 0xa7, 0x14, 0xc4, 0x0b, 0x15, 0x70, 0x81, 0xf0,
+	0x8f, 0x22, 0xbc, 0x01, 0x16, 0x11, 0x52, 0xe3, 0x62, 0x05, 0x2b, 0x05, 0xfb, 0x82, 0xd3, 0x49,
+	0xe0, 0xd3, 0x3d, 0x79, 0x1e, 0x88, 0x49, 0x60, 0x61, 0xa5, 0x20, 0x88, 0xb4, 0x50, 0x1b, 0x23,
+	0x97, 0x10, 0x3c, 0x18, 0xe3, 0x73, 0xa1, 0xe1, 0x28, 0xc1, 0x04, 0xf6, 0xbb, 0x09, 0x7e, 0xf7,
+	0x82, 0x6d, 0x72, 0x44, 0x8f, 0x03, 0x27, 0x45, 0xa8, 0xcb, 0x25, 0x21, 0xf6, 0x61, 0x9a, 0xae,
+	0x14, 0x24, 0x88, 0x11, 0x73, 0x56, 0x2c, 0x2f, 0x16, 0xc8, 0x33, 0x3a, 0x85, 0x9f, 0x78, 0x24,
+	0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78,
+	0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x6d, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e,
+	0x72, 0x7e, 0xae, 0x7e, 0x79, 0x7e, 0x51, 0x6e, 0x46, 0x7e, 0x4e, 0xaa, 0x6e, 0x5a, 0x7e, 0x69,
+	0x5e, 0x4a, 0x62, 0x49, 0x66, 0x7e, 0x1e, 0x58, 0x2c, 0x39, 0x23, 0x31, 0x33, 0x4f, 0xbf, 0x02,
+	0x35, 0xda, 0x4b, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0xd1, 0x6d, 0x0c, 0x08, 0x00, 0x00,
+	0xff, 0xff, 0x73, 0x72, 0x68, 0x5a, 0xb1, 0x02, 0x00, 0x00,
+}
+
+func (this *GenesisDenom) Equal(that interface{}) bool {
+	if that == nil {
+		return this == nil
+	}
+
+	that1, ok := that.(*GenesisDenom)
+	if !ok {
+		that2, ok := that.(GenesisDenom)
+		if ok {
+			that1 = &that2
+		} else {
+			return false
+		}
+	}
+	if that1 == nil {
+		return this == nil
+	} else if this == nil {
+		return false
+	}
+	if this.Denom != that1.Denom {
+		return false
+	}
+	if !this.AuthorityMetadata.Equal(&that1.AuthorityMetadata) {
+		return false
+	}
+	return true
+}
+func (m *GenesisState) 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 *GenesisState) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.FactoryDenoms) > 0 {
+		for iNdEx := len(m.FactoryDenoms) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.FactoryDenoms[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintGenesis(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x12
+		}
+	}
+	{
+		size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenesis(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *GenesisDenom) 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 *GenesisDenom) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *GenesisDenom) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.AuthorityMetadata.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintGenesis(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if len(m.Denom) > 0 {
+		i -= len(m.Denom)
+		copy(dAtA[i:], m.Denom)
+		i = encodeVarintGenesis(dAtA, i, uint64(len(m.Denom)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int {
+	offset -= sovGenesis(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *GenesisState) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Params.Size()
+	n += 1 + l + sovGenesis(uint64(l))
+	if len(m.FactoryDenoms) > 0 {
+		for _, e := range m.FactoryDenoms {
+			l = e.Size()
+			n += 1 + l + sovGenesis(uint64(l))
+		}
+	}
+	return n
+}
+
+func (m *GenesisDenom) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Denom)
+	if l > 0 {
+		n += 1 + l + sovGenesis(uint64(l))
+	}
+	l = m.AuthorityMetadata.Size()
+	n += 1 + l + sovGenesis(uint64(l))
+	return n
+}
+
+func sovGenesis(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozGenesis(x uint64) (n int) {
+	return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *GenesisState) 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 ErrIntOverflowGenesis
+			}
+			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: GenesisState: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field FactoryDenoms", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.FactoryDenoms = append(m.FactoryDenoms, GenesisDenom{})
+			if err := m.FactoryDenoms[len(m.FactoryDenoms)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenesis(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *GenesisDenom) 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 ErrIntOverflowGenesis
+			}
+			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: GenesisDenom: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: GenesisDenom: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				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 ErrInvalidLengthGenesis
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Denom = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AuthorityMetadata", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.AuthorityMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipGenesis(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthGenesis
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipGenesis(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowGenesis
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowGenesis
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthGenesis
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupGenesis
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthGenesis
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthGenesis        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowGenesis          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group")
+)

+ 139 - 0
wormchain/x/tokenfactory/types/genesis_test.go

@@ -0,0 +1,139 @@
+package types_test
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/require"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+)
+
+func TestGenesisState_Validate(t *testing.T) {
+	for _, tc := range []struct {
+		desc     string
+		genState *types.GenesisState
+		valid    bool
+	}{
+		{
+			desc:     "default is valid",
+			genState: types.DefaultGenesis(),
+			valid:    true,
+		},
+		{
+			desc: "valid genesis state",
+			genState: &types.GenesisState{
+				FactoryDenoms: []types.GenesisDenom{
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+						AuthorityMetadata: types.DenomAuthorityMetadata{
+							Admin: "cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8",
+						},
+					},
+				},
+			},
+			valid: true,
+		},
+		{
+			desc: "different admin from creator",
+			genState: &types.GenesisState{
+				FactoryDenoms: []types.GenesisDenom{
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+						AuthorityMetadata: types.DenomAuthorityMetadata{
+							Admin: "cosmos1ft6e5esdtdegnvcr3djd3ftk4kwpcr6jta8eyh",
+						},
+					},
+				},
+			},
+			valid: true,
+		},
+		{
+			desc: "empty admin",
+			genState: &types.GenesisState{
+				FactoryDenoms: []types.GenesisDenom{
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+						AuthorityMetadata: types.DenomAuthorityMetadata{
+							Admin: "",
+						},
+					},
+				},
+			},
+			valid: true,
+		},
+		{
+			desc: "no admin",
+			genState: &types.GenesisState{
+				FactoryDenoms: []types.GenesisDenom{
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+					},
+				},
+			},
+			valid: true,
+		},
+		{
+			desc: "invalid admin",
+			genState: &types.GenesisState{
+				FactoryDenoms: []types.GenesisDenom{
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+						AuthorityMetadata: types.DenomAuthorityMetadata{
+							Admin: "moose",
+						},
+					},
+				},
+			},
+			valid: false,
+		},
+		{
+			desc: "multiple denoms",
+			genState: &types.GenesisState{
+				FactoryDenoms: []types.GenesisDenom{
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+						AuthorityMetadata: types.DenomAuthorityMetadata{
+							Admin: "",
+						},
+					},
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/litecoin",
+						AuthorityMetadata: types.DenomAuthorityMetadata{
+							Admin: "",
+						},
+					},
+				},
+			},
+			valid: true,
+		},
+		{
+			desc: "duplicate denoms",
+			genState: &types.GenesisState{
+				FactoryDenoms: []types.GenesisDenom{
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+						AuthorityMetadata: types.DenomAuthorityMetadata{
+							Admin: "",
+						},
+					},
+					{
+						Denom: "factory/cosmos1t7egva48prqmzl59x5ngv4zx0dtrwewcdqdjr8/bitcoin",
+						AuthorityMetadata: types.DenomAuthorityMetadata{
+							Admin: "",
+						},
+					},
+				},
+			},
+			valid: false,
+		},
+	} {
+		t.Run(tc.desc, func(t *testing.T) {
+			err := tc.genState.Validate()
+			if tc.valid {
+				require.NoError(t, err)
+			} else {
+				require.Error(t, err)
+			}
+		})
+	}
+}

+ 49 - 0
wormchain/x/tokenfactory/types/keys.go

@@ -0,0 +1,49 @@
+package types
+
+import (
+	"strings"
+)
+
+const (
+	// ModuleName defines the module name
+	ModuleName = "tokenfactory"
+
+	// StoreKey defines the primary module store key
+	StoreKey = ModuleName
+
+	// RouterKey is the message route for slashing
+	RouterKey = ModuleName
+
+	// QuerierRoute defines the module's query routing key
+	QuerierRoute = ModuleName
+
+	// MemStoreKey defines the in-memory store key
+	MemStoreKey = "mem_tokenfactory"
+)
+
+// KeySeparator is used to combine parts of the keys in the store
+const KeySeparator = "|"
+
+var (
+	DenomAuthorityMetadataKey = "authoritymetadata"
+	DenomsPrefixKey           = "denoms"
+	CreatorPrefixKey          = "creator"
+	AdminPrefixKey            = "admin"
+)
+
+// GetDenomPrefixStore returns the store prefix where all the data associated with a specific denom
+// is stored
+func GetDenomPrefixStore(denom string) []byte {
+	return []byte(strings.Join([]string{DenomsPrefixKey, denom, ""}, KeySeparator))
+}
+
+// GetCreatorsPrefix returns the store prefix where the list of the denoms created by a specific
+// creator are stored
+func GetCreatorPrefix(creator string) []byte {
+	return []byte(strings.Join([]string{CreatorPrefixKey, creator, ""}, KeySeparator))
+}
+
+// GetCreatorsPrefix returns the store prefix where a list of all creator addresses are stored
+func GetCreatorsPrefix() []byte {
+	return []byte(strings.Join([]string{CreatorPrefixKey, ""}, KeySeparator))
+}

+ 277 - 0
wormchain/x/tokenfactory/types/msgs.go

@@ -0,0 +1,277 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+)
+
+// constants
+const (
+	TypeMsgCreateDenom      = "create_denom"
+	TypeMsgMint             = "tf_mint"
+	TypeMsgBurn             = "tf_burn"
+	TypeMsgForceTransfer    = "force_transfer"
+	TypeMsgChangeAdmin      = "change_admin"
+	TypeMsgSetDenomMetadata = "set_denom_metadata"
+)
+
+var _ sdk.Msg = &MsgCreateDenom{}
+
+// NewMsgCreateDenom creates a msg to create a new denom
+func NewMsgCreateDenom(sender, subdenom string) *MsgCreateDenom {
+	return &MsgCreateDenom{
+		Sender:   sender,
+		Subdenom: subdenom,
+	}
+}
+
+func (m MsgCreateDenom) Route() string { return RouterKey }
+func (m MsgCreateDenom) Type() string  { return TypeMsgCreateDenom }
+func (m MsgCreateDenom) ValidateBasic() error {
+	_, err := sdk.AccAddressFromBech32(m.Sender)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err)
+	}
+
+	_, err = GetTokenDenom(m.Sender, m.Subdenom)
+	if err != nil {
+		return sdkerrors.Wrap(ErrInvalidDenom, err.Error())
+	}
+
+	return nil
+}
+
+func (m MsgCreateDenom) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
+}
+
+func (m MsgCreateDenom) GetSigners() []sdk.AccAddress {
+	sender, _ := sdk.AccAddressFromBech32(m.Sender)
+	return []sdk.AccAddress{sender}
+}
+
+var _ sdk.Msg = &MsgMint{}
+
+// NewMsgMint creates a message to mint tokens
+func NewMsgMint(sender string, amount sdk.Coin) *MsgMint {
+	return &MsgMint{
+		Sender: sender,
+		Amount: amount,
+	}
+}
+
+func NewMsgMintTo(sender string, amount sdk.Coin, mintToAddress string) *MsgMint {
+	return &MsgMint{
+		Sender:        sender,
+		Amount:        amount,
+		MintToAddress: mintToAddress,
+	}
+}
+
+func (m MsgMint) Route() string { return RouterKey }
+func (m MsgMint) Type() string  { return TypeMsgMint }
+func (m MsgMint) ValidateBasic() error {
+	_, err := sdk.AccAddressFromBech32(m.Sender)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err)
+	}
+
+	if m.MintToAddress != "" {
+		_, err = sdk.AccAddressFromBech32(m.MintToAddress)
+		if err != nil {
+			return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid mint to address (%s)", err)
+		}
+	}
+
+	if !m.Amount.IsValid() || m.Amount.Amount.Equal(sdk.ZeroInt()) {
+		return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String())
+	}
+
+	return nil
+}
+
+func (m MsgMint) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
+}
+
+func (m MsgMint) GetSigners() []sdk.AccAddress {
+	sender, _ := sdk.AccAddressFromBech32(m.Sender)
+	return []sdk.AccAddress{sender}
+}
+
+var _ sdk.Msg = &MsgBurn{}
+
+// NewMsgBurn creates a message to burn tokens
+func NewMsgBurn(sender string, amount sdk.Coin) *MsgBurn {
+	return &MsgBurn{
+		Sender: sender,
+		Amount: amount,
+	}
+}
+
+// NewMsgBurn creates a message to burn tokens
+func NewMsgBurnFrom(sender string, amount sdk.Coin, burnFromAddress string) *MsgBurn {
+	return &MsgBurn{
+		Sender:          sender,
+		Amount:          amount,
+		BurnFromAddress: burnFromAddress,
+	}
+}
+
+func (m MsgBurn) Route() string { return RouterKey }
+func (m MsgBurn) Type() string  { return TypeMsgBurn }
+func (m MsgBurn) ValidateBasic() error {
+	_, err := sdk.AccAddressFromBech32(m.Sender)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err)
+	}
+
+	if !m.Amount.IsValid() || m.Amount.Amount.Equal(sdk.ZeroInt()) {
+		return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String())
+	}
+
+	if m.BurnFromAddress != "" {
+		_, err = sdk.AccAddressFromBech32(m.BurnFromAddress)
+		if err != nil {
+			return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid burn from address (%s)", err)
+		}
+	}
+
+	return nil
+}
+
+func (m MsgBurn) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
+}
+
+func (m MsgBurn) GetSigners() []sdk.AccAddress {
+	sender, _ := sdk.AccAddressFromBech32(m.Sender)
+	return []sdk.AccAddress{sender}
+}
+
+var _ sdk.Msg = &MsgForceTransfer{}
+
+// NewMsgForceTransfer creates a transfer funds from one account to another
+func NewMsgForceTransfer(sender string, amount sdk.Coin, fromAddr, toAddr string) *MsgForceTransfer {
+	return &MsgForceTransfer{
+		Sender:              sender,
+		Amount:              amount,
+		TransferFromAddress: fromAddr,
+		TransferToAddress:   toAddr,
+	}
+}
+
+func (m MsgForceTransfer) Route() string { return RouterKey }
+func (m MsgForceTransfer) Type() string  { return TypeMsgForceTransfer }
+func (m MsgForceTransfer) ValidateBasic() error {
+	_, err := sdk.AccAddressFromBech32(m.Sender)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err)
+	}
+
+	_, err = sdk.AccAddressFromBech32(m.TransferFromAddress)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid from address (%s)", err)
+	}
+	_, err = sdk.AccAddressFromBech32(m.TransferToAddress)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid to address (%s)", err)
+	}
+
+	if !m.Amount.IsValid() {
+		return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String())
+	}
+
+	return nil
+}
+
+func (m MsgForceTransfer) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
+}
+
+func (m MsgForceTransfer) GetSigners() []sdk.AccAddress {
+	sender, _ := sdk.AccAddressFromBech32(m.Sender)
+	return []sdk.AccAddress{sender}
+}
+
+var _ sdk.Msg = &MsgChangeAdmin{}
+
+// NewMsgChangeAdmin creates a message to burn tokens
+func NewMsgChangeAdmin(sender, denom, newAdmin string) *MsgChangeAdmin {
+	return &MsgChangeAdmin{
+		Sender:   sender,
+		Denom:    denom,
+		NewAdmin: newAdmin,
+	}
+}
+
+func (m MsgChangeAdmin) Route() string { return RouterKey }
+func (m MsgChangeAdmin) Type() string  { return TypeMsgChangeAdmin }
+func (m MsgChangeAdmin) ValidateBasic() error {
+	_, err := sdk.AccAddressFromBech32(m.Sender)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err)
+	}
+
+	_, err = sdk.AccAddressFromBech32(m.NewAdmin)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid address (%s)", err)
+	}
+
+	_, _, err = DeconstructDenom(m.Denom)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m MsgChangeAdmin) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
+}
+
+func (m MsgChangeAdmin) GetSigners() []sdk.AccAddress {
+	sender, _ := sdk.AccAddressFromBech32(m.Sender)
+	return []sdk.AccAddress{sender}
+}
+
+var _ sdk.Msg = &MsgSetDenomMetadata{}
+
+// NewMsgChangeAdmin creates a message to burn tokens
+func NewMsgSetDenomMetadata(sender string, metadata banktypes.Metadata) *MsgSetDenomMetadata {
+	return &MsgSetDenomMetadata{
+		Sender:   sender,
+		Metadata: metadata,
+	}
+}
+
+func (m MsgSetDenomMetadata) Route() string { return RouterKey }
+func (m MsgSetDenomMetadata) Type() string  { return TypeMsgSetDenomMetadata }
+func (m MsgSetDenomMetadata) ValidateBasic() error {
+	_, err := sdk.AccAddressFromBech32(m.Sender)
+	if err != nil {
+		return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "Invalid sender address (%s)", err)
+	}
+
+	err = m.Metadata.Validate()
+	if err != nil {
+		return err
+	}
+
+	_, _, err = DeconstructDenom(m.Metadata.Base)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (m MsgSetDenomMetadata) GetSignBytes() []byte {
+	return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
+}
+
+func (m MsgSetDenomMetadata) GetSigners() []sdk.AccAddress {
+	sender, _ := sdk.AccAddressFromBech32(m.Sender)
+	return []sdk.AccAddress{sender}
+}

+ 451 - 0
wormchain/x/tokenfactory/types/msgs_test.go

@@ -0,0 +1,451 @@
+package types_test
+
+import (
+	fmt "fmt"
+	"testing"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/stretchr/testify/require"
+
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/testhelpers"
+	"github.com/wormhole-foundation/wormchain/x/tokenfactory/types"
+
+	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
+	"github.com/tendermint/tendermint/crypto/ed25519"
+)
+
+// // Test authz serialize and de-serializes for tokenfactory msg.
+func TestAuthzMsg(t *testing.T) {
+	t.Skip("TODO: figure out how to register authz interfaces for tests")
+	pk1 := ed25519.GenPrivKey().PubKey()
+	addr1 := sdk.AccAddress(pk1.Address()).String()
+	coin := sdk.NewCoin("denom", sdk.NewInt(1))
+
+	testCases := []struct {
+		name string
+		msg  sdk.Msg
+	}{
+		{
+			name: "MsgCreateDenom",
+			msg: &types.MsgCreateDenom{
+				Sender:   addr1,
+				Subdenom: "valoper1xyz",
+			},
+		},
+		{
+			name: "MsgBurn",
+			msg: &types.MsgBurn{
+				Sender: addr1,
+				Amount: coin,
+			},
+		},
+		{
+			name: "MsgMint",
+			msg: &types.MsgMint{
+				Sender: addr1,
+				Amount: coin,
+			},
+		},
+		{
+			name: "MsgChangeAdmin",
+			msg: &types.MsgChangeAdmin{
+				Sender:   addr1,
+				Denom:    "denom",
+				NewAdmin: "osmo1q8tq5qhrhw6t970egemuuwywhlhpnmdmts6xnu",
+			},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			testhelpers.TestMessageAuthzSerialization(t, tc.msg)
+		})
+	}
+}
+
+// TestMsgCreateDenom tests if valid/invalid create denom messages are properly validated/invalidated
+func TestMsgCreateDenom(t *testing.T) {
+	// generate a private/public key pair and get the respective address
+	pk1 := ed25519.GenPrivKey().PubKey()
+	addr1 := sdk.AccAddress(pk1.Address())
+
+	// make a proper createDenom message
+	createMsg := func(after func(msg types.MsgCreateDenom) types.MsgCreateDenom) types.MsgCreateDenom {
+		properMsg := *types.NewMsgCreateDenom(
+			addr1.String(),
+			"bitcoin",
+		)
+
+		return after(properMsg)
+	}
+
+	// validate createDenom message was created as intended
+	msg := createMsg(func(msg types.MsgCreateDenom) types.MsgCreateDenom {
+		return msg
+	})
+	require.Equal(t, msg.Route(), types.RouterKey)
+	require.Equal(t, msg.Type(), "create_denom")
+	signers := msg.GetSigners()
+	require.Equal(t, len(signers), 1)
+	require.Equal(t, signers[0].String(), addr1.String())
+
+	tests := []struct {
+		name       string
+		msg        types.MsgCreateDenom
+		expectPass bool
+	}{
+		{
+			name: "proper msg",
+			msg: createMsg(func(msg types.MsgCreateDenom) types.MsgCreateDenom {
+				return msg
+			}),
+			expectPass: true,
+		},
+		{
+			name: "empty sender",
+			msg: createMsg(func(msg types.MsgCreateDenom) types.MsgCreateDenom {
+				msg.Sender = ""
+				return msg
+			}),
+			expectPass: false,
+		},
+		{
+			name: "invalid subdenom",
+			msg: createMsg(func(msg types.MsgCreateDenom) types.MsgCreateDenom {
+				msg.Subdenom = "thissubdenomismuchtoolongasdkfjaasdfdsafsdlkfnmlksadmflksmdlfmlsakmfdsafasdfasdf"
+				return msg
+			}),
+			expectPass: false,
+		},
+	}
+
+	for _, test := range tests {
+		if test.expectPass {
+			require.NoError(t, test.msg.ValidateBasic(), "test: %v", test.name)
+		} else {
+			require.Error(t, test.msg.ValidateBasic(), "test: %v", test.name)
+		}
+	}
+}
+
+// TestMsgMint tests if valid/invalid create denom messages are properly validated/invalidated
+func TestMsgMint(t *testing.T) {
+	// generate a private/public key pair and get the respective address
+	pk1 := ed25519.GenPrivKey().PubKey()
+	addr1 := sdk.AccAddress(pk1.Address())
+
+	// make a proper mint message
+	createMsg := func(after func(msg types.MsgMint) types.MsgMint) types.MsgMint {
+		properMsg := *types.NewMsgMint(
+			addr1.String(),
+			sdk.NewCoin("bitcoin", sdk.NewInt(500000000)),
+		)
+
+		return after(properMsg)
+	}
+
+	// validate mint message was created as intended
+	msg := createMsg(func(msg types.MsgMint) types.MsgMint {
+		return msg
+	})
+	require.Equal(t, msg.Route(), types.RouterKey)
+	require.Equal(t, msg.Type(), "tf_mint")
+	signers := msg.GetSigners()
+	require.Equal(t, len(signers), 1)
+	require.Equal(t, signers[0].String(), addr1.String())
+
+	tests := []struct {
+		name       string
+		msg        types.MsgMint
+		expectPass bool
+	}{
+		{
+			name: "proper msg",
+			msg: createMsg(func(msg types.MsgMint) types.MsgMint {
+				return msg
+			}),
+			expectPass: true,
+		},
+		{
+			name: "empty sender",
+			msg: createMsg(func(msg types.MsgMint) types.MsgMint {
+				msg.Sender = ""
+				return msg
+			}),
+			expectPass: false,
+		},
+		{
+			name: "zero amount",
+			msg: createMsg(func(msg types.MsgMint) types.MsgMint {
+				msg.Amount = sdk.NewCoin("bitcoin", sdk.ZeroInt())
+				return msg
+			}),
+			expectPass: false,
+		},
+		{
+			name: "negative amount",
+			msg: createMsg(func(msg types.MsgMint) types.MsgMint {
+				msg.Amount.Amount = sdk.NewInt(-10000000)
+				return msg
+			}),
+			expectPass: false,
+		},
+	}
+
+	for _, test := range tests {
+		if test.expectPass {
+			require.NoError(t, test.msg.ValidateBasic(), "test: %v", test.name)
+		} else {
+			require.Error(t, test.msg.ValidateBasic(), "test: %v", test.name)
+		}
+	}
+}
+
+// TestMsgBurn tests if valid/invalid create denom messages are properly validated/invalidated
+func TestMsgBurn(t *testing.T) {
+	// generate a private/public key pair and get the respective address
+	pk1 := ed25519.GenPrivKey().PubKey()
+	addr1 := sdk.AccAddress(pk1.Address())
+
+	// make a proper burn message
+	baseMsg := types.NewMsgBurn(
+		addr1.String(),
+		sdk.NewCoin("bitcoin", sdk.NewInt(500000000)),
+	)
+
+	// validate burn message was created as intended
+	require.Equal(t, baseMsg.Route(), types.RouterKey)
+	require.Equal(t, baseMsg.Type(), "tf_burn")
+	signers := baseMsg.GetSigners()
+	require.Equal(t, len(signers), 1)
+	require.Equal(t, signers[0].String(), addr1.String())
+
+	tests := []struct {
+		name       string
+		msg        func() *types.MsgBurn
+		expectPass bool
+	}{
+		{
+			name: "proper msg",
+			msg: func() *types.MsgBurn {
+				msg := baseMsg
+				return msg
+			},
+			expectPass: true,
+		},
+		{
+			name: "empty sender",
+			msg: func() *types.MsgBurn {
+				msg := baseMsg
+				msg.Sender = ""
+				return msg
+			},
+			expectPass: false,
+		},
+		{
+			name: "zero amount",
+			msg: func() *types.MsgBurn {
+				msg := baseMsg
+				msg.Amount.Amount = sdk.ZeroInt()
+				return msg
+			},
+			expectPass: false,
+		},
+		{
+			name: "negative amount",
+			msg: func() *types.MsgBurn {
+				msg := baseMsg
+				msg.Amount.Amount = sdk.NewInt(-10000000)
+				return msg
+			},
+			expectPass: false,
+		},
+	}
+
+	for _, test := range tests {
+		if test.expectPass {
+			require.NoError(t, test.msg().ValidateBasic(), "test: %v", test.name)
+		} else {
+			require.Error(t, test.msg().ValidateBasic(), "test: %v", test.name)
+		}
+	}
+}
+
+// TestMsgChangeAdmin tests if valid/invalid create denom messages are properly validated/invalidated
+func TestMsgChangeAdmin(t *testing.T) {
+	// generate a private/public key pair and get the respective address
+	pk1 := ed25519.GenPrivKey().PubKey()
+	addr1 := sdk.AccAddress(pk1.Address())
+	pk2 := ed25519.GenPrivKey().PubKey()
+	addr2 := sdk.AccAddress(pk2.Address())
+	tokenFactoryDenom := fmt.Sprintf("factory/%s/bitcoin", addr1.String())
+
+	// make a proper changeAdmin message
+	baseMsg := types.NewMsgChangeAdmin(
+		addr1.String(),
+		tokenFactoryDenom,
+		addr2.String(),
+	)
+
+	// validate changeAdmin message was created as intended
+	require.Equal(t, baseMsg.Route(), types.RouterKey)
+	require.Equal(t, baseMsg.Type(), "change_admin")
+	signers := baseMsg.GetSigners()
+	require.Equal(t, len(signers), 1)
+	require.Equal(t, signers[0].String(), addr1.String())
+
+	tests := []struct {
+		name       string
+		msg        func() *types.MsgChangeAdmin
+		expectPass bool
+	}{
+		{
+			name: "proper msg",
+			msg: func() *types.MsgChangeAdmin {
+				msg := baseMsg
+				return msg
+			},
+			expectPass: true,
+		},
+		{
+			name: "empty sender",
+			msg: func() *types.MsgChangeAdmin {
+				msg := baseMsg
+				msg.Sender = ""
+				return msg
+			},
+			expectPass: false,
+		},
+		{
+			name: "empty newAdmin",
+			msg: func() *types.MsgChangeAdmin {
+				msg := baseMsg
+				msg.NewAdmin = ""
+				return msg
+			},
+			expectPass: false,
+		},
+		{
+			name: "invalid denom",
+			msg: func() *types.MsgChangeAdmin {
+				msg := baseMsg
+				msg.Denom = "bitcoin"
+				return msg
+			},
+			expectPass: false,
+		},
+	}
+
+	for _, test := range tests {
+		if test.expectPass {
+			require.NoError(t, test.msg().ValidateBasic(), "test: %v", test.name)
+		} else {
+			require.Error(t, test.msg().ValidateBasic(), "test: %v", test.name)
+		}
+	}
+}
+
+// TestMsgSetDenomMetadata tests if valid/invalid create denom messages are properly validated/invalidated
+func TestMsgSetDenomMetadata(t *testing.T) {
+	// generate a private/public key pair and get the respective address
+	pk1 := ed25519.GenPrivKey().PubKey()
+	addr1 := sdk.AccAddress(pk1.Address())
+	tokenFactoryDenom := fmt.Sprintf("factory/%s/bitcoin", addr1.String())
+	denomMetadata := banktypes.Metadata{
+		Description: "nakamoto",
+		DenomUnits: []*banktypes.DenomUnit{
+			{
+				Denom:    tokenFactoryDenom,
+				Exponent: 0,
+			},
+			{
+				Denom:    "sats",
+				Exponent: 6,
+			},
+		},
+		Display: "sats",
+		Base:    tokenFactoryDenom,
+		Name:    "bitcoin",
+		Symbol:  "BTC",
+	}
+	invalidDenomMetadata := banktypes.Metadata{
+		Description: "nakamoto",
+		DenomUnits: []*banktypes.DenomUnit{
+			{
+				Denom:    "bitcoin",
+				Exponent: 0,
+			},
+			{
+				Denom:    "sats",
+				Exponent: 6,
+			},
+		},
+		Display: "sats",
+		Base:    "bitcoin",
+		Name:    "bitcoin",
+		Symbol:  "BTC",
+	}
+
+	// make a proper setDenomMetadata message
+	baseMsg := types.NewMsgSetDenomMetadata(
+		addr1.String(),
+		denomMetadata,
+	)
+
+	// validate setDenomMetadata message was created as intended
+	require.Equal(t, baseMsg.Route(), types.RouterKey)
+	require.Equal(t, baseMsg.Type(), "set_denom_metadata")
+	signers := baseMsg.GetSigners()
+	require.Equal(t, len(signers), 1)
+	require.Equal(t, signers[0].String(), addr1.String())
+
+	tests := []struct {
+		name       string
+		msg        func() *types.MsgSetDenomMetadata
+		expectPass bool
+	}{
+		{
+			name: "proper msg",
+			msg: func() *types.MsgSetDenomMetadata {
+				msg := baseMsg
+				return msg
+			},
+			expectPass: true,
+		},
+		{
+			name: "empty sender",
+			msg: func() *types.MsgSetDenomMetadata {
+				msg := baseMsg
+				msg.Sender = ""
+				return msg
+			},
+			expectPass: false,
+		},
+		{
+			name: "invalid metadata",
+			msg: func() *types.MsgSetDenomMetadata {
+				msg := baseMsg
+				msg.Metadata.Name = ""
+				return msg
+			},
+
+			expectPass: false,
+		},
+		{
+			name: "invalid base",
+			msg: func() *types.MsgSetDenomMetadata {
+				msg := baseMsg
+				msg.Metadata = invalidDenomMetadata
+				return msg
+			},
+			expectPass: false,
+		},
+	}
+
+	for _, test := range tests {
+		if test.expectPass {
+			require.NoError(t, test.msg().ValidateBasic(), "test: %v", test.name)
+		} else {
+			require.Error(t, test.msg().ValidateBasic(), "test: %v", test.name)
+		}
+	}
+}

+ 71 - 0
wormchain/x/tokenfactory/types/params.go

@@ -0,0 +1,71 @@
+package types
+
+import (
+	"fmt"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	paramtypes "github.com/cosmos/cosmos-sdk/x/params/types"
+)
+
+// Parameter store keys.
+var (
+	KeyDenomCreationFee        = []byte("DenomCreationFee")
+	KeyDenomCreationGasConsume = []byte("DenomCreationGasConsume")
+)
+
+// ParamTable for tokenfactory module.
+func ParamKeyTable() paramtypes.KeyTable {
+	return paramtypes.NewKeyTable().RegisterParamSet(&Params{})
+}
+
+func NewParams(denomCreationFee sdk.Coins) Params {
+	return Params{
+		DenomCreationFee: denomCreationFee,
+	}
+}
+
+// default tokenfactory module parameters.
+func DefaultParams() Params {
+	return Params{
+		DenomCreationFee: nil,
+		//DenomCreationFee:        sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 10_000_000)),
+		DenomCreationGasConsume: 0,
+	}
+}
+
+// validate params.
+func (p Params) Validate() error {
+	err := validateDenomCreationFee(p.DenomCreationFee)
+
+	return err
+}
+
+// Implements params.ParamSet.
+func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
+	return paramtypes.ParamSetPairs{
+		paramtypes.NewParamSetPair(KeyDenomCreationFee, &p.DenomCreationFee, validateDenomCreationFee),
+		paramtypes.NewParamSetPair(KeyDenomCreationGasConsume, &p.DenomCreationGasConsume, validateDenomCreationFeeGasConsume),
+	}
+}
+
+func validateDenomCreationFee(i interface{}) error {
+	v, ok := i.(sdk.Coins)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", i)
+	}
+
+	if v.Validate() != nil {
+		return fmt.Errorf("invalid denom creation fee: %+v", i)
+	}
+
+	return nil
+}
+
+func validateDenomCreationFeeGasConsume(i interface{}) error {
+	_, ok := i.(uint64)
+	if !ok {
+		return fmt.Errorf("invalid parameter type: %T", i)
+	}
+
+	return nil
+}

+ 383 - 0
wormchain/x/tokenfactory/types/params.pb.go

@@ -0,0 +1,383 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: osmosis/tokenfactory/v1beta1/params.proto
+
+package types
+
+import (
+	fmt "fmt"
+	github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
+	types "github.com/cosmos/cosmos-sdk/types"
+	_ "github.com/gogo/protobuf/gogoproto"
+	proto "github.com/gogo/protobuf/proto"
+	_ "github.com/regen-network/cosmos-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
+
+// Params defines the parameters for the tokenfactory module.
+type Params struct {
+	DenomCreationFee github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,1,rep,name=denom_creation_fee,json=denomCreationFee,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"denom_creation_fee" yaml:"denom_creation_fee"`
+	// if denom_creation_fee is an empty array, then this field is used to add more gas consumption
+	// to the base cost.
+	// https://github.com/CosmWasm/token-factory/issues/11
+	DenomCreationGasConsume uint64 `protobuf:"varint,2,opt,name=denom_creation_gas_consume,json=denomCreationGasConsume,proto3" json:"denom_creation_gas_consume,omitempty" yaml:"denom_creation_gas_consume"`
+}
+
+func (m *Params) Reset()         { *m = Params{} }
+func (m *Params) String() string { return proto.CompactTextString(m) }
+func (*Params) ProtoMessage()    {}
+func (*Params) Descriptor() ([]byte, []int) {
+	return fileDescriptor_cc8299d306f3ff47, []int{0}
+}
+func (m *Params) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_Params.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 *Params) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Params.Merge(m, src)
+}
+func (m *Params) XXX_Size() int {
+	return m.Size()
+}
+func (m *Params) XXX_DiscardUnknown() {
+	xxx_messageInfo_Params.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Params proto.InternalMessageInfo
+
+func (m *Params) GetDenomCreationFee() github_com_cosmos_cosmos_sdk_types.Coins {
+	if m != nil {
+		return m.DenomCreationFee
+	}
+	return nil
+}
+
+func (m *Params) GetDenomCreationGasConsume() uint64 {
+	if m != nil {
+		return m.DenomCreationGasConsume
+	}
+	return 0
+}
+
+func init() {
+	proto.RegisterType((*Params)(nil), "osmosis.tokenfactory.v1beta1.Params")
+}
+
+func init() {
+	proto.RegisterFile("osmosis/tokenfactory/v1beta1/params.proto", fileDescriptor_cc8299d306f3ff47)
+}
+
+var fileDescriptor_cc8299d306f3ff47 = []byte{
+	// 362 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xbf, 0x4e, 0xf3, 0x30,
+	0x14, 0xc5, 0x93, 0x7e, 0x9f, 0x3a, 0x84, 0x05, 0x45, 0x48, 0xb4, 0x15, 0x4a, 0x4a, 0xa6, 0x76,
+	0x68, 0xa2, 0x02, 0x13, 0x12, 0x4b, 0x2b, 0xc1, 0x54, 0x09, 0x75, 0x41, 0x62, 0x89, 0x6e, 0x12,
+	0xe7, 0x8f, 0xda, 0xf8, 0x56, 0xb1, 0x03, 0xe4, 0x11, 0xd8, 0x98, 0x78, 0x08, 0x9e, 0xa4, 0x63,
+	0x47, 0xa6, 0x80, 0xda, 0x37, 0xe8, 0x13, 0xa0, 0x3a, 0x06, 0xb5, 0x80, 0x98, 0xec, 0xeb, 0x7b,
+	0xce, 0xcf, 0xe7, 0xda, 0x5a, 0x17, 0x59, 0x8a, 0x2c, 0x61, 0x0e, 0xc7, 0x09, 0xa1, 0x21, 0xf8,
+	0x1c, 0xb3, 0xc2, 0xb9, 0xeb, 0x7b, 0x84, 0x43, 0xdf, 0x99, 0x41, 0x06, 0x29, 0xb3, 0x67, 0x19,
+	0x72, 0xd4, 0x8f, 0xa4, 0xd4, 0xde, 0x96, 0xda, 0x52, 0xda, 0x3a, 0x88, 0x30, 0x42, 0x21, 0x74,
+	0x36, 0xbb, 0xca, 0xd3, 0x3a, 0xfb, 0x13, 0x0f, 0x39, 0x8f, 0x31, 0x4b, 0x78, 0x31, 0x22, 0x1c,
+	0x02, 0xe0, 0x20, 0x5d, 0x4d, 0x5f, 0xd8, 0xdc, 0x0a, 0x57, 0x15, 0xb2, 0x65, 0x54, 0x95, 0xe3,
+	0x01, 0x23, 0x5f, 0x1c, 0x1f, 0x13, 0x5a, 0xf5, 0xad, 0xc7, 0x9a, 0x56, 0xbf, 0x16, 0xa9, 0xf5,
+	0x67, 0x55, 0xd3, 0x03, 0x42, 0x31, 0x75, 0xfd, 0x8c, 0x00, 0x4f, 0x90, 0xba, 0x21, 0x21, 0x0d,
+	0xb5, 0xfd, 0xaf, 0xb3, 0x77, 0xd2, 0xb4, 0x25, 0x76, 0x03, 0xfa, 0x1c, 0xc2, 0x1e, 0x62, 0x42,
+	0x07, 0xa3, 0x79, 0x69, 0x2a, 0xeb, 0xd2, 0x6c, 0x16, 0x90, 0x4e, 0xcf, 0xad, 0x9f, 0x08, 0xeb,
+	0xe5, 0xcd, 0xec, 0x44, 0x09, 0x8f, 0x73, 0xcf, 0xf6, 0x31, 0x95, 0x01, 0xe5, 0xd2, 0x63, 0xc1,
+	0xc4, 0xe1, 0xc5, 0x8c, 0x30, 0x41, 0x63, 0xe3, 0x7d, 0x01, 0x18, 0x4a, 0xff, 0x25, 0x21, 0x7a,
+	0xa8, 0xb5, 0xbe, 0x41, 0x23, 0x60, 0xae, 0x8f, 0x94, 0xe5, 0x29, 0x69, 0xd4, 0xda, 0x6a, 0xe7,
+	0xff, 0xa0, 0x3b, 0x2f, 0x4d, 0x75, 0x5d, 0x9a, 0xc7, 0xbf, 0x86, 0xd8, 0xd2, 0x5b, 0xe3, 0xc3,
+	0x9d, 0x0b, 0xae, 0x80, 0x0d, 0xab, 0xce, 0xe0, 0x66, 0xbe, 0x34, 0xd4, 0xc5, 0xd2, 0x50, 0xdf,
+	0x97, 0x86, 0xfa, 0xb4, 0x32, 0x94, 0xc5, 0xca, 0x50, 0x5e, 0x57, 0x86, 0x72, 0x7b, 0xb1, 0x95,
+	0xfe, 0x1e, 0xb3, 0x34, 0xc6, 0x29, 0xe9, 0x85, 0x98, 0xd3, 0x40, 0x30, 0xc4, 0x99, 0x1f, 0x43,
+	0x42, 0x9d, 0x87, 0xdd, 0x9f, 0x13, 0x83, 0x79, 0x75, 0xf1, 0xd6, 0xa7, 0x1f, 0x01, 0x00, 0x00,
+	0xff, 0xff, 0xdf, 0x2c, 0x19, 0x8a, 0x3d, 0x02, 0x00, 0x00,
+}
+
+func (m *Params) 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 *Params) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.DenomCreationGasConsume != 0 {
+		i = encodeVarintParams(dAtA, i, uint64(m.DenomCreationGasConsume))
+		i--
+		dAtA[i] = 0x10
+	}
+	if len(m.DenomCreationFee) > 0 {
+		for iNdEx := len(m.DenomCreationFee) - 1; iNdEx >= 0; iNdEx-- {
+			{
+				size, err := m.DenomCreationFee[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintParams(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintParams(dAtA []byte, offset int, v uint64) int {
+	offset -= sovParams(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *Params) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.DenomCreationFee) > 0 {
+		for _, e := range m.DenomCreationFee {
+			l = e.Size()
+			n += 1 + l + sovParams(uint64(l))
+		}
+	}
+	if m.DenomCreationGasConsume != 0 {
+		n += 1 + sovParams(uint64(m.DenomCreationGasConsume))
+	}
+	return n
+}
+
+func sovParams(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozParams(x uint64) (n int) {
+	return sovParams(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *Params) 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 ErrIntOverflowParams
+			}
+			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: Params: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DenomCreationFee", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthParams
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthParams
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.DenomCreationFee = append(m.DenomCreationFee, types.Coin{})
+			if err := m.DenomCreationFee[len(m.DenomCreationFee)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 2:
+			if wireType != 0 {
+				return fmt.Errorf("proto: wrong wireType = %d for field DenomCreationGasConsume", wireType)
+			}
+			m.DenomCreationGasConsume = 0
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				m.DenomCreationGasConsume |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+		default:
+			iNdEx = preIndex
+			skippy, err := skipParams(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthParams
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipParams(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowParams
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowParams
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthParams
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupParams
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthParams
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthParams        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowParams          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupParams = fmt.Errorf("proto: unexpected end of group")
+)

+ 1333 - 0
wormchain/x/tokenfactory/types/query.pb.go

@@ -0,0 +1,1333 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: osmosis/tokenfactory/v1beta1/query.proto
+
+package types
+
+import (
+	context "context"
+	fmt "fmt"
+	_ "github.com/cosmos/cosmos-sdk/types/query"
+	_ "github.com/gogo/protobuf/gogoproto"
+	grpc1 "github.com/gogo/protobuf/grpc"
+	proto "github.com/gogo/protobuf/proto"
+	_ "google.golang.org/genproto/googleapis/api/annotations"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// QueryParamsRequest is the request type for the Query/Params RPC method.
+type QueryParamsRequest struct {
+}
+
+func (m *QueryParamsRequest) Reset()         { *m = QueryParamsRequest{} }
+func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryParamsRequest) ProtoMessage()    {}
+func (*QueryParamsRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6f22013ad0f72e3f, []int{0}
+}
+func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryParamsRequest.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 *QueryParamsRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryParamsRequest.Merge(m, src)
+}
+func (m *QueryParamsRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryParamsRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo
+
+// QueryParamsResponse is the response type for the Query/Params RPC method.
+type QueryParamsResponse struct {
+	// params defines the parameters of the module.
+	Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
+}
+
+func (m *QueryParamsResponse) Reset()         { *m = QueryParamsResponse{} }
+func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryParamsResponse) ProtoMessage()    {}
+func (*QueryParamsResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6f22013ad0f72e3f, []int{1}
+}
+func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryParamsResponse.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 *QueryParamsResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryParamsResponse.Merge(m, src)
+}
+func (m *QueryParamsResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryParamsResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo
+
+func (m *QueryParamsResponse) GetParams() Params {
+	if m != nil {
+		return m.Params
+	}
+	return Params{}
+}
+
+// QueryDenomAuthorityMetadataRequest defines the request structure for the
+// DenomAuthorityMetadata gRPC query.
+type QueryDenomAuthorityMetadataRequest struct {
+	Denom string `protobuf:"bytes,1,opt,name=denom,proto3" json:"denom,omitempty" yaml:"denom"`
+}
+
+func (m *QueryDenomAuthorityMetadataRequest) Reset()         { *m = QueryDenomAuthorityMetadataRequest{} }
+func (m *QueryDenomAuthorityMetadataRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryDenomAuthorityMetadataRequest) ProtoMessage()    {}
+func (*QueryDenomAuthorityMetadataRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6f22013ad0f72e3f, []int{2}
+}
+func (m *QueryDenomAuthorityMetadataRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryDenomAuthorityMetadataRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryDenomAuthorityMetadataRequest.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 *QueryDenomAuthorityMetadataRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryDenomAuthorityMetadataRequest.Merge(m, src)
+}
+func (m *QueryDenomAuthorityMetadataRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryDenomAuthorityMetadataRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryDenomAuthorityMetadataRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryDenomAuthorityMetadataRequest proto.InternalMessageInfo
+
+func (m *QueryDenomAuthorityMetadataRequest) GetDenom() string {
+	if m != nil {
+		return m.Denom
+	}
+	return ""
+}
+
+// QueryDenomAuthorityMetadataResponse defines the response structure for the
+// DenomAuthorityMetadata gRPC query.
+type QueryDenomAuthorityMetadataResponse struct {
+	AuthorityMetadata DenomAuthorityMetadata `protobuf:"bytes,1,opt,name=authority_metadata,json=authorityMetadata,proto3" json:"authority_metadata" yaml:"authority_metadata"`
+}
+
+func (m *QueryDenomAuthorityMetadataResponse) Reset()         { *m = QueryDenomAuthorityMetadataResponse{} }
+func (m *QueryDenomAuthorityMetadataResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryDenomAuthorityMetadataResponse) ProtoMessage()    {}
+func (*QueryDenomAuthorityMetadataResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6f22013ad0f72e3f, []int{3}
+}
+func (m *QueryDenomAuthorityMetadataResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryDenomAuthorityMetadataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryDenomAuthorityMetadataResponse.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 *QueryDenomAuthorityMetadataResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryDenomAuthorityMetadataResponse.Merge(m, src)
+}
+func (m *QueryDenomAuthorityMetadataResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryDenomAuthorityMetadataResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryDenomAuthorityMetadataResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryDenomAuthorityMetadataResponse proto.InternalMessageInfo
+
+func (m *QueryDenomAuthorityMetadataResponse) GetAuthorityMetadata() DenomAuthorityMetadata {
+	if m != nil {
+		return m.AuthorityMetadata
+	}
+	return DenomAuthorityMetadata{}
+}
+
+// QueryDenomsFromCreatorRequest defines the request structure for the
+// DenomsFromCreator gRPC query.
+type QueryDenomsFromCreatorRequest struct {
+	Creator string `protobuf:"bytes,1,opt,name=creator,proto3" json:"creator,omitempty" yaml:"creator"`
+}
+
+func (m *QueryDenomsFromCreatorRequest) Reset()         { *m = QueryDenomsFromCreatorRequest{} }
+func (m *QueryDenomsFromCreatorRequest) String() string { return proto.CompactTextString(m) }
+func (*QueryDenomsFromCreatorRequest) ProtoMessage()    {}
+func (*QueryDenomsFromCreatorRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6f22013ad0f72e3f, []int{4}
+}
+func (m *QueryDenomsFromCreatorRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryDenomsFromCreatorRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryDenomsFromCreatorRequest.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 *QueryDenomsFromCreatorRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryDenomsFromCreatorRequest.Merge(m, src)
+}
+func (m *QueryDenomsFromCreatorRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryDenomsFromCreatorRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryDenomsFromCreatorRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryDenomsFromCreatorRequest proto.InternalMessageInfo
+
+func (m *QueryDenomsFromCreatorRequest) GetCreator() string {
+	if m != nil {
+		return m.Creator
+	}
+	return ""
+}
+
+// QueryDenomsFromCreatorRequest defines the response structure for the
+// DenomsFromCreator gRPC query.
+type QueryDenomsFromCreatorResponse struct {
+	Denoms []string `protobuf:"bytes,1,rep,name=denoms,proto3" json:"denoms,omitempty" yaml:"denoms"`
+}
+
+func (m *QueryDenomsFromCreatorResponse) Reset()         { *m = QueryDenomsFromCreatorResponse{} }
+func (m *QueryDenomsFromCreatorResponse) String() string { return proto.CompactTextString(m) }
+func (*QueryDenomsFromCreatorResponse) ProtoMessage()    {}
+func (*QueryDenomsFromCreatorResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6f22013ad0f72e3f, []int{5}
+}
+func (m *QueryDenomsFromCreatorResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *QueryDenomsFromCreatorResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_QueryDenomsFromCreatorResponse.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 *QueryDenomsFromCreatorResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_QueryDenomsFromCreatorResponse.Merge(m, src)
+}
+func (m *QueryDenomsFromCreatorResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *QueryDenomsFromCreatorResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_QueryDenomsFromCreatorResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_QueryDenomsFromCreatorResponse proto.InternalMessageInfo
+
+func (m *QueryDenomsFromCreatorResponse) GetDenoms() []string {
+	if m != nil {
+		return m.Denoms
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*QueryParamsRequest)(nil), "osmosis.tokenfactory.v1beta1.QueryParamsRequest")
+	proto.RegisterType((*QueryParamsResponse)(nil), "osmosis.tokenfactory.v1beta1.QueryParamsResponse")
+	proto.RegisterType((*QueryDenomAuthorityMetadataRequest)(nil), "osmosis.tokenfactory.v1beta1.QueryDenomAuthorityMetadataRequest")
+	proto.RegisterType((*QueryDenomAuthorityMetadataResponse)(nil), "osmosis.tokenfactory.v1beta1.QueryDenomAuthorityMetadataResponse")
+	proto.RegisterType((*QueryDenomsFromCreatorRequest)(nil), "osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorRequest")
+	proto.RegisterType((*QueryDenomsFromCreatorResponse)(nil), "osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorResponse")
+}
+
+func init() {
+	proto.RegisterFile("osmosis/tokenfactory/v1beta1/query.proto", fileDescriptor_6f22013ad0f72e3f)
+}
+
+var fileDescriptor_6f22013ad0f72e3f = []byte{
+	// 582 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xcd, 0x6e, 0xd3, 0x40,
+	0x10, 0x8e, 0xa1, 0x0d, 0xea, 0xf2, 0x23, 0xba, 0x54, 0x08, 0xa2, 0xe2, 0xc0, 0x52, 0x55, 0x29,
+	0x2a, 0x5e, 0x52, 0x7a, 0xa2, 0x54, 0x10, 0x17, 0xc1, 0x01, 0x2a, 0x81, 0x2f, 0x08, 0x2e, 0xd1,
+	0x26, 0xd9, 0x38, 0x16, 0xb1, 0xc7, 0xf5, 0x6e, 0x80, 0xa8, 0xea, 0x85, 0x03, 0x67, 0x24, 0x8e,
+	0xbc, 0x03, 0xcf, 0xd1, 0x63, 0xa5, 0x5e, 0x38, 0x45, 0x28, 0xa9, 0x78, 0x80, 0x3c, 0x01, 0xca,
+	0xee, 0xb6, 0xb4, 0x38, 0x58, 0x01, 0x4e, 0xb6, 0x66, 0xbe, 0xf9, 0xe6, 0xfb, 0x66, 0x46, 0x8b,
+	0x4a, 0x20, 0x42, 0x10, 0x81, 0xa0, 0x12, 0xde, 0xf0, 0xa8, 0xc9, 0xea, 0x12, 0x92, 0x2e, 0x7d,
+	0x5b, 0xae, 0x71, 0xc9, 0xca, 0x74, 0xab, 0xc3, 0x93, 0xae, 0x13, 0x27, 0x20, 0x01, 0xcf, 0x1b,
+	0xa4, 0x73, 0x1c, 0xe9, 0x18, 0x64, 0x61, 0xce, 0x07, 0x1f, 0x14, 0x90, 0x8e, 0xfe, 0x74, 0x4d,
+	0x61, 0xde, 0x07, 0xf0, 0xdb, 0x9c, 0xb2, 0x38, 0xa0, 0x2c, 0x8a, 0x40, 0x32, 0x19, 0x40, 0x24,
+	0x4c, 0xf6, 0x56, 0x5d, 0x51, 0xd2, 0x1a, 0x13, 0x5c, 0xb7, 0x3a, 0x6a, 0x1c, 0x33, 0x3f, 0x88,
+	0x14, 0xd8, 0x60, 0x57, 0x33, 0x75, 0xb2, 0x8e, 0x6c, 0x41, 0x12, 0xc8, 0xee, 0x26, 0x97, 0xac,
+	0xc1, 0x24, 0x33, 0x55, 0x4b, 0x99, 0x55, 0x31, 0x4b, 0x58, 0x68, 0xc4, 0x90, 0x39, 0x84, 0x5f,
+	0x8c, 0x24, 0x3c, 0x57, 0x41, 0x8f, 0x6f, 0x75, 0xb8, 0x90, 0xe4, 0x15, 0xba, 0x74, 0x22, 0x2a,
+	0x62, 0x88, 0x04, 0xc7, 0x2e, 0xca, 0xeb, 0xe2, 0x2b, 0xd6, 0x75, 0xab, 0x74, 0x76, 0x65, 0xc1,
+	0xc9, 0x1a, 0x8e, 0xa3, 0xab, 0xdd, 0xa9, 0xdd, 0x5e, 0x31, 0xe7, 0x99, 0x4a, 0xf2, 0x0c, 0x11,
+	0x45, 0xfd, 0x88, 0x47, 0x10, 0x56, 0x7e, 0x37, 0x60, 0x04, 0xe0, 0x45, 0x34, 0xdd, 0x18, 0x01,
+	0x54, 0xa3, 0x19, 0xf7, 0xe2, 0xb0, 0x57, 0x3c, 0xd7, 0x65, 0x61, 0xfb, 0x1e, 0x51, 0x61, 0xe2,
+	0xe9, 0x34, 0xf9, 0x6a, 0xa1, 0x9b, 0x99, 0x74, 0x46, 0xf9, 0x47, 0x0b, 0xe1, 0xa3, 0x69, 0x55,
+	0x43, 0x93, 0x36, 0x36, 0x56, 0xb3, 0x6d, 0x8c, 0xa7, 0x76, 0x6f, 0x8c, 0x6c, 0x0d, 0x7b, 0xc5,
+	0xab, 0x5a, 0x57, 0x9a, 0x9d, 0x78, 0xb3, 0xa9, 0x05, 0x91, 0x4d, 0x74, 0xed, 0x97, 0x5e, 0xf1,
+	0x38, 0x81, 0x70, 0x23, 0xe1, 0x4c, 0x42, 0x72, 0xe8, 0x7c, 0x19, 0x9d, 0xa9, 0xeb, 0x88, 0xf1,
+	0x8e, 0x87, 0xbd, 0xe2, 0x05, 0xdd, 0xc3, 0x24, 0x88, 0x77, 0x08, 0x21, 0x4f, 0x91, 0xfd, 0x27,
+	0x3a, 0xe3, 0x7c, 0x09, 0xe5, 0xd5, 0xa8, 0x46, 0x3b, 0x3b, 0x5d, 0x9a, 0x71, 0x67, 0x87, 0xbd,
+	0xe2, 0xf9, 0x63, 0xa3, 0x14, 0xc4, 0x33, 0x80, 0x95, 0x83, 0x29, 0x34, 0xad, 0xd8, 0xf0, 0x17,
+	0x0b, 0xe5, 0xf5, 0xf6, 0xf0, 0x9d, 0xec, 0xe1, 0xa4, 0x8f, 0xa7, 0x50, 0xfe, 0x8b, 0x0a, 0x2d,
+	0x92, 0x2c, 0x7f, 0xd8, 0x3f, 0xf8, 0x7c, 0x6a, 0x11, 0x2f, 0xd0, 0x09, 0x2e, 0x17, 0xff, 0xb0,
+	0xd0, 0xe5, 0xf1, 0x4b, 0xc1, 0x0f, 0x27, 0xe8, 0x9d, 0x79, 0x79, 0x85, 0xca, 0x7f, 0x30, 0x18,
+	0x37, 0x4f, 0x94, 0x9b, 0x0a, 0x7e, 0x90, 0xed, 0x46, 0x4f, 0x9d, 0x6e, 0xab, 0xef, 0x0e, 0x4d,
+	0x1f, 0x10, 0xde, 0xb7, 0xd0, 0x6c, 0x6a, 0xb3, 0x78, 0x6d, 0x52, 0x85, 0x63, 0xce, 0xab, 0x70,
+	0xff, 0xdf, 0x8a, 0x8d, 0xb3, 0x0d, 0xe5, 0x6c, 0x1d, 0xaf, 0x4d, 0xe2, 0xac, 0xda, 0x4c, 0x20,
+	0xac, 0x9a, 0x4b, 0xa5, 0xdb, 0xe6, 0x67, 0xc7, 0x7d, 0xb9, 0xdb, 0xb7, 0xad, 0xbd, 0xbe, 0x6d,
+	0x7d, 0xef, 0xdb, 0xd6, 0xa7, 0x81, 0x9d, 0xdb, 0x1b, 0xd8, 0xb9, 0x6f, 0x03, 0x3b, 0xf7, 0x7a,
+	0xdd, 0x0f, 0x64, 0xab, 0x53, 0x73, 0xea, 0x10, 0xd2, 0x77, 0x90, 0x84, 0x2d, 0x68, 0xf3, 0xdb,
+	0x4d, 0xe8, 0x44, 0x0d, 0xf5, 0x26, 0xaa, 0x58, 0xbd, 0xc5, 0x82, 0x88, 0xbe, 0x3f, 0xd9, 0x58,
+	0x76, 0x63, 0x2e, 0x6a, 0x79, 0xf5, 0xa4, 0xdd, 0xfd, 0x19, 0x00, 0x00, 0xff, 0xff, 0x8e, 0x68,
+	0xf9, 0x44, 0xdd, 0x05, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// QueryClient is the client API for Query service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type QueryClient interface {
+	// Params defines a gRPC query method that returns the tokenfactory module's
+	// parameters.
+	Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
+	// DenomAuthorityMetadata defines a gRPC query method for fetching
+	// DenomAuthorityMetadata for a particular denom.
+	DenomAuthorityMetadata(ctx context.Context, in *QueryDenomAuthorityMetadataRequest, opts ...grpc.CallOption) (*QueryDenomAuthorityMetadataResponse, error)
+	// DenomsFromCreator defines a gRPC query method for fetching all
+	// denominations created by a specific admin/creator.
+	DenomsFromCreator(ctx context.Context, in *QueryDenomsFromCreatorRequest, opts ...grpc.CallOption) (*QueryDenomsFromCreatorResponse, error)
+}
+
+type queryClient struct {
+	cc grpc1.ClientConn
+}
+
+func NewQueryClient(cc grpc1.ClientConn) QueryClient {
+	return &queryClient{cc}
+}
+
+func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
+	out := new(QueryParamsResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Query/Params", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *queryClient) DenomAuthorityMetadata(ctx context.Context, in *QueryDenomAuthorityMetadataRequest, opts ...grpc.CallOption) (*QueryDenomAuthorityMetadataResponse, error) {
+	out := new(QueryDenomAuthorityMetadataResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Query/DenomAuthorityMetadata", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *queryClient) DenomsFromCreator(ctx context.Context, in *QueryDenomsFromCreatorRequest, opts ...grpc.CallOption) (*QueryDenomsFromCreatorResponse, error) {
+	out := new(QueryDenomsFromCreatorResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Query/DenomsFromCreator", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// QueryServer is the server API for Query service.
+type QueryServer interface {
+	// Params defines a gRPC query method that returns the tokenfactory module's
+	// parameters.
+	Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
+	// DenomAuthorityMetadata defines a gRPC query method for fetching
+	// DenomAuthorityMetadata for a particular denom.
+	DenomAuthorityMetadata(context.Context, *QueryDenomAuthorityMetadataRequest) (*QueryDenomAuthorityMetadataResponse, error)
+	// DenomsFromCreator defines a gRPC query method for fetching all
+	// denominations created by a specific admin/creator.
+	DenomsFromCreator(context.Context, *QueryDenomsFromCreatorRequest) (*QueryDenomsFromCreatorResponse, error)
+}
+
+// UnimplementedQueryServer can be embedded to have forward compatible implementations.
+type UnimplementedQueryServer struct {
+}
+
+func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
+}
+func (*UnimplementedQueryServer) DenomAuthorityMetadata(ctx context.Context, req *QueryDenomAuthorityMetadataRequest) (*QueryDenomAuthorityMetadataResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method DenomAuthorityMetadata not implemented")
+}
+func (*UnimplementedQueryServer) DenomsFromCreator(ctx context.Context, req *QueryDenomsFromCreatorRequest) (*QueryDenomsFromCreatorResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method DenomsFromCreator not implemented")
+}
+
+func RegisterQueryServer(s grpc1.Server, srv QueryServer) {
+	s.RegisterService(&_Query_serviceDesc, srv)
+}
+
+func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryParamsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).Params(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Query/Params",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Query_DenomAuthorityMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryDenomAuthorityMetadataRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).DenomAuthorityMetadata(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Query/DenomAuthorityMetadata",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).DenomAuthorityMetadata(ctx, req.(*QueryDenomAuthorityMetadataRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Query_DenomsFromCreator_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(QueryDenomsFromCreatorRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(QueryServer).DenomsFromCreator(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Query/DenomsFromCreator",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(QueryServer).DenomsFromCreator(ctx, req.(*QueryDenomsFromCreatorRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Query_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "osmosis.tokenfactory.v1beta1.Query",
+	HandlerType: (*QueryServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "Params",
+			Handler:    _Query_Params_Handler,
+		},
+		{
+			MethodName: "DenomAuthorityMetadata",
+			Handler:    _Query_DenomAuthorityMetadata_Handler,
+		},
+		{
+			MethodName: "DenomsFromCreator",
+			Handler:    _Query_DenomsFromCreator_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "osmosis/tokenfactory/v1beta1/query.proto",
+}
+
+func (m *QueryParamsRequest) 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 *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryParamsResponse) 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 *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Params.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintQuery(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryDenomAuthorityMetadataRequest) 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 *QueryDenomAuthorityMetadataRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryDenomAuthorityMetadataRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Denom) > 0 {
+		i -= len(m.Denom)
+		copy(dAtA[i:], m.Denom)
+		i = encodeVarintQuery(dAtA, i, uint64(len(m.Denom)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryDenomAuthorityMetadataResponse) 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 *QueryDenomAuthorityMetadataResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryDenomAuthorityMetadataResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.AuthorityMetadata.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintQuery(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0xa
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryDenomsFromCreatorRequest) 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 *QueryDenomsFromCreatorRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryDenomsFromCreatorRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Creator) > 0 {
+		i -= len(m.Creator)
+		copy(dAtA[i:], m.Creator)
+		i = encodeVarintQuery(dAtA, i, uint64(len(m.Creator)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *QueryDenomsFromCreatorResponse) 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 *QueryDenomsFromCreatorResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *QueryDenomsFromCreatorResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Denoms) > 0 {
+		for iNdEx := len(m.Denoms) - 1; iNdEx >= 0; iNdEx-- {
+			i -= len(m.Denoms[iNdEx])
+			copy(dAtA[i:], m.Denoms[iNdEx])
+			i = encodeVarintQuery(dAtA, i, uint64(len(m.Denoms[iNdEx])))
+			i--
+			dAtA[i] = 0xa
+		}
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintQuery(dAtA []byte, offset int, v uint64) int {
+	offset -= sovQuery(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *QueryParamsRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *QueryParamsResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.Params.Size()
+	n += 1 + l + sovQuery(uint64(l))
+	return n
+}
+
+func (m *QueryDenomAuthorityMetadataRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Denom)
+	if l > 0 {
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryDenomAuthorityMetadataResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = m.AuthorityMetadata.Size()
+	n += 1 + l + sovQuery(uint64(l))
+	return n
+}
+
+func (m *QueryDenomsFromCreatorRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Creator)
+	if l > 0 {
+		n += 1 + l + sovQuery(uint64(l))
+	}
+	return n
+}
+
+func (m *QueryDenomsFromCreatorResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if len(m.Denoms) > 0 {
+		for _, s := range m.Denoms {
+			l = len(s)
+			n += 1 + l + sovQuery(uint64(l))
+		}
+	}
+	return n
+}
+
+func sovQuery(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozQuery(x uint64) (n int) {
+	return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *QueryParamsRequest) 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 ErrIntOverflowQuery
+			}
+			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: QueryParamsRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryParamsResponse) 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 ErrIntOverflowQuery
+			}
+			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: QueryParamsResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryDenomAuthorityMetadataRequest) 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 ErrIntOverflowQuery
+			}
+			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: QueryDenomAuthorityMetadataRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryDenomAuthorityMetadataRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				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 ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Denom = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryDenomAuthorityMetadataResponse) 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 ErrIntOverflowQuery
+			}
+			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: QueryDenomAuthorityMetadataResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryDenomAuthorityMetadataResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AuthorityMetadata", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.AuthorityMetadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryDenomsFromCreatorRequest) 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 ErrIntOverflowQuery
+			}
+			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: QueryDenomsFromCreatorRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryDenomsFromCreatorRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Creator", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				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 ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Creator = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *QueryDenomsFromCreatorResponse) 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 ErrIntOverflowQuery
+			}
+			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: QueryDenomsFromCreatorResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: QueryDenomsFromCreatorResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Denoms", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowQuery
+				}
+				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 ErrInvalidLengthQuery
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Denoms = append(m.Denoms, string(dAtA[iNdEx:postIndex]))
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipQuery(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthQuery
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipQuery(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowQuery
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowQuery
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthQuery
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupQuery
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthQuery
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthQuery        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowQuery          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group")
+)

+ 355 - 0
wormchain/x/tokenfactory/types/query.pb.gw.go

@@ -0,0 +1,355 @@
+// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
+// source: osmosis/tokenfactory/v1beta1/query.proto
+
+/*
+Package types is a reverse proxy.
+
+It translates gRPC into RESTful JSON APIs.
+*/
+package types
+
+import (
+	"context"
+	"io"
+	"net/http"
+
+	"github.com/golang/protobuf/descriptor"
+	"github.com/golang/protobuf/proto"
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"github.com/grpc-ecosystem/grpc-gateway/utilities"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/grpclog"
+	"google.golang.org/grpc/metadata"
+	"google.golang.org/grpc/status"
+)
+
+// Suppress "imported and not used" errors
+var _ codes.Code
+var _ io.Reader
+var _ status.Status
+var _ = runtime.String
+var _ = utilities.NewDoubleArray
+var _ = descriptor.ForMessage
+var _ = metadata.Join
+
+func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryParamsRequest
+	var metadata runtime.ServerMetadata
+
+	msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryParamsRequest
+	var metadata runtime.ServerMetadata
+
+	msg, err := server.Params(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_Query_DenomAuthorityMetadata_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryDenomAuthorityMetadataRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["denom"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "denom")
+	}
+
+	protoReq.Denom, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "denom", err)
+	}
+
+	msg, err := client.DenomAuthorityMetadata(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_DenomAuthorityMetadata_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryDenomAuthorityMetadataRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["denom"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "denom")
+	}
+
+	protoReq.Denom, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "denom", err)
+	}
+
+	msg, err := server.DenomAuthorityMetadata(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+func request_Query_DenomsFromCreator_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryDenomsFromCreatorRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["creator"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "creator")
+	}
+
+	protoReq.Creator, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "creator", err)
+	}
+
+	msg, err := client.DenomsFromCreator(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_Query_DenomsFromCreator_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq QueryDenomsFromCreatorRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["creator"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "creator")
+	}
+
+	protoReq.Creator, err = runtime.String(val)
+
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "creator", err)
+	}
+
+	msg, err := server.DenomsFromCreator(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
+// RegisterQueryHandlerServer registers the http handlers for service Query to "mux".
+// UnaryRPC     :call QueryServer directly.
+// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
+// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead.
+func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error {
+
+	mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_DenomAuthorityMetadata_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_DenomAuthorityMetadata_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_DenomAuthorityMetadata_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_DenomsFromCreator_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_Query_DenomsFromCreator_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_DenomsFromCreator_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but
+// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
+func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
+	conn, err := grpc.Dial(endpoint, opts...)
+	if err != nil {
+		return err
+	}
+	defer func() {
+		if err != nil {
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+			return
+		}
+		go func() {
+			<-ctx.Done()
+			if cerr := conn.Close(); cerr != nil {
+				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
+			}
+		}()
+	}()
+
+	return RegisterQueryHandler(ctx, mux, conn)
+}
+
+// RegisterQueryHandler registers the http handlers for service Query to "mux".
+// The handlers forward requests to the grpc endpoint over "conn".
+func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
+	return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn))
+}
+
+// RegisterQueryHandlerClient registers the http handlers for service Query
+// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient".
+// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient"
+// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
+// "QueryClient" to call the correct interceptors.
+func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error {
+
+	mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_DenomAuthorityMetadata_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_DenomAuthorityMetadata_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_DenomAuthorityMetadata_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	mux.Handle("GET", pattern_Query_DenomsFromCreator_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_Query_DenomsFromCreator_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_Query_DenomsFromCreator_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
+	return nil
+}
+
+var (
+	pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"osmosis", "tokenfactory", "v1beta1", "params"}, "", runtime.AssumeColonVerbOpt(true)))
+
+	pattern_Query_DenomAuthorityMetadata_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 2, 5}, []string{"osmosis", "tokenfactory", "v1beta1", "denoms", "denom", "authority_metadata"}, "", runtime.AssumeColonVerbOpt(true)))
+
+	pattern_Query_DenomsFromCreator_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"osmosis", "tokenfactory", "v1beta1", "denoms_from_creator", "creator"}, "", runtime.AssumeColonVerbOpt(true)))
+)
+
+var (
+	forward_Query_Params_0 = runtime.ForwardResponseMessage
+
+	forward_Query_DenomAuthorityMetadata_0 = runtime.ForwardResponseMessage
+
+	forward_Query_DenomsFromCreator_0 = runtime.ForwardResponseMessage
+)

+ 2830 - 0
wormchain/x/tokenfactory/types/tx.pb.go

@@ -0,0 +1,2830 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: osmosis/tokenfactory/v1beta1/tx.proto
+
+package types
+
+import (
+	context "context"
+	fmt "fmt"
+	types "github.com/cosmos/cosmos-sdk/types"
+	types1 "github.com/cosmos/cosmos-sdk/x/bank/types"
+	_ "github.com/gogo/protobuf/gogoproto"
+	grpc1 "github.com/gogo/protobuf/grpc"
+	proto "github.com/gogo/protobuf/proto"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+// MsgCreateDenom defines the message structure for the CreateDenom gRPC service
+// method. It allows an account to create a new denom. It requires a sender
+// address and a sub denomination. The (sender_address, sub_denomination) tuple
+// must be unique and cannot be re-used.
+//
+// The resulting denom created is defined as
+// <factory/{creatorAddress}/{subdenom}>. The resulting denom's admin is
+// originally set to be the creator, but this can be changed later. The token
+// denom does not indicate the current admin.
+type MsgCreateDenom struct {
+	Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+	// subdenom can be up to 44 "alphanumeric" characters long.
+	Subdenom string `protobuf:"bytes,2,opt,name=subdenom,proto3" json:"subdenom,omitempty" yaml:"subdenom"`
+}
+
+func (m *MsgCreateDenom) Reset()         { *m = MsgCreateDenom{} }
+func (m *MsgCreateDenom) String() string { return proto.CompactTextString(m) }
+func (*MsgCreateDenom) ProtoMessage()    {}
+func (*MsgCreateDenom) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{0}
+}
+func (m *MsgCreateDenom) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCreateDenom) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCreateDenom.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 *MsgCreateDenom) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCreateDenom.Merge(m, src)
+}
+func (m *MsgCreateDenom) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCreateDenom) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCreateDenom.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCreateDenom proto.InternalMessageInfo
+
+func (m *MsgCreateDenom) GetSender() string {
+	if m != nil {
+		return m.Sender
+	}
+	return ""
+}
+
+func (m *MsgCreateDenom) GetSubdenom() string {
+	if m != nil {
+		return m.Subdenom
+	}
+	return ""
+}
+
+// MsgCreateDenomResponse is the return value of MsgCreateDenom
+// It returns the full string of the newly created denom
+type MsgCreateDenomResponse struct {
+	NewTokenDenom string `protobuf:"bytes,1,opt,name=new_token_denom,json=newTokenDenom,proto3" json:"new_token_denom,omitempty" yaml:"new_token_denom"`
+}
+
+func (m *MsgCreateDenomResponse) Reset()         { *m = MsgCreateDenomResponse{} }
+func (m *MsgCreateDenomResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgCreateDenomResponse) ProtoMessage()    {}
+func (*MsgCreateDenomResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{1}
+}
+func (m *MsgCreateDenomResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgCreateDenomResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgCreateDenomResponse.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 *MsgCreateDenomResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgCreateDenomResponse.Merge(m, src)
+}
+func (m *MsgCreateDenomResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgCreateDenomResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgCreateDenomResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgCreateDenomResponse proto.InternalMessageInfo
+
+func (m *MsgCreateDenomResponse) GetNewTokenDenom() string {
+	if m != nil {
+		return m.NewTokenDenom
+	}
+	return ""
+}
+
+// MsgMint is the sdk.Msg type for allowing an admin account to mint
+// more of a token.  For now, we only support minting to the sender account
+type MsgMint struct {
+	Sender        string     `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+	Amount        types.Coin `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount" yaml:"amount"`
+	MintToAddress string     `protobuf:"bytes,3,opt,name=mintToAddress,proto3" json:"mintToAddress,omitempty" yaml:"mint_to_address"`
+}
+
+func (m *MsgMint) Reset()         { *m = MsgMint{} }
+func (m *MsgMint) String() string { return proto.CompactTextString(m) }
+func (*MsgMint) ProtoMessage()    {}
+func (*MsgMint) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{2}
+}
+func (m *MsgMint) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgMint) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgMint.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 *MsgMint) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgMint.Merge(m, src)
+}
+func (m *MsgMint) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgMint) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgMint.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgMint proto.InternalMessageInfo
+
+func (m *MsgMint) GetSender() string {
+	if m != nil {
+		return m.Sender
+	}
+	return ""
+}
+
+func (m *MsgMint) GetAmount() types.Coin {
+	if m != nil {
+		return m.Amount
+	}
+	return types.Coin{}
+}
+
+func (m *MsgMint) GetMintToAddress() string {
+	if m != nil {
+		return m.MintToAddress
+	}
+	return ""
+}
+
+type MsgMintResponse struct {
+}
+
+func (m *MsgMintResponse) Reset()         { *m = MsgMintResponse{} }
+func (m *MsgMintResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgMintResponse) ProtoMessage()    {}
+func (*MsgMintResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{3}
+}
+func (m *MsgMintResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgMintResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgMintResponse.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 *MsgMintResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgMintResponse.Merge(m, src)
+}
+func (m *MsgMintResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgMintResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgMintResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgMintResponse proto.InternalMessageInfo
+
+// MsgBurn is the sdk.Msg type for allowing an admin account to burn
+// a token.  For now, we only support burning from the sender account.
+type MsgBurn struct {
+	Sender          string     `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+	Amount          types.Coin `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount" yaml:"amount"`
+	BurnFromAddress string     `protobuf:"bytes,3,opt,name=burnFromAddress,proto3" json:"burnFromAddress,omitempty" yaml:"burn_from_address"`
+}
+
+func (m *MsgBurn) Reset()         { *m = MsgBurn{} }
+func (m *MsgBurn) String() string { return proto.CompactTextString(m) }
+func (*MsgBurn) ProtoMessage()    {}
+func (*MsgBurn) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{4}
+}
+func (m *MsgBurn) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgBurn) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgBurn.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 *MsgBurn) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgBurn.Merge(m, src)
+}
+func (m *MsgBurn) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgBurn) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgBurn.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgBurn proto.InternalMessageInfo
+
+func (m *MsgBurn) GetSender() string {
+	if m != nil {
+		return m.Sender
+	}
+	return ""
+}
+
+func (m *MsgBurn) GetAmount() types.Coin {
+	if m != nil {
+		return m.Amount
+	}
+	return types.Coin{}
+}
+
+func (m *MsgBurn) GetBurnFromAddress() string {
+	if m != nil {
+		return m.BurnFromAddress
+	}
+	return ""
+}
+
+type MsgBurnResponse struct {
+}
+
+func (m *MsgBurnResponse) Reset()         { *m = MsgBurnResponse{} }
+func (m *MsgBurnResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgBurnResponse) ProtoMessage()    {}
+func (*MsgBurnResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{5}
+}
+func (m *MsgBurnResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgBurnResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgBurnResponse.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 *MsgBurnResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgBurnResponse.Merge(m, src)
+}
+func (m *MsgBurnResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgBurnResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgBurnResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgBurnResponse proto.InternalMessageInfo
+
+// MsgChangeAdmin is the sdk.Msg type for allowing an admin account to reassign
+// adminship of a denom to a new account
+type MsgChangeAdmin struct {
+	Sender   string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+	Denom    string `protobuf:"bytes,2,opt,name=denom,proto3" json:"denom,omitempty" yaml:"denom"`
+	NewAdmin string `protobuf:"bytes,3,opt,name=new_admin,json=newAdmin,proto3" json:"new_admin,omitempty" yaml:"new_admin"`
+}
+
+func (m *MsgChangeAdmin) Reset()         { *m = MsgChangeAdmin{} }
+func (m *MsgChangeAdmin) String() string { return proto.CompactTextString(m) }
+func (*MsgChangeAdmin) ProtoMessage()    {}
+func (*MsgChangeAdmin) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{6}
+}
+func (m *MsgChangeAdmin) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgChangeAdmin) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgChangeAdmin.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 *MsgChangeAdmin) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgChangeAdmin.Merge(m, src)
+}
+func (m *MsgChangeAdmin) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgChangeAdmin) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgChangeAdmin.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgChangeAdmin proto.InternalMessageInfo
+
+func (m *MsgChangeAdmin) GetSender() string {
+	if m != nil {
+		return m.Sender
+	}
+	return ""
+}
+
+func (m *MsgChangeAdmin) GetDenom() string {
+	if m != nil {
+		return m.Denom
+	}
+	return ""
+}
+
+func (m *MsgChangeAdmin) GetNewAdmin() string {
+	if m != nil {
+		return m.NewAdmin
+	}
+	return ""
+}
+
+// MsgChangeAdminResponse defines the response structure for an executed
+// MsgChangeAdmin message.
+type MsgChangeAdminResponse struct {
+}
+
+func (m *MsgChangeAdminResponse) Reset()         { *m = MsgChangeAdminResponse{} }
+func (m *MsgChangeAdminResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgChangeAdminResponse) ProtoMessage()    {}
+func (*MsgChangeAdminResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{7}
+}
+func (m *MsgChangeAdminResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgChangeAdminResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgChangeAdminResponse.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 *MsgChangeAdminResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgChangeAdminResponse.Merge(m, src)
+}
+func (m *MsgChangeAdminResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgChangeAdminResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgChangeAdminResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgChangeAdminResponse proto.InternalMessageInfo
+
+// MsgSetDenomMetadata is the sdk.Msg type for allowing an admin account to set
+// the denom's bank metadata
+type MsgSetDenomMetadata struct {
+	Sender   string          `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+	Metadata types1.Metadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata" yaml:"metadata"`
+}
+
+func (m *MsgSetDenomMetadata) Reset()         { *m = MsgSetDenomMetadata{} }
+func (m *MsgSetDenomMetadata) String() string { return proto.CompactTextString(m) }
+func (*MsgSetDenomMetadata) ProtoMessage()    {}
+func (*MsgSetDenomMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{8}
+}
+func (m *MsgSetDenomMetadata) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgSetDenomMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgSetDenomMetadata.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 *MsgSetDenomMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgSetDenomMetadata.Merge(m, src)
+}
+func (m *MsgSetDenomMetadata) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgSetDenomMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgSetDenomMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSetDenomMetadata proto.InternalMessageInfo
+
+func (m *MsgSetDenomMetadata) GetSender() string {
+	if m != nil {
+		return m.Sender
+	}
+	return ""
+}
+
+func (m *MsgSetDenomMetadata) GetMetadata() types1.Metadata {
+	if m != nil {
+		return m.Metadata
+	}
+	return types1.Metadata{}
+}
+
+// MsgSetDenomMetadataResponse defines the response structure for an executed
+// MsgSetDenomMetadata message.
+type MsgSetDenomMetadataResponse struct {
+}
+
+func (m *MsgSetDenomMetadataResponse) Reset()         { *m = MsgSetDenomMetadataResponse{} }
+func (m *MsgSetDenomMetadataResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgSetDenomMetadataResponse) ProtoMessage()    {}
+func (*MsgSetDenomMetadataResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{9}
+}
+func (m *MsgSetDenomMetadataResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgSetDenomMetadataResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgSetDenomMetadataResponse.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 *MsgSetDenomMetadataResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgSetDenomMetadataResponse.Merge(m, src)
+}
+func (m *MsgSetDenomMetadataResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgSetDenomMetadataResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgSetDenomMetadataResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgSetDenomMetadataResponse proto.InternalMessageInfo
+
+type MsgForceTransfer struct {
+	Sender              string     `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"`
+	Amount              types.Coin `protobuf:"bytes,2,opt,name=amount,proto3" json:"amount" yaml:"amount"`
+	TransferFromAddress string     `protobuf:"bytes,3,opt,name=transferFromAddress,proto3" json:"transferFromAddress,omitempty" yaml:"transfer_from_address"`
+	TransferToAddress   string     `protobuf:"bytes,4,opt,name=transferToAddress,proto3" json:"transferToAddress,omitempty" yaml:"transfer_to_address"`
+}
+
+func (m *MsgForceTransfer) Reset()         { *m = MsgForceTransfer{} }
+func (m *MsgForceTransfer) String() string { return proto.CompactTextString(m) }
+func (*MsgForceTransfer) ProtoMessage()    {}
+func (*MsgForceTransfer) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{10}
+}
+func (m *MsgForceTransfer) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgForceTransfer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgForceTransfer.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 *MsgForceTransfer) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgForceTransfer.Merge(m, src)
+}
+func (m *MsgForceTransfer) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgForceTransfer) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgForceTransfer.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgForceTransfer proto.InternalMessageInfo
+
+func (m *MsgForceTransfer) GetSender() string {
+	if m != nil {
+		return m.Sender
+	}
+	return ""
+}
+
+func (m *MsgForceTransfer) GetAmount() types.Coin {
+	if m != nil {
+		return m.Amount
+	}
+	return types.Coin{}
+}
+
+func (m *MsgForceTransfer) GetTransferFromAddress() string {
+	if m != nil {
+		return m.TransferFromAddress
+	}
+	return ""
+}
+
+func (m *MsgForceTransfer) GetTransferToAddress() string {
+	if m != nil {
+		return m.TransferToAddress
+	}
+	return ""
+}
+
+type MsgForceTransferResponse struct {
+}
+
+func (m *MsgForceTransferResponse) Reset()         { *m = MsgForceTransferResponse{} }
+func (m *MsgForceTransferResponse) String() string { return proto.CompactTextString(m) }
+func (*MsgForceTransferResponse) ProtoMessage()    {}
+func (*MsgForceTransferResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_283b6c9a90a846b4, []int{11}
+}
+func (m *MsgForceTransferResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *MsgForceTransferResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_MsgForceTransferResponse.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 *MsgForceTransferResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MsgForceTransferResponse.Merge(m, src)
+}
+func (m *MsgForceTransferResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *MsgForceTransferResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_MsgForceTransferResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MsgForceTransferResponse proto.InternalMessageInfo
+
+func init() {
+	proto.RegisterType((*MsgCreateDenom)(nil), "osmosis.tokenfactory.v1beta1.MsgCreateDenom")
+	proto.RegisterType((*MsgCreateDenomResponse)(nil), "osmosis.tokenfactory.v1beta1.MsgCreateDenomResponse")
+	proto.RegisterType((*MsgMint)(nil), "osmosis.tokenfactory.v1beta1.MsgMint")
+	proto.RegisterType((*MsgMintResponse)(nil), "osmosis.tokenfactory.v1beta1.MsgMintResponse")
+	proto.RegisterType((*MsgBurn)(nil), "osmosis.tokenfactory.v1beta1.MsgBurn")
+	proto.RegisterType((*MsgBurnResponse)(nil), "osmosis.tokenfactory.v1beta1.MsgBurnResponse")
+	proto.RegisterType((*MsgChangeAdmin)(nil), "osmosis.tokenfactory.v1beta1.MsgChangeAdmin")
+	proto.RegisterType((*MsgChangeAdminResponse)(nil), "osmosis.tokenfactory.v1beta1.MsgChangeAdminResponse")
+	proto.RegisterType((*MsgSetDenomMetadata)(nil), "osmosis.tokenfactory.v1beta1.MsgSetDenomMetadata")
+	proto.RegisterType((*MsgSetDenomMetadataResponse)(nil), "osmosis.tokenfactory.v1beta1.MsgSetDenomMetadataResponse")
+	proto.RegisterType((*MsgForceTransfer)(nil), "osmosis.tokenfactory.v1beta1.MsgForceTransfer")
+	proto.RegisterType((*MsgForceTransferResponse)(nil), "osmosis.tokenfactory.v1beta1.MsgForceTransferResponse")
+}
+
+func init() {
+	proto.RegisterFile("osmosis/tokenfactory/v1beta1/tx.proto", fileDescriptor_283b6c9a90a846b4)
+}
+
+var fileDescriptor_283b6c9a90a846b4 = []byte{
+	// 753 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x56, 0xcd, 0x6e, 0xd3, 0x4a,
+	0x14, 0x8e, 0xfb, 0x77, 0xdb, 0xe9, 0xcd, 0x4d, 0xea, 0xf6, 0x96, 0x60, 0x5a, 0xbb, 0x1a, 0xa9,
+	0x08, 0x24, 0x6a, 0x2b, 0x05, 0x21, 0x81, 0x84, 0x44, 0x53, 0x54, 0xb1, 0x20, 0x1b, 0x53, 0x09,
+	0x09, 0x55, 0x8a, 0x26, 0xc9, 0xc4, 0xb1, 0x5a, 0xcf, 0x14, 0xcf, 0x84, 0xb4, 0x3b, 0x1e, 0x81,
+	0x05, 0xe2, 0x05, 0x58, 0xf1, 0x16, 0xac, 0x50, 0x97, 0x5d, 0xb2, 0xb2, 0x50, 0xfb, 0x06, 0x7e,
+	0x02, 0xe4, 0x99, 0xb1, 0xf3, 0x57, 0x91, 0x64, 0xd5, 0x5d, 0x32, 0xe7, 0xfb, 0xbe, 0x39, 0xdf,
+	0x99, 0x73, 0x8e, 0x0c, 0xb6, 0x29, 0x0b, 0x28, 0xf3, 0x99, 0xc3, 0xe9, 0x31, 0x26, 0x2d, 0xd4,
+	0xe0, 0x34, 0x3c, 0x77, 0x3e, 0x96, 0xeb, 0x98, 0xa3, 0xb2, 0xc3, 0xcf, 0xec, 0xd3, 0x90, 0x72,
+	0xaa, 0x6f, 0x28, 0x98, 0xdd, 0x0f, 0xb3, 0x15, 0xcc, 0x58, 0xf3, 0xa8, 0x47, 0x05, 0xd0, 0x49,
+	0x7e, 0x49, 0x8e, 0x61, 0x36, 0x04, 0xc9, 0xa9, 0x23, 0x86, 0x33, 0xc5, 0x06, 0xf5, 0xc9, 0x48,
+	0x9c, 0x1c, 0x67, 0xf1, 0xe4, 0x8f, 0x8c, 0xc3, 0x13, 0xf0, 0x5f, 0x95, 0x79, 0xfb, 0x21, 0x46,
+	0x1c, 0xbf, 0xc2, 0x84, 0x06, 0xfa, 0x43, 0xb0, 0xc0, 0x30, 0x69, 0xe2, 0xb0, 0xa4, 0x6d, 0x69,
+	0x0f, 0x96, 0x2a, 0x2b, 0x71, 0x64, 0xe5, 0xcf, 0x51, 0x70, 0xf2, 0x1c, 0xca, 0x73, 0xe8, 0x2a,
+	0x80, 0xee, 0x80, 0x45, 0xd6, 0xa9, 0x37, 0x13, 0x5a, 0x69, 0x46, 0x80, 0x57, 0xe3, 0xc8, 0x2a,
+	0x28, 0xb0, 0x8a, 0x40, 0x37, 0x03, 0xc1, 0x23, 0xb0, 0x3e, 0x78, 0x9b, 0x8b, 0xd9, 0x29, 0x25,
+	0x0c, 0xeb, 0x15, 0x50, 0x20, 0xb8, 0x5b, 0x13, 0xce, 0x6b, 0x52, 0x51, 0x5e, 0x6f, 0xc4, 0x91,
+	0xb5, 0x2e, 0x15, 0x87, 0x00, 0xd0, 0xcd, 0x13, 0xdc, 0x3d, 0x4c, 0x0e, 0x84, 0x16, 0xfc, 0xa1,
+	0x81, 0x7f, 0xaa, 0xcc, 0xab, 0xfa, 0x84, 0x4f, 0xe3, 0xe2, 0x35, 0x58, 0x40, 0x01, 0xed, 0x10,
+	0x2e, 0x3c, 0x2c, 0xef, 0xde, 0xb5, 0x65, 0xcd, 0xec, 0xa4, 0xa6, 0x69, 0xf9, 0xed, 0x7d, 0xea,
+	0x93, 0xca, 0xff, 0x17, 0x91, 0x95, 0xeb, 0x29, 0x49, 0x1a, 0x74, 0x15, 0x5f, 0x7f, 0x09, 0xf2,
+	0x81, 0x4f, 0xf8, 0x21, 0xdd, 0x6b, 0x36, 0x43, 0xcc, 0x58, 0x69, 0x76, 0xd8, 0x42, 0x12, 0xae,
+	0x71, 0x5a, 0x43, 0x12, 0x00, 0xdd, 0x41, 0x02, 0x5c, 0x01, 0x05, 0xe5, 0x20, 0xad, 0x0c, 0xfc,
+	0x29, 0x5d, 0x55, 0x3a, 0x21, 0xb9, 0x1d, 0x57, 0x07, 0xa0, 0x50, 0xef, 0x84, 0xe4, 0x20, 0xa4,
+	0xc1, 0xa0, 0xaf, 0x8d, 0x38, 0xb2, 0x4a, 0x92, 0x93, 0x00, 0x6a, 0xad, 0x90, 0x06, 0x3d, 0x67,
+	0xc3, 0x24, 0xe5, 0x2d, 0xf1, 0x91, 0x79, 0xfb, 0xaa, 0xc9, 0xf6, 0x6b, 0x23, 0xe2, 0xe1, 0xbd,
+	0x66, 0xe0, 0x4f, 0x65, 0xf1, 0x3e, 0x98, 0xef, 0xef, 0xbd, 0x62, 0x1c, 0x59, 0xff, 0x4a, 0xa4,
+	0xea, 0x0f, 0x19, 0xd6, 0xcb, 0x60, 0x29, 0x69, 0x1d, 0x94, 0xe8, 0xab, 0xd4, 0xd7, 0xe2, 0xc8,
+	0x2a, 0xf6, 0xba, 0x4a, 0x84, 0xa0, 0xbb, 0x48, 0x70, 0x57, 0x64, 0x01, 0x4b, 0xb2, 0x51, 0x7b,
+	0x79, 0x65, 0x29, 0x7f, 0xd1, 0xc0, 0x6a, 0x95, 0x79, 0x6f, 0x31, 0x17, 0x4d, 0x57, 0xc5, 0x1c,
+	0x35, 0x11, 0x47, 0xd3, 0xe4, 0xed, 0x82, 0xc5, 0x40, 0xd1, 0xd4, 0xe3, 0x6c, 0xf6, 0x1e, 0x87,
+	0x1c, 0x67, 0x8f, 0x93, 0x6a, 0x57, 0xee, 0xa8, 0x07, 0x52, 0x93, 0x95, 0x92, 0xa1, 0x9b, 0xe9,
+	0xc0, 0x4d, 0x70, 0xef, 0x86, 0xac, 0xb2, 0xac, 0xbf, 0xcf, 0x80, 0x62, 0x95, 0x79, 0x07, 0x34,
+	0x6c, 0xe0, 0xc3, 0x10, 0x11, 0xd6, 0xc2, 0xe1, 0xed, 0x74, 0x93, 0x0b, 0x56, 0xb9, 0x4a, 0x60,
+	0xb4, 0xa3, 0xb6, 0xe2, 0xc8, 0xda, 0x90, 0xbc, 0x14, 0x34, 0xd4, 0x55, 0x37, 0x91, 0xf5, 0x37,
+	0x60, 0x25, 0x3d, 0xee, 0xcd, 0xde, 0x9c, 0x50, 0x34, 0xe3, 0xc8, 0x32, 0x86, 0x14, 0xfb, 0xe7,
+	0x6f, 0x94, 0x08, 0x0d, 0x50, 0x1a, 0x2e, 0x55, 0x5a, 0xc7, 0xdd, 0x6f, 0xf3, 0x60, 0xb6, 0xca,
+	0x3c, 0xfd, 0x03, 0x58, 0xee, 0xdf, 0x99, 0x8f, 0xec, 0xbf, 0xad, 0x6e, 0x7b, 0x70, 0xe7, 0x19,
+	0x4f, 0xa6, 0x41, 0x67, 0x1b, 0xf2, 0x08, 0xcc, 0x89, 0xcd, 0xb6, 0x3d, 0x96, 0x9d, 0xc0, 0x8c,
+	0x9d, 0x89, 0x60, 0xfd, 0xea, 0x62, 0xc3, 0x8c, 0x57, 0x4f, 0x60, 0x13, 0xa8, 0xf7, 0xcf, 0xb9,
+	0x28, 0x57, 0xdf, 0x8c, 0x4f, 0x50, 0xae, 0x1e, 0x7a, 0x92, 0x72, 0x8d, 0xce, 0xa9, 0xfe, 0x49,
+	0x03, 0xc5, 0x91, 0x21, 0x2d, 0x8f, 0x95, 0x1a, 0xa6, 0x18, 0xcf, 0xa6, 0xa6, 0x64, 0x29, 0x74,
+	0x41, 0x7e, 0x70, 0xe0, 0xec, 0xb1, 0x5a, 0x03, 0x78, 0xe3, 0xe9, 0x74, 0xf8, 0xf4, 0xe2, 0xca,
+	0xbb, 0x8b, 0x2b, 0x53, 0xbb, 0xbc, 0x32, 0xb5, 0xdf, 0x57, 0xa6, 0xf6, 0xf9, 0xda, 0xcc, 0x5d,
+	0x5e, 0x9b, 0xb9, 0x5f, 0xd7, 0x66, 0xee, 0xfd, 0x0b, 0xcf, 0xe7, 0xed, 0x4e, 0xdd, 0x6e, 0xd0,
+	0xc0, 0xe9, 0xd2, 0x30, 0x68, 0xd3, 0x13, 0xbc, 0xd3, 0xa2, 0x1d, 0xd2, 0x44, 0xdc, 0xa7, 0x44,
+	0x9c, 0x35, 0xda, 0xc8, 0x27, 0xce, 0xd9, 0xe0, 0xc7, 0x0a, 0x3f, 0x3f, 0xc5, 0xac, 0xbe, 0x20,
+	0x3e, 0x1a, 0x1e, 0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0xb4, 0xe0, 0xd3, 0x97, 0xd1, 0x08, 0x00,
+	0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// MsgClient is the client API for Msg service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type MsgClient interface {
+	CreateDenom(ctx context.Context, in *MsgCreateDenom, opts ...grpc.CallOption) (*MsgCreateDenomResponse, error)
+	Mint(ctx context.Context, in *MsgMint, opts ...grpc.CallOption) (*MsgMintResponse, error)
+	Burn(ctx context.Context, in *MsgBurn, opts ...grpc.CallOption) (*MsgBurnResponse, error)
+	ChangeAdmin(ctx context.Context, in *MsgChangeAdmin, opts ...grpc.CallOption) (*MsgChangeAdminResponse, error)
+	SetDenomMetadata(ctx context.Context, in *MsgSetDenomMetadata, opts ...grpc.CallOption) (*MsgSetDenomMetadataResponse, error)
+	ForceTransfer(ctx context.Context, in *MsgForceTransfer, opts ...grpc.CallOption) (*MsgForceTransferResponse, error)
+}
+
+type msgClient struct {
+	cc grpc1.ClientConn
+}
+
+func NewMsgClient(cc grpc1.ClientConn) MsgClient {
+	return &msgClient{cc}
+}
+
+func (c *msgClient) CreateDenom(ctx context.Context, in *MsgCreateDenom, opts ...grpc.CallOption) (*MsgCreateDenomResponse, error) {
+	out := new(MsgCreateDenomResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Msg/CreateDenom", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) Mint(ctx context.Context, in *MsgMint, opts ...grpc.CallOption) (*MsgMintResponse, error) {
+	out := new(MsgMintResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Msg/Mint", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) Burn(ctx context.Context, in *MsgBurn, opts ...grpc.CallOption) (*MsgBurnResponse, error) {
+	out := new(MsgBurnResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Msg/Burn", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) ChangeAdmin(ctx context.Context, in *MsgChangeAdmin, opts ...grpc.CallOption) (*MsgChangeAdminResponse, error) {
+	out := new(MsgChangeAdminResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Msg/ChangeAdmin", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) SetDenomMetadata(ctx context.Context, in *MsgSetDenomMetadata, opts ...grpc.CallOption) (*MsgSetDenomMetadataResponse, error) {
+	out := new(MsgSetDenomMetadataResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Msg/SetDenomMetadata", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *msgClient) ForceTransfer(ctx context.Context, in *MsgForceTransfer, opts ...grpc.CallOption) (*MsgForceTransferResponse, error) {
+	out := new(MsgForceTransferResponse)
+	err := c.cc.Invoke(ctx, "/osmosis.tokenfactory.v1beta1.Msg/ForceTransfer", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// MsgServer is the server API for Msg service.
+type MsgServer interface {
+	CreateDenom(context.Context, *MsgCreateDenom) (*MsgCreateDenomResponse, error)
+	Mint(context.Context, *MsgMint) (*MsgMintResponse, error)
+	Burn(context.Context, *MsgBurn) (*MsgBurnResponse, error)
+	ChangeAdmin(context.Context, *MsgChangeAdmin) (*MsgChangeAdminResponse, error)
+	SetDenomMetadata(context.Context, *MsgSetDenomMetadata) (*MsgSetDenomMetadataResponse, error)
+	ForceTransfer(context.Context, *MsgForceTransfer) (*MsgForceTransferResponse, error)
+}
+
+// UnimplementedMsgServer can be embedded to have forward compatible implementations.
+type UnimplementedMsgServer struct {
+}
+
+func (*UnimplementedMsgServer) CreateDenom(ctx context.Context, req *MsgCreateDenom) (*MsgCreateDenomResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method CreateDenom not implemented")
+}
+func (*UnimplementedMsgServer) Mint(ctx context.Context, req *MsgMint) (*MsgMintResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Mint not implemented")
+}
+func (*UnimplementedMsgServer) Burn(ctx context.Context, req *MsgBurn) (*MsgBurnResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method Burn not implemented")
+}
+func (*UnimplementedMsgServer) ChangeAdmin(ctx context.Context, req *MsgChangeAdmin) (*MsgChangeAdminResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ChangeAdmin not implemented")
+}
+func (*UnimplementedMsgServer) SetDenomMetadata(ctx context.Context, req *MsgSetDenomMetadata) (*MsgSetDenomMetadataResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method SetDenomMetadata not implemented")
+}
+func (*UnimplementedMsgServer) ForceTransfer(ctx context.Context, req *MsgForceTransfer) (*MsgForceTransferResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ForceTransfer not implemented")
+}
+
+func RegisterMsgServer(s grpc1.Server, srv MsgServer) {
+	s.RegisterService(&_Msg_serviceDesc, srv)
+}
+
+func _Msg_CreateDenom_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgCreateDenom)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).CreateDenom(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Msg/CreateDenom",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).CreateDenom(ctx, req.(*MsgCreateDenom))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_Mint_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgMint)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).Mint(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Msg/Mint",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).Mint(ctx, req.(*MsgMint))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_Burn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgBurn)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).Burn(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Msg/Burn",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).Burn(ctx, req.(*MsgBurn))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_ChangeAdmin_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgChangeAdmin)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).ChangeAdmin(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Msg/ChangeAdmin",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).ChangeAdmin(ctx, req.(*MsgChangeAdmin))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_SetDenomMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgSetDenomMetadata)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).SetDenomMetadata(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Msg/SetDenomMetadata",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).SetDenomMetadata(ctx, req.(*MsgSetDenomMetadata))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _Msg_ForceTransfer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(MsgForceTransfer)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(MsgServer).ForceTransfer(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/osmosis.tokenfactory.v1beta1.Msg/ForceTransfer",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(MsgServer).ForceTransfer(ctx, req.(*MsgForceTransfer))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _Msg_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "osmosis.tokenfactory.v1beta1.Msg",
+	HandlerType: (*MsgServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "CreateDenom",
+			Handler:    _Msg_CreateDenom_Handler,
+		},
+		{
+			MethodName: "Mint",
+			Handler:    _Msg_Mint_Handler,
+		},
+		{
+			MethodName: "Burn",
+			Handler:    _Msg_Burn_Handler,
+		},
+		{
+			MethodName: "ChangeAdmin",
+			Handler:    _Msg_ChangeAdmin_Handler,
+		},
+		{
+			MethodName: "SetDenomMetadata",
+			Handler:    _Msg_SetDenomMetadata_Handler,
+		},
+		{
+			MethodName: "ForceTransfer",
+			Handler:    _Msg_ForceTransfer_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "osmosis/tokenfactory/v1beta1/tx.proto",
+}
+
+func (m *MsgCreateDenom) 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 *MsgCreateDenom) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCreateDenom) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Subdenom) > 0 {
+		i -= len(m.Subdenom)
+		copy(dAtA[i:], m.Subdenom)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Subdenom)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Sender) > 0 {
+		i -= len(m.Sender)
+		copy(dAtA[i:], m.Sender)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Sender)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgCreateDenomResponse) 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 *MsgCreateDenomResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgCreateDenomResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.NewTokenDenom) > 0 {
+		i -= len(m.NewTokenDenom)
+		copy(dAtA[i:], m.NewTokenDenom)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.NewTokenDenom)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgMint) 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 *MsgMint) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgMint) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.MintToAddress) > 0 {
+		i -= len(m.MintToAddress)
+		copy(dAtA[i:], m.MintToAddress)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.MintToAddress)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	{
+		size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if len(m.Sender) > 0 {
+		i -= len(m.Sender)
+		copy(dAtA[i:], m.Sender)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Sender)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgMintResponse) 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 *MsgMintResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgMintResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgBurn) 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 *MsgBurn) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgBurn) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.BurnFromAddress) > 0 {
+		i -= len(m.BurnFromAddress)
+		copy(dAtA[i:], m.BurnFromAddress)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.BurnFromAddress)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	{
+		size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if len(m.Sender) > 0 {
+		i -= len(m.Sender)
+		copy(dAtA[i:], m.Sender)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Sender)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgBurnResponse) 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 *MsgBurnResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgBurnResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgChangeAdmin) 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 *MsgChangeAdmin) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgChangeAdmin) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.NewAdmin) > 0 {
+		i -= len(m.NewAdmin)
+		copy(dAtA[i:], m.NewAdmin)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.NewAdmin)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	if len(m.Denom) > 0 {
+		i -= len(m.Denom)
+		copy(dAtA[i:], m.Denom)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Denom)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Sender) > 0 {
+		i -= len(m.Sender)
+		copy(dAtA[i:], m.Sender)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Sender)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgChangeAdminResponse) 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 *MsgChangeAdminResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgChangeAdminResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgSetDenomMetadata) 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 *MsgSetDenomMetadata) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSetDenomMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	{
+		size, err := m.Metadata.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if len(m.Sender) > 0 {
+		i -= len(m.Sender)
+		copy(dAtA[i:], m.Sender)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Sender)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgSetDenomMetadataResponse) 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 *MsgSetDenomMetadataResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgSetDenomMetadataResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgForceTransfer) 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 *MsgForceTransfer) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgForceTransfer) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.TransferToAddress) > 0 {
+		i -= len(m.TransferToAddress)
+		copy(dAtA[i:], m.TransferToAddress)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.TransferToAddress)))
+		i--
+		dAtA[i] = 0x22
+	}
+	if len(m.TransferFromAddress) > 0 {
+		i -= len(m.TransferFromAddress)
+		copy(dAtA[i:], m.TransferFromAddress)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.TransferFromAddress)))
+		i--
+		dAtA[i] = 0x1a
+	}
+	{
+		size, err := m.Amount.MarshalToSizedBuffer(dAtA[:i])
+		if err != nil {
+			return 0, err
+		}
+		i -= size
+		i = encodeVarintTx(dAtA, i, uint64(size))
+	}
+	i--
+	dAtA[i] = 0x12
+	if len(m.Sender) > 0 {
+		i -= len(m.Sender)
+		copy(dAtA[i:], m.Sender)
+		i = encodeVarintTx(dAtA, i, uint64(len(m.Sender)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *MsgForceTransferResponse) 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 *MsgForceTransferResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *MsgForceTransferResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintTx(dAtA []byte, offset int, v uint64) int {
+	offset -= sovTx(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *MsgCreateDenom) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Sender)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.Subdenom)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgCreateDenomResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.NewTokenDenom)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgMint) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Sender)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = m.Amount.Size()
+	n += 1 + l + sovTx(uint64(l))
+	l = len(m.MintToAddress)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgMintResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgBurn) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Sender)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = m.Amount.Size()
+	n += 1 + l + sovTx(uint64(l))
+	l = len(m.BurnFromAddress)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgBurnResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgChangeAdmin) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Sender)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.Denom)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.NewAdmin)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgChangeAdminResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgSetDenomMetadata) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Sender)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = m.Metadata.Size()
+	n += 1 + l + sovTx(uint64(l))
+	return n
+}
+
+func (m *MsgSetDenomMetadataResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func (m *MsgForceTransfer) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Sender)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = m.Amount.Size()
+	n += 1 + l + sovTx(uint64(l))
+	l = len(m.TransferFromAddress)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	l = len(m.TransferToAddress)
+	if l > 0 {
+		n += 1 + l + sovTx(uint64(l))
+	}
+	return n
+}
+
+func (m *MsgForceTransferResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	return n
+}
+
+func sovTx(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozTx(x uint64) (n int) {
+	return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *MsgCreateDenom) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCreateDenom: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCreateDenom: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Sender = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Subdenom", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Subdenom = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgCreateDenomResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgCreateDenomResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgCreateDenomResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field NewTokenDenom", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.NewTokenDenom = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgMint) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgMint: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgMint: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Sender = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field MintToAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.MintToAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgMintResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgMintResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgMintResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgBurn) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgBurn: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgBurn: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Sender = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field BurnFromAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.BurnFromAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgBurnResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgBurnResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgBurnResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgChangeAdmin) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgChangeAdmin: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgChangeAdmin: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Sender = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Denom", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Denom = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field NewAdmin", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.NewAdmin = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgChangeAdminResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgChangeAdminResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgChangeAdminResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgSetDenomMetadata) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgSetDenomMetadata: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgSetDenomMetadata: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Sender = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Metadata.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgSetDenomMetadataResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgSetDenomMetadataResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgSetDenomMetadataResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgForceTransfer) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgForceTransfer: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgForceTransfer: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Sender = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if err := m.Amount.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TransferFromAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.TransferFromAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field TransferToAddress", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthTx
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthTx
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.TransferToAddress = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *MsgForceTransferResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: MsgForceTransferResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: MsgForceTransferResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		default:
+			iNdEx = preIndex
+			skippy, err := skipTx(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthTx
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipTx(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowTx
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowTx
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthTx
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupTx
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthTx
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthTx        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowTx          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group")
+)