diff --git a/proto/tokenfactory/tx.proto b/proto/tokenfactory/tx.proto index 17d0a2495..7c0402b9f 100644 --- a/proto/tokenfactory/tx.proto +++ b/proto/tokenfactory/tx.proto @@ -98,4 +98,4 @@ message MsgUpdateDenom { // MsgUpdateDenomResponse defines the response structure for an executed MsgUpdateDenom message. -message MsgUpdateDenomResponse {} \ No newline at end of file +message MsgUpdateDenomResponse {} diff --git a/x/tokenfactory/client/cli/tx.go b/x/tokenfactory/client/cli/tx.go index a6d837b13..9807749ad 100644 --- a/x/tokenfactory/client/cli/tx.go +++ b/x/tokenfactory/client/cli/tx.go @@ -23,7 +23,10 @@ import ( ) const ( - FlagAllowList = "allow-list" + FlagAllowList = "allow-list" + FlagAllowListDescription = "Path to the allow list JSON file with an array of addresses " + + "that are allowed to send/receive the token. The file should have the following format: {\"addresses\": " + + "[\"addr1\", \"addr2\"]}, where addr1 and addr2 are bech32 Sei native addresses or EVM addresses." ) // GetTxCmd returns the transaction commands for this module @@ -92,9 +95,7 @@ func NewCreateDenomCmd() *cobra.Command { } flags.AddTxFlagsToCmd(cmd) - cmd.Flags().String(FlagAllowList, "", "Path to the allow list JSON file with an array of addresses "+ - "that are allowed to send/receive the token. The file should have the following format: {\"addresses\": "+ - "[\"addr1\", \"addr2\"]}, where addr1 and addr2 are bech32 Sei native addresses.") + cmd.Flags().String(FlagAllowList, "", FlagAllowListDescription) return cmd } @@ -122,11 +123,11 @@ func NewUpdateDenomCmd() *cobra.Command { txf := tx.NewFactoryCLI(clientCtx, cmd.Flags()).WithTxConfig(clientCtx.TxConfig).WithAccountRetriever(clientCtx.AccountRetriever) - // only parse allow list if it is provided + // Fail if allow list is not provided as this is the only feature that can be updated for now if allowListFilePath == "" { return fmt.Errorf("allow list file path is required") } - // Parse the allow list + allowList, err := ParseAllowListJSON(allowListFilePath, queryClient) if err != nil { return err @@ -143,9 +144,7 @@ func NewUpdateDenomCmd() *cobra.Command { } flags.AddTxFlagsToCmd(cmd) - cmd.Flags().String(FlagAllowList, "", "Path to the allow list JSON file with an array of addresses "+ - "that are allowed to send/receive the token. The file should have the following format: {\"addresses\": "+ - "[\"addr1\", \"addr2\"]}, where addr1 and addr2 are bech32 Sei native addresses.") + cmd.Flags().String(FlagAllowList, "", FlagAllowListDescription) return cmd } diff --git a/x/tokenfactory/keeper/createdenom.go b/x/tokenfactory/keeper/createdenom.go index 4f96f23ce..3647d5f3b 100644 --- a/x/tokenfactory/keeper/createdenom.go +++ b/x/tokenfactory/keeper/createdenom.go @@ -80,7 +80,7 @@ func (k Keeper) validateUpdateDenom(ctx sdk.Context, msg *types.MsgUpdateDenom) return "", types.ErrDenomDoesNotExist.Wrapf("denom: %s", denom) } - err = k.validateAllowListSize(*msg.AllowList) + err = k.validateAllowList(msg.AllowList) if err != nil { return "", err } @@ -88,9 +88,24 @@ func (k Keeper) validateUpdateDenom(ctx sdk.Context, msg *types.MsgUpdateDenom) return denom, nil } -func (k Keeper) validateAllowListSize(allowList banktypes.AllowList) error { +func (k Keeper) validateAllowListSize(allowList *banktypes.AllowList) error { if len(allowList.Addresses) > k.config.DenomAllowListMaxSize { return types.ErrAllowListTooLarge } return nil } + +func (k Keeper) validateAllowList(allowList *banktypes.AllowList) error { + err := k.validateAllowListSize(allowList) + if err != nil { + return err + } + + // validate all addresses in the allow list are bech32 + for _, addr := range allowList.Addresses { + if _, err = sdk.AccAddressFromBech32(addr); err != nil { + return fmt.Errorf("invalid address %s: %w", addr, err) + } + } + return nil +} diff --git a/x/tokenfactory/keeper/createdenom_test.go b/x/tokenfactory/keeper/createdenom_test.go index 21d5b4372..29990cee5 100644 --- a/x/tokenfactory/keeper/createdenom_test.go +++ b/x/tokenfactory/keeper/createdenom_test.go @@ -2,6 +2,7 @@ package keeper_test import ( "fmt" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -51,10 +52,11 @@ func (suite *KeeperTestSuite) TestMsgCreateDenom() { func (suite *KeeperTestSuite) TestCreateDenom() { for _, tc := range []struct { - desc string - setup func() - subdenom string - valid bool + desc string + setup func() + subdenom string + allowList *banktypes.AllowList + valid bool }{ { desc: "subdenom too long", @@ -80,13 +82,35 @@ func (suite *KeeperTestSuite) TestCreateDenom() { subdenom: "bit/***///&&&/coin", valid: false, }, + { + desc: "valid allow list", + subdenom: "withallowlist", + allowList: &banktypes.AllowList{ + Addresses: []string{suite.TestAccs[0].String(), suite.TestAccs[1].String()}, + }, + valid: true, + }, + { + desc: "invalid allow list with invalid address", + subdenom: "invalidallowlist", + allowList: &banktypes.AllowList{ + Addresses: []string{"invalid_address"}, + }, + valid: false, + }, } { suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { if tc.setup != nil { tc.setup() } + + msg := types.NewMsgCreateDenom(suite.TestAccs[0].String(), tc.subdenom) + if tc.allowList != nil { + msg.AllowList = tc.allowList + } + // Create a denom - res, err := suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), tc.subdenom)) + res, err := suite.msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), msg) if tc.valid { suite.Require().NoError(err) @@ -97,6 +121,7 @@ func (suite *KeeperTestSuite) TestCreateDenom() { suite.Require().NoError(err) suite.Require().Equal(suite.TestAccs[0].String(), queryRes.AuthorityMetadata.Admin) + // Make sure that the denom is valid from the perspective of x/bank bankQueryRes, err := suite.bankQueryClient.DenomMetadata(suite.Ctx.Context(), &banktypes.QueryDenomMetadataRequest{ Denom: res.GetNewTokenDenom(), @@ -104,6 +129,15 @@ func (suite *KeeperTestSuite) TestCreateDenom() { suite.Require().NoError(err) suite.Require().NoError(bankQueryRes.Metadata.Validate()) + + // Verify the allow list if provided + if tc.allowList != nil { + allowListRes, err := suite.queryClient.DenomAllowList(suite.Ctx.Context(), &types.QueryDenomAllowListRequest{ + Denom: res.GetNewTokenDenom(), + }) + suite.Require().NoError(err) + suite.Require().Equal(tc.allowList, allowListRes.AllowList) + } } else { suite.Require().Error(err) } diff --git a/x/tokenfactory/keeper/msg_server.go b/x/tokenfactory/keeper/msg_server.go index d8c9244fe..e9b347ef1 100644 --- a/x/tokenfactory/keeper/msg_server.go +++ b/x/tokenfactory/keeper/msg_server.go @@ -35,7 +35,7 @@ func (server msgServer) CreateDenom(goCtx context.Context, msg *types.MsgCreateD ) if msg.AllowList != nil { - err = server.validateAllowListSize(*msg.AllowList) + err = server.validateAllowList(msg.AllowList) if err != nil { return nil, err }