diff --git a/app/custom_queriers/custom_queriers.go b/app/custom_queriers/custom_queriers.go index 5318a798..b5b9e547 100644 --- a/app/custom_queriers/custom_queriers.go +++ b/app/custom_queriers/custom_queriers.go @@ -3,14 +3,16 @@ package custom_queriers import ( "encoding/json" "fmt" + "strings" + "github.com/CosmWasm/wasmd/x/wasm" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" alliancebindings "github.com/terra-money/alliance/x/alliance/bindings" alliancekeeper "github.com/terra-money/alliance/x/alliance/keeper" tokenfactorybindings "github.com/terra-money/core/v2/x/tokenfactory/bindings" tokenfactorykeeper "github.com/terra-money/core/v2/x/tokenfactory/keeper" - "strings" + + bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" sdk "github.com/cosmos/cosmos-sdk/types" ) diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 37cf352a..8d529222 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -306,6 +306,7 @@ func NewTerraAppKeepers( ) keepers.TokenFactoryKeeper = tokenfactorykeeper.NewKeeper( keys[tokenfactorytypes.StoreKey], + maccPerms, keepers.AccountKeeper, &keepers.BankKeeper, keepers.DistrKeeper, diff --git a/x/tokenfactory/bindings/wasm.go b/x/tokenfactory/bindings/wasm.go index e0dd9d4b..95f91744 100644 --- a/x/tokenfactory/bindings/wasm.go +++ b/x/tokenfactory/bindings/wasm.go @@ -1,2 +1 @@ package bindings - diff --git a/x/tokenfactory/keeper/bankactions.go b/x/tokenfactory/keeper/bankactions.go index d1f28fbe..88f87e24 100644 --- a/x/tokenfactory/keeper/bankactions.go +++ b/x/tokenfactory/keeper/bankactions.go @@ -1,6 +1,11 @@ package keeper import ( + "sort" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + sdk "github.com/cosmos/cosmos-sdk/types" errorsmod "cosmossdk.io/errors" @@ -71,6 +76,28 @@ func (k Keeper) forceTransfer(ctx sdk.Context, amount sdk.Coin, fromAddr string, return err } + fromAcc, err := sdk.AccAddressFromBech32(fromAddr) + if err != nil { + return err + } + + sortedPermAddrs := make([]string, 0, len(k.permAddrs)) + for moduleName := range k.permAddrs { + sortedPermAddrs = append(sortedPermAddrs, moduleName) + } + sort.Strings(sortedPermAddrs) + + for _, moduleName := range sortedPermAddrs { + account := k.accountKeeper.GetModuleAccount(ctx, moduleName) + if account == nil { + return status.Errorf(codes.NotFound, "account %s not found", moduleName) + } + + if account.GetAddress().Equals(fromAcc) { + return status.Errorf(codes.Internal, "send from module acc not available") + } + } + fromSdkAddr, err := sdk.AccAddressFromBech32(fromAddr) if err != nil { return err diff --git a/x/tokenfactory/keeper/keeper.go b/x/tokenfactory/keeper/keeper.go index 9727c192..8660eb11 100644 --- a/x/tokenfactory/keeper/keeper.go +++ b/x/tokenfactory/keeper/keeper.go @@ -17,7 +17,8 @@ import ( type ( Keeper struct { - storeKey storetypes.StoreKey + storeKey storetypes.StoreKey + permAddrs map[string][]string accountKeeper types.AccountKeeper bankKeeper *customtypes.Keeper @@ -33,6 +34,7 @@ type ( // NewKeeper returns a new instance of the x/tokenfactory keeper func NewKeeper( storeKey storetypes.StoreKey, + permAddrs map[string][]string, accountKeeper types.AccountKeeper, bankKeeper *customtypes.Keeper, communityPoolKeeper types.CommunityPoolKeeper, @@ -41,7 +43,8 @@ func NewKeeper( ) Keeper { return Keeper{ - storeKey: storeKey, + storeKey: storeKey, + permAddrs: permAddrs, accountKeeper: accountKeeper, bankKeeper: bankKeeper, diff --git a/x/tokenfactory/keeper/msg_server_test.go b/x/tokenfactory/keeper/msg_server_test.go index bd6ba3f8..d89460d0 100644 --- a/x/tokenfactory/keeper/msg_server_test.go +++ b/x/tokenfactory/keeper/msg_server_test.go @@ -4,6 +4,8 @@ import ( "fmt" "testing" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/stretchr/testify/suite" "github.com/terra-money/core/v2/x/tokenfactory/types" @@ -187,6 +189,26 @@ func (s *KeeperTestSuite) TestBurnDenomMsg() { } } +func (s *KeeperTestSuite) TestForceTransferMsgFromModuleAcc() { + // Create a denom + res, _ := s.msgServer.CreateDenom(sdk.WrapSDKContext(s.Ctx), types.NewMsgCreateDenom(s.TestAccs[0].String(), "bitcoin")) + defaultDenom := res.GetNewTokenDenom() + + s.Run(fmt.Sprintf("test force transfer"), func() { + mintAmt := sdk.NewInt64Coin(defaultDenom, 10) + + _, err := s.msgServer.Mint(sdk.WrapSDKContext(s.Ctx), types.NewMsgMint(s.TestAccs[0].String(), mintAmt)) + + govModAcc := s.App.Keepers.AccountKeeper.GetModuleAccount(s.Ctx, govtypes.ModuleName) + + err = s.App.Keepers.BankKeeper.SendCoins(s.Ctx, s.TestAccs[0], govModAcc.GetAddress(), sdk.NewCoins(mintAmt)) + s.Require().NoError(err) + + _, err = s.msgServer.ForceTransfer(s.Ctx, types.NewMsgForceTransfer(s.TestAccs[0].String(), mintAmt, govModAcc.GetAddress().String(), s.TestAccs[1].String())) + s.Require().ErrorContains(err, "send from module acc not available") + }) +} + // TestCreateDenomMsg tests TypeMsgCreateDenom message is emitted on a successful denom creation func (s *KeeperTestSuite) TestCreateDenomMsg() { for _, tc := range []struct {