github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/keeper/query_plugins.go (about)

     1  package keeper
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  
     7  	"github.com/fibonacci-chain/fbc/libs/cosmos-sdk/baseapp"
     8  
     9  	channeltypes "github.com/fibonacci-chain/fbc/libs/ibc-go/modules/core/04-channel/types"
    10  
    11  	"github.com/fibonacci-chain/fbc/x/wasm/types"
    12  
    13  	wasmvmtypes "github.com/CosmWasm/wasmvm/types"
    14  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    15  	sdkerrors "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types/errors"
    16  	distributiontypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/distribution/types"
    17  	stakingtypes "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/x/staking/types"
    18  )
    19  
    20  type QueryHandler struct {
    21  	Ctx         sdk.Context
    22  	Plugins     WasmVMQueryHandler
    23  	Caller      sdk.AccAddress
    24  	gasRegister GasRegister
    25  }
    26  
    27  func NewQueryHandler(ctx sdk.Context, vmQueryHandler WasmVMQueryHandler, caller sdk.AccAddress, gasRegister GasRegister) QueryHandler {
    28  	return QueryHandler{
    29  		Ctx:         ctx,
    30  		Plugins:     vmQueryHandler,
    31  		Caller:      caller,
    32  		gasRegister: gasRegister,
    33  	}
    34  }
    35  
    36  type GRPCQueryRouter interface {
    37  	Route(path string) baseapp.GRPCQueryHandler
    38  }
    39  
    40  // -- end baseapp interfaces --
    41  
    42  var _ wasmvmtypes.Querier = QueryHandler{}
    43  
    44  func (q QueryHandler) Query(request wasmvmtypes.QueryRequest, gasLimit uint64) ([]byte, error) {
    45  	// set a limit for a subCtx
    46  	sdkGas := q.gasRegister.FromWasmVMGas(gasLimit)
    47  	// discard all changes/ events in subCtx by not committing the cached context
    48  	subCtx, _ := q.Ctx.CacheContext()
    49  	subCtx.SetGasMeter(sdk.NewGasMeter(sdkGas))
    50  
    51  	// make sure we charge the higher level context even on panic
    52  	defer func() {
    53  		q.Ctx.GasMeter().ConsumeGas(subCtx.GasMeter().GasConsumed(), "contract sub-query")
    54  	}()
    55  
    56  	res, err := q.Plugins.HandleQuery(subCtx, q.Caller, request)
    57  	if err == nil {
    58  		// short-circuit, the rest is dealing with handling existing errors
    59  		return res, nil
    60  	}
    61  
    62  	// special mappings to system error (which are not redacted)
    63  	var noSuchContract *types.ErrNoSuchContract
    64  	if ok := errors.As(err, &noSuchContract); ok {
    65  		err = wasmvmtypes.NoSuchContract{Addr: noSuchContract.Addr}
    66  	}
    67  
    68  	// Issue #759 - we don't return error string for worries of non-determinism
    69  	return nil, redactError(err)
    70  }
    71  
    72  func (q QueryHandler) GasConsumed() uint64 {
    73  	return q.Ctx.GasMeter().GasConsumed()
    74  }
    75  
    76  type CustomQuerier func(ctx sdk.Context, request json.RawMessage) ([]byte, error)
    77  
    78  type QueryPlugins struct {
    79  	Bank   func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error)
    80  	Custom CustomQuerier
    81  	//IBC      func(ctx sdk.Context, caller sdk.AccAddress, request *wasmvmtypes.IBCQuery) ([]byte, error)
    82  	//Staking  func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error)
    83  	Stargate func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error)
    84  	Wasm     func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error)
    85  }
    86  
    87  type contractMetaDataSource interface {
    88  	GetContractInfo(ctx sdk.Context, contractAddress sdk.AccAddress) *types.ContractInfo
    89  }
    90  
    91  type wasmQueryKeeper interface {
    92  	contractMetaDataSource
    93  	QueryRaw(ctx sdk.Context, contractAddress sdk.AccAddress, key []byte) []byte
    94  	QuerySmart(ctx sdk.Context, contractAddr sdk.AccAddress, req []byte) ([]byte, error)
    95  	IsPinnedCode(ctx sdk.Context, codeID uint64) bool
    96  }
    97  
    98  func DefaultQueryPlugins(
    99  	bank types.BankViewKeeper,
   100  	//staking types.StakingKeeper,
   101  	//distKeeper types.DistributionKeeper,
   102  	channelKeeper types.ChannelKeeper,
   103  	queryRouter GRPCQueryRouter,
   104  	wasm wasmQueryKeeper,
   105  ) QueryPlugins {
   106  	return QueryPlugins{
   107  		Bank:   BankQuerier(bank),
   108  		Custom: NoCustomQuerier,
   109  		//IBC:    IBCQuerier(wasm, channelKeeper),
   110  		//Staking:  StakingQuerier(staking, distKeeper),
   111  		Stargate: StargateQuerier(queryRouter),
   112  		Wasm:     WasmQuerier(wasm),
   113  	}
   114  }
   115  
   116  func (e QueryPlugins) Merge(o *QueryPlugins) QueryPlugins {
   117  	// only update if this is non-nil and then only set values
   118  	if o == nil {
   119  		return e
   120  	}
   121  	if o.Bank != nil {
   122  		e.Bank = o.Bank
   123  	}
   124  	if o.Custom != nil {
   125  		e.Custom = o.Custom
   126  	}
   127  	//if o.IBC != nil {
   128  	//	e.IBC = o.IBC
   129  	//}
   130  	//if o.Staking != nil {
   131  	//	e.Staking = o.Staking
   132  	//}
   133  	if o.Stargate != nil {
   134  		e.Stargate = o.Stargate
   135  	}
   136  	if o.Wasm != nil {
   137  		e.Wasm = o.Wasm
   138  	}
   139  	return e
   140  }
   141  
   142  // HandleQuery executes the requested query
   143  func (e QueryPlugins) HandleQuery(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) {
   144  	// do the query
   145  	if request.Bank != nil {
   146  		return e.Bank(ctx, request.Bank)
   147  	}
   148  	if request.Custom != nil {
   149  		return e.Custom(ctx, request.Custom)
   150  	}
   151  	//if request.IBC != nil {
   152  	//	return e.IBC(ctx, caller, request.IBC)
   153  	//}
   154  	//if request.Staking != nil {
   155  	//	return e.Staking(ctx, request.Staking)
   156  	//}
   157  	if request.Stargate != nil {
   158  		return e.Stargate(ctx, request.Stargate)
   159  	}
   160  	if request.Wasm != nil {
   161  		return e.Wasm(ctx, request.Wasm)
   162  	}
   163  	return nil, wasmvmtypes.Unknown{}
   164  }
   165  
   166  func BankQuerier(bankKeeper types.BankViewKeeper) func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error) {
   167  	return func(ctx sdk.Context, request *wasmvmtypes.BankQuery) ([]byte, error) {
   168  		if request.AllBalances != nil {
   169  			addr, err := sdk.AccAddressFromBech32(request.AllBalances.Address)
   170  			if err != nil {
   171  				return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.AllBalances.Address)
   172  			}
   173  			coins := bankKeeper.GetAllBalances(ctx, addr)
   174  			adapters := sdk.CoinsToCoinAdapters(coins)
   175  			res := wasmvmtypes.AllBalancesResponse{
   176  				Amount: ConvertSdkCoinsToWasmCoins(adapters),
   177  			}
   178  			return json.Marshal(res)
   179  		}
   180  		if request.Balance != nil {
   181  			addr, err := sdk.AccAddressFromBech32(request.Balance.Address)
   182  			if err != nil {
   183  				return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Balance.Address)
   184  			}
   185  			coin := bankKeeper.GetBalance(ctx, addr, request.Balance.Denom)
   186  			adapter := sdk.CoinToCoinAdapter(coin)
   187  			res := wasmvmtypes.BalanceResponse{
   188  				Amount: ConvertSdkCoinToWasmCoin(adapter),
   189  			}
   190  			return json.Marshal(res)
   191  		}
   192  		return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown BankQuery variant"}
   193  	}
   194  }
   195  
   196  func NoCustomQuerier(sdk.Context, json.RawMessage) ([]byte, error) {
   197  	return nil, wasmvmtypes.UnsupportedRequest{Kind: "custom"}
   198  }
   199  
   200  func IBCQuerier(wasm contractMetaDataSource, channelKeeper types.ChannelKeeper) func(ctx sdk.Context, caller sdk.AccAddress, request *wasmvmtypes.IBCQuery) ([]byte, error) {
   201  	return func(ctx sdk.Context, caller sdk.AccAddress, request *wasmvmtypes.IBCQuery) ([]byte, error) {
   202  		if request.PortID != nil {
   203  			contractInfo := wasm.GetContractInfo(ctx, caller)
   204  			res := wasmvmtypes.PortIDResponse{
   205  				PortID: contractInfo.IBCPortID,
   206  			}
   207  			return json.Marshal(res)
   208  		}
   209  		if request.ListChannels != nil {
   210  			portID := request.ListChannels.PortID
   211  			channels := make(wasmvmtypes.IBCChannels, 0)
   212  			channelKeeper.IterateChannels(ctx, func(ch channeltypes.IdentifiedChannel) bool {
   213  				// it must match the port and be in open state
   214  				if (portID == "" || portID == ch.PortId) && ch.State == channeltypes.OPEN {
   215  					newChan := wasmvmtypes.IBCChannel{
   216  						Endpoint: wasmvmtypes.IBCEndpoint{
   217  							PortID:    ch.PortId,
   218  							ChannelID: ch.ChannelId,
   219  						},
   220  						CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{
   221  							PortID:    ch.Counterparty.PortId,
   222  							ChannelID: ch.Counterparty.ChannelId,
   223  						},
   224  						Order:        ch.Ordering.String(),
   225  						Version:      ch.Version,
   226  						ConnectionID: ch.ConnectionHops[0],
   227  					}
   228  					channels = append(channels, newChan)
   229  				}
   230  				return false
   231  			})
   232  			res := wasmvmtypes.ListChannelsResponse{
   233  				Channels: channels,
   234  			}
   235  			return json.Marshal(res)
   236  		}
   237  		if request.Channel != nil {
   238  			channelID := request.Channel.ChannelID
   239  			portID := request.Channel.PortID
   240  			if portID == "" {
   241  				contractInfo := wasm.GetContractInfo(ctx, caller)
   242  				portID = contractInfo.IBCPortID
   243  			}
   244  			got, found := channelKeeper.GetChannel(ctx, portID, channelID)
   245  			var channel *wasmvmtypes.IBCChannel
   246  			// it must be in open state
   247  			if found && got.State == channeltypes.OPEN {
   248  				channel = &wasmvmtypes.IBCChannel{
   249  					Endpoint: wasmvmtypes.IBCEndpoint{
   250  						PortID:    portID,
   251  						ChannelID: channelID,
   252  					},
   253  					CounterpartyEndpoint: wasmvmtypes.IBCEndpoint{
   254  						PortID:    got.Counterparty.PortId,
   255  						ChannelID: got.Counterparty.ChannelId,
   256  					},
   257  					Order:        got.Ordering.String(),
   258  					Version:      got.Version,
   259  					ConnectionID: got.ConnectionHops[0],
   260  				}
   261  			}
   262  			res := wasmvmtypes.ChannelResponse{
   263  				Channel: channel,
   264  			}
   265  			return json.Marshal(res)
   266  		}
   267  		return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown IBCQuery variant"}
   268  	}
   269  }
   270  
   271  func StargateQuerier(queryRouter GRPCQueryRouter) func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
   272  	return func(ctx sdk.Context, msg *wasmvmtypes.StargateQuery) ([]byte, error) {
   273  		return nil, wasmvmtypes.UnsupportedRequest{Kind: "Stargate queries are disabled."}
   274  	}
   275  }
   276  
   277  //var queryDenyList = []string{
   278  //	"/cosmos.tx.",
   279  //	"/cosmos.base.tendermint.",
   280  //}
   281  //
   282  //func StargateQuerier(queryRouter GRPCQueryRouter) func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
   283  //	return func(ctx sdk.Context, msg *wasmvmtypes.StargateQuery) ([]byte, error) {
   284  //		for _, b := range queryDenyList {
   285  //			if strings.HasPrefix(msg.Path, b) {
   286  //				return nil, wasmvmtypes.UnsupportedRequest{Kind: "path is not allowed from the contract"}
   287  //			}
   288  //		}
   289  //
   290  //		route := queryRouter.Route(msg.Path)
   291  //		if route == nil {
   292  //			return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("No route to query '%s'", msg.Path)}
   293  //		}
   294  //		req := abci.RequestQuery{
   295  //			Data: msg.Data,
   296  //			Path: msg.Path,
   297  //		}
   298  //		res, err := route(ctx, req)
   299  //		if err != nil {
   300  //			return nil, err
   301  //		}
   302  //		return res.Value, nil
   303  //	}
   304  //}
   305  
   306  func StakingQuerier(keeper types.StakingKeeper, distKeeper types.DistributionKeeper) func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) {
   307  	return func(ctx sdk.Context, request *wasmvmtypes.StakingQuery) ([]byte, error) {
   308  		if request.BondedDenom != nil {
   309  			denom := keeper.BondDenom(ctx)
   310  			res := wasmvmtypes.BondedDenomResponse{
   311  				Denom: denom,
   312  			}
   313  			return json.Marshal(res)
   314  		}
   315  		if request.AllValidators != nil {
   316  			validators := keeper.GetBondedValidatorsByPower(ctx)
   317  			// validators := keeper.GetAllValidators(ctx)
   318  			wasmVals := make([]wasmvmtypes.Validator, len(validators))
   319  			for i, v := range validators {
   320  				wasmVals[i] = wasmvmtypes.Validator{
   321  					Address:       v.OperatorAddress.String(),
   322  					Commission:    v.Commission.Rate.String(),
   323  					MaxCommission: v.Commission.MaxRate.String(),
   324  					MaxChangeRate: v.Commission.MaxChangeRate.String(),
   325  				}
   326  			}
   327  			res := wasmvmtypes.AllValidatorsResponse{
   328  				Validators: wasmVals,
   329  			}
   330  			return json.Marshal(res)
   331  		}
   332  		if request.Validator != nil {
   333  			valAddr, err := sdk.ValAddressFromBech32(request.Validator.Address)
   334  			if err != nil {
   335  				return nil, err
   336  			}
   337  			v, found := keeper.GetValidator(ctx, valAddr)
   338  			res := wasmvmtypes.ValidatorResponse{}
   339  			if found {
   340  				res.Validator = &wasmvmtypes.Validator{
   341  					Address:       v.OperatorAddress.String(),
   342  					Commission:    v.Commission.Rate.String(),
   343  					MaxCommission: v.Commission.MaxRate.String(),
   344  					MaxChangeRate: v.Commission.MaxChangeRate.String(),
   345  				}
   346  			}
   347  			return json.Marshal(res)
   348  		}
   349  		if request.AllDelegations != nil {
   350  			delegator, err := sdk.AccAddressFromBech32(request.AllDelegations.Delegator)
   351  			if err != nil {
   352  				return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.AllDelegations.Delegator)
   353  			}
   354  			sdkDels := keeper.GetAllDelegatorDelegations(ctx, delegator)
   355  			delegations, err := sdkToDelegations(ctx, keeper, sdkDels)
   356  			if err != nil {
   357  				return nil, err
   358  			}
   359  			res := wasmvmtypes.AllDelegationsResponse{
   360  				Delegations: delegations,
   361  			}
   362  			return json.Marshal(res)
   363  		}
   364  		if request.Delegation != nil {
   365  			delegator, err := sdk.AccAddressFromBech32(request.Delegation.Delegator)
   366  			if err != nil {
   367  				return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Delegation.Delegator)
   368  			}
   369  			validator, err := sdk.ValAddressFromBech32(request.Delegation.Validator)
   370  			if err != nil {
   371  				return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Delegation.Validator)
   372  			}
   373  
   374  			var res wasmvmtypes.DelegationResponse
   375  			d, found := keeper.GetDelegation(ctx, delegator, validator)
   376  			if found {
   377  				res.Delegation, err = sdkToFullDelegation(ctx, keeper, distKeeper, d)
   378  				if err != nil {
   379  					return nil, err
   380  				}
   381  			}
   382  			return json.Marshal(res)
   383  		}
   384  		return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown Staking variant"}
   385  	}
   386  }
   387  
   388  func sdkToDelegations(ctx sdk.Context, keeper types.StakingKeeper, delegations []stakingtypes.Delegation) (wasmvmtypes.Delegations, error) {
   389  	result := make([]wasmvmtypes.Delegation, len(delegations))
   390  	bondDenom := keeper.BondDenom(ctx)
   391  
   392  	for i, d := range delegations {
   393  		delAddr, err := sdk.AccAddressFromBech32(d.DelegatorAddress.String())
   394  		if err != nil {
   395  			return nil, sdkerrors.Wrap(err, "delegator address")
   396  		}
   397  		valAddr, err := sdk.ValAddressFromBech32(d.ValidatorAddress.String())
   398  		if err != nil {
   399  			return nil, sdkerrors.Wrap(err, "validator address")
   400  		}
   401  
   402  		// shares to amount logic comes from here:
   403  		// https://github.com/fibonacci-chain/fbc/libs/cosmos-sdk/blob/v0.38.3/x/staking/keeper/querier.go#L404
   404  		val, found := keeper.GetValidator(ctx, valAddr)
   405  		if !found {
   406  			return nil, sdkerrors.Wrap(stakingtypes.ErrNoValidatorFound, "can't load validator for delegation")
   407  		}
   408  		amount := sdk.NewCoin(bondDenom, val.TokensFromShares(d.Shares).TruncateInt())
   409  		adapter := sdk.CoinToCoinAdapter(amount)
   410  		result[i] = wasmvmtypes.Delegation{
   411  			Delegator: delAddr.String(),
   412  			Validator: valAddr.String(),
   413  			Amount:    ConvertSdkCoinToWasmCoin(adapter),
   414  		}
   415  	}
   416  	return result, nil
   417  }
   418  
   419  func sdkToFullDelegation(ctx sdk.Context, keeper types.StakingKeeper, distKeeper types.DistributionKeeper, delegation stakingtypes.Delegation) (*wasmvmtypes.FullDelegation, error) {
   420  	delAddr, err := sdk.AccAddressFromBech32(delegation.DelegatorAddress.String())
   421  	if err != nil {
   422  		return nil, sdkerrors.Wrap(err, "delegator address")
   423  	}
   424  	valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress.String())
   425  	if err != nil {
   426  		return nil, sdkerrors.Wrap(err, "validator address")
   427  	}
   428  	val, found := keeper.GetValidator(ctx, valAddr)
   429  	if !found {
   430  		return nil, sdkerrors.Wrap(stakingtypes.ErrNoValidatorFound, "can't load validator for delegation")
   431  	}
   432  	bondDenom := keeper.BondDenom(ctx)
   433  	amount := sdk.NewCoin(bondDenom, val.TokensFromShares(delegation.Shares).TruncateInt())
   434  	adapter := sdk.CoinToCoinAdapter(amount)
   435  	delegationCoins := ConvertSdkCoinToWasmCoin(adapter)
   436  
   437  	// FIXME: this is very rough but better than nothing...
   438  	// https://github.com/fibonacci-chain/fbc/issues/282
   439  	// if this (val, delegate) pair is receiving a redelegation, it cannot redelegate more
   440  	// otherwise, it can redelegate the full amount
   441  	// (there are cases of partial funds redelegated, but this is a start)
   442  	redelegateCoins := wasmvmtypes.NewCoin(0, bondDenom)
   443  	if !keeper.HasReceivingRedelegation(ctx, delAddr, valAddr) {
   444  		redelegateCoins = delegationCoins
   445  	}
   446  
   447  	// FIXME: make a cleaner way to do this (modify the sdk)
   448  	// we need the info from `distKeeper.calculateDelegationRewards()`, but it is not public
   449  	// neither is `queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, k Keeper)`
   450  	// so we go through the front door of the querier....
   451  	accRewards, err := getAccumulatedRewards(ctx, distKeeper, delegation)
   452  	if err != nil {
   453  		return nil, err
   454  	}
   455  
   456  	return &wasmvmtypes.FullDelegation{
   457  		Delegator:          delAddr.String(),
   458  		Validator:          valAddr.String(),
   459  		Amount:             delegationCoins,
   460  		AccumulatedRewards: accRewards,
   461  		CanRedelegate:      redelegateCoins,
   462  	}, nil
   463  }
   464  
   465  // FIXME: simplify this enormously when
   466  // https://github.com/fibonacci-chain/fbc/libs/cosmos-sdk/issues/7466 is merged
   467  func getAccumulatedRewards(ctx sdk.Context, distKeeper types.DistributionKeeper, delegation stakingtypes.Delegation) ([]wasmvmtypes.Coin, error) {
   468  	// Try to get *delegator* reward info!
   469  	params := distributiontypes.QueryDelegationRewardsParams{
   470  		DelegatorAddress: delegation.DelegatorAddress,
   471  		ValidatorAddress: delegation.ValidatorAddress,
   472  	}
   473  	cache, _ := ctx.CacheContext()
   474  	qres, err := distKeeper.DelegationRewards(sdk.WrapSDKContext(cache), &params)
   475  	if err != nil {
   476  		return nil, err
   477  	}
   478  
   479  	// now we have it, convert it into wasmvm types
   480  	rewards := make([]wasmvmtypes.Coin, qres.Len())
   481  	for i, r := range *qres {
   482  		rewards[i] = wasmvmtypes.Coin{
   483  			Denom:  r.Denom,
   484  			Amount: r.Amount.TruncateInt().String(),
   485  		}
   486  	}
   487  	return rewards, nil
   488  }
   489  
   490  func WasmQuerier(k wasmQueryKeeper) func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error) {
   491  	return func(ctx sdk.Context, request *wasmvmtypes.WasmQuery) ([]byte, error) {
   492  		switch {
   493  		case request.Smart != nil:
   494  			addr, err := sdk.AccAddressFromBech32(request.Smart.ContractAddr)
   495  			if err != nil {
   496  				return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Smart.ContractAddr)
   497  			}
   498  			msg := types.RawContractMessage(request.Smart.Msg)
   499  			if err := msg.ValidateBasic(); err != nil {
   500  				return nil, sdkerrors.Wrap(err, "json msg")
   501  			}
   502  			return k.QuerySmart(ctx, addr, msg)
   503  		case request.Raw != nil:
   504  			addr, err := sdk.AccAddressFromBech32(request.Raw.ContractAddr)
   505  			if err != nil {
   506  				return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.Raw.ContractAddr)
   507  			}
   508  			return k.QueryRaw(ctx, addr, request.Raw.Key), nil
   509  		case request.ContractInfo != nil:
   510  			addr, err := sdk.AccAddressFromBech32(request.ContractInfo.ContractAddr)
   511  			if err != nil {
   512  				return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, request.ContractInfo.ContractAddr)
   513  			}
   514  			info := k.GetContractInfo(ctx, addr)
   515  			if info == nil {
   516  				return nil, &types.ErrNoSuchContract{Addr: request.ContractInfo.ContractAddr}
   517  			}
   518  
   519  			res := wasmvmtypes.ContractInfoResponse{
   520  				CodeID:  info.CodeID,
   521  				Creator: info.Creator,
   522  				Admin:   info.Admin,
   523  				Pinned:  k.IsPinnedCode(ctx, info.CodeID),
   524  				IBCPort: info.IBCPortID,
   525  			}
   526  			return json.Marshal(res)
   527  		}
   528  		return nil, wasmvmtypes.UnsupportedRequest{Kind: "unknown WasmQuery variant"}
   529  	}
   530  }
   531  
   532  // ConvertSdkCoinsToWasmCoins covert sdk type to wasmvm coins type
   533  func ConvertSdkCoinsToWasmCoins(coins []sdk.CoinAdapter) wasmvmtypes.Coins {
   534  	converted := make(wasmvmtypes.Coins, len(coins))
   535  	for i, c := range coins {
   536  		converted[i] = ConvertSdkCoinToWasmCoin(c)
   537  	}
   538  	return converted
   539  }
   540  
   541  // ConvertSdkCoinToWasmCoin covert sdk type to wasmvm coin type
   542  func ConvertSdkCoinToWasmCoin(coin sdk.CoinAdapter) wasmvmtypes.Coin {
   543  	return wasmvmtypes.Coin{
   544  		Denom:  coin.Denom,
   545  		Amount: coin.Amount.String(),
   546  	}
   547  }
   548  
   549  var _ WasmVMQueryHandler = WasmVMQueryHandlerFn(nil)
   550  
   551  // WasmVMQueryHandlerFn is a helper to construct a function based query handler.
   552  type WasmVMQueryHandlerFn func(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error)
   553  
   554  // HandleQuery delegates call into wrapped WasmVMQueryHandlerFn
   555  func (w WasmVMQueryHandlerFn) HandleQuery(ctx sdk.Context, caller sdk.AccAddress, request wasmvmtypes.QueryRequest) ([]byte, error) {
   556  	return w(ctx, caller, request)
   557  }