func getInputMap(inputs []banktypes.Input) map[string][]string {
	inputMap := make(map[string][]string)
	for _, input := range inputs {
		for _, coin := range input.Coins {
			inputMap[coin.Denom] = append(inputMap[coin.Denom], input.Address)
		}
	}
	return inputMap
}
getInputMap maps input denoms to addresses
package keeper
import (
	"github.com/CosmWasm/wasmd/x/wasm"
	sdk "github.com/cosmos/cosmos-sdk/types"
	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
	"github.com/bianjieai/irita/modules/opb/types"
)
type ValidateTokenTransferDecorator struct {
	keeper      Keeper
	tokenKeeper types.TokenKeeper
}
func NewValidateTokenTransferDecorator(
	keeper Keeper,
	tokenKeeper types.TokenKeeper,
) ValidateTokenTransferDecorator {
	return ValidateTokenTransferDecorator{
		keeper:      keeper,
		tokenKeeper: tokenKeeper,
	}
}
func (vtd ValidateTokenTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) {
	restrictionEnabled := !vtd.keeper.UnrestrictedTokenTransfer(ctx)
	if restrictionEnabled {
		for _, msg := range tx.GetMsgs() {
			switch msg := msg.(type) {
			case *banktypes.MsgSend:
				err :=vtd.validateMsgSend(ctx, msg)
				if err != nil {
					return ctx, err
				}
			case *banktypes.MsgMultiSend:
				err := vtd.validateMsgMultiSend(ctx, msg)
				if err != nil {
					return ctx, err
				}
			case *wasm.MsgInstantiateContract:
				err := vtd.validateMsgInstantiateContract(ctx, msg)
				if err != nil {
					return ctx, err
				}
			case *wasm.MsgExecuteContract:
				err := vtd.validateMsgExecuteContract(ctx, msg)
				if err != nil {
					return ctx, err
				}
			}
		}
	}
	return next(ctx, tx, simulate)
}
func (vtd ValidateTokenTransferDecorator) validateMsgSend(ctx sdk.Context, msg *banktypes.MsgSend) error {
	for _, coin := range msg.Amount {
		owner, err := vtd.getOwner(ctx, coin.Denom)
		if err != nil {
			continue
		}
		if msg.FromAddress != owner && msg.ToAddress != owner {
			return sdkerrors.Wrapf(
				types.ErrUnauthorized,
				"either the sender or recipient must be the owner %s for token %s",
				owner, coin.Denom,
			)
		}
	}
	return nil
}
func (vtd ValidateTokenTransferDecorator) validateMsgMultiSend(ctx sdk.Context, msg *banktypes.MsgMultiSend) error {
	inputMap := getInputMap(msg.Inputs)
	outputMap := getOutputMap(msg.Outputs)
	for denom, addresses := range inputMap {
		owner, err := vtd.getOwner(ctx, denom)
		if err != nil {
			continue
		}
		if !owned(owner, addresses) && !owned(owner, outputMap[denom]) {
			return sdkerrors.Wrapf(
				types.ErrUnauthorized,
				"either the sender or recipient must be the owner %s for token %s",
				owner, denom,
			)
		}
	}
	return nil
}
func (vtd ValidateTokenTransferDecorator) validateMsgInstantiateContract(ctx sdk.Context, msg *wasm.MsgInstantiateContract) error {
	return vtd.validateContractFunds(ctx, msg.Funds)
}
func (vtd ValidateTokenTransferDecorator) validateMsgExecuteContract(ctx sdk.Context, msg *wasm.MsgExecuteContract) error {
	return vtd.validateContractFunds(ctx, msg.Funds)
}
func (vtd ValidateTokenTransferDecorator) getOwner(ctx sdk.Context, denom string) (owner string, err error) {
	baseTokenDenom := vtd.keeper.BaseTokenDenom(ctx)
	if denom == baseTokenDenom {
		owner = vtd.keeper.BaseTokenManager(ctx)
	} else {
		ownerAddr, err := vtd.tokenKeeper.GetOwner(ctx, denom)
		if err == nil {
			owner = ownerAddr.String()
		}
	}
	return
}
func (vtd ValidateTokenTransferDecorator) validateContractFunds(ctx sdk.Context, coins sdk.Coins) error {
	baseTokenDenom := vtd.keeper.BaseTokenDenom(ctx)
	for _, coin := range coins {
		if coin.Denom == baseTokenDenom {
			return sdkerrors.Wrapf(
				types.ErrUnauthorized,
				"%s not allowed for contract transactions",
				coin.Denom,
			)
		}
	}
	return nil
}
func owned(owner string, addresses []string) bool {
	for _, addr := range addresses {
		if addr != owner {
			return false
		}
	}
	return true
}