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), ¶ms) 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 }