github.com/Finschia/finschia-sdk@v0.48.1/x/collection/keeper/grpc_query.go (about)

     1  package keeper
     2  
     3  import (
     4  	"context"
     5  
     6  	"google.golang.org/grpc/codes"
     7  	"google.golang.org/grpc/status"
     8  
     9  	"github.com/gogo/protobuf/proto"
    10  
    11  	codectypes "github.com/Finschia/finschia-sdk/codec/types"
    12  	"github.com/Finschia/finschia-sdk/store/prefix"
    13  	sdk "github.com/Finschia/finschia-sdk/types"
    14  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
    15  	"github.com/Finschia/finschia-sdk/types/query"
    16  	"github.com/Finschia/finschia-sdk/x/collection"
    17  )
    18  
    19  type queryServer struct {
    20  	keeper Keeper
    21  }
    22  
    23  // NewQueryServer returns an implementation of the token QueryServer interface
    24  // for the provided Keeper.
    25  func NewQueryServer(keeper Keeper) collection.QueryServer {
    26  	return &queryServer{
    27  		keeper: keeper,
    28  	}
    29  }
    30  
    31  func (s queryServer) addressFromBech32GRPC(bech32 string, context string) (sdk.AccAddress, error) {
    32  	addr, err := sdk.AccAddressFromBech32(bech32)
    33  	if err != nil {
    34  		return nil, status.Error(codes.InvalidArgument, sdkerrors.Wrap(sdkerrors.ErrInvalidAddress.Wrap(bech32), context).Error())
    35  	}
    36  
    37  	return addr, nil
    38  }
    39  
    40  func (s queryServer) assertTokenIsFungible(ctx sdk.Context, contractID string, classID string) error {
    41  	class, err := s.keeper.GetTokenClass(ctx, contractID, classID)
    42  	if err != nil {
    43  		return err
    44  	}
    45  
    46  	if _, ok := class.(*collection.FTClass); !ok {
    47  		return collection.ErrTokenNotExist.Wrap(collection.NewFTID(classID))
    48  	}
    49  
    50  	return nil
    51  }
    52  
    53  func (s queryServer) assertTokenTypeIsNonFungible(ctx sdk.Context, contractID string, classID string) error {
    54  	class, err := s.keeper.GetTokenClass(ctx, contractID, classID)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	if _, ok := class.(*collection.NFTClass); !ok {
    60  		return collection.ErrTokenTypeNotExist.Wrap(classID)
    61  	}
    62  
    63  	return nil
    64  }
    65  
    66  var _ collection.QueryServer = queryServer{}
    67  
    68  // Balance queries the number of tokens of a given token id owned by the owner.
    69  func (s queryServer) Balance(c context.Context, req *collection.QueryBalanceRequest) (*collection.QueryBalanceResponse, error) {
    70  	if req == nil {
    71  		return nil, status.Error(codes.InvalidArgument, "empty request")
    72  	}
    73  
    74  	if err := collection.ValidateContractID(req.ContractId); err != nil {
    75  		return nil, status.Error(codes.InvalidArgument, err.Error())
    76  	}
    77  
    78  	addr, err := s.addressFromBech32GRPC(req.Address, "address")
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	if err := collection.ValidateTokenID(req.TokenId); err != nil {
    84  		return nil, status.Error(codes.InvalidArgument, err.Error())
    85  	}
    86  
    87  	ctx := sdk.UnwrapSDKContext(c)
    88  	balance := s.keeper.GetBalance(ctx, req.ContractId, addr, req.TokenId)
    89  	coin := collection.Coin{
    90  		TokenId: req.TokenId,
    91  		Amount:  balance,
    92  	}
    93  
    94  	return &collection.QueryBalanceResponse{Balance: coin}, nil
    95  }
    96  
    97  // AllBalances queries all tokens owned by owner.
    98  func (s queryServer) AllBalances(c context.Context, req *collection.QueryAllBalancesRequest) (*collection.QueryAllBalancesResponse, error) {
    99  	if req == nil {
   100  		return nil, status.Error(codes.InvalidArgument, "empty request")
   101  	}
   102  
   103  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   104  		return nil, status.Error(codes.InvalidArgument, err.Error())
   105  	}
   106  
   107  	addr, err := s.addressFromBech32GRPC(req.Address, "address")
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	ctx := sdk.UnwrapSDKContext(c)
   113  	store := ctx.KVStore(s.keeper.storeKey)
   114  	balanceStore := prefix.NewStore(store, balanceKeyPrefixByAddress(req.ContractId, addr))
   115  	var balances []collection.Coin
   116  	pageRes, err := query.Paginate(balanceStore, req.Pagination, func(key []byte, value []byte) error {
   117  		tokenID := string(key)
   118  
   119  		var balance sdk.Int
   120  		if err := balance.Unmarshal(value); err != nil {
   121  			panic(err)
   122  		}
   123  
   124  		coin := collection.NewCoin(tokenID, balance)
   125  		balances = append(balances, coin)
   126  		return nil
   127  	})
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	return &collection.QueryAllBalancesResponse{Balances: balances, Pagination: pageRes}, nil
   133  }
   134  
   135  func (s queryServer) FTSupply(c context.Context, req *collection.QueryFTSupplyRequest) (*collection.QueryFTSupplyResponse, error) {
   136  	if req == nil {
   137  		return nil, status.Error(codes.InvalidArgument, "empty request")
   138  	}
   139  
   140  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   141  		return nil, status.Error(codes.InvalidArgument, err.Error())
   142  	}
   143  
   144  	if err := collection.ValidateTokenID(req.TokenId); err != nil {
   145  		return nil, status.Error(codes.InvalidArgument, err.Error())
   146  	}
   147  
   148  	classID := collection.SplitTokenID(req.TokenId)
   149  
   150  	ctx := sdk.UnwrapSDKContext(c)
   151  
   152  	if err := s.assertTokenIsFungible(ctx, req.ContractId, classID); err != nil {
   153  		return &collection.QueryFTSupplyResponse{Supply: sdk.ZeroInt()}, nil
   154  	}
   155  
   156  	supply := s.keeper.GetSupply(ctx, req.ContractId, classID)
   157  
   158  	return &collection.QueryFTSupplyResponse{Supply: supply}, nil
   159  }
   160  
   161  func (s queryServer) FTMinted(c context.Context, req *collection.QueryFTMintedRequest) (*collection.QueryFTMintedResponse, error) {
   162  	if req == nil {
   163  		return nil, status.Error(codes.InvalidArgument, "empty request")
   164  	}
   165  
   166  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   167  		return nil, status.Error(codes.InvalidArgument, err.Error())
   168  	}
   169  
   170  	if err := collection.ValidateTokenID(req.TokenId); err != nil {
   171  		return nil, status.Error(codes.InvalidArgument, err.Error())
   172  	}
   173  
   174  	classID := collection.SplitTokenID(req.TokenId)
   175  
   176  	ctx := sdk.UnwrapSDKContext(c)
   177  
   178  	if err := s.assertTokenIsFungible(ctx, req.ContractId, classID); err != nil {
   179  		return &collection.QueryFTMintedResponse{Minted: sdk.ZeroInt()}, nil
   180  	}
   181  
   182  	minted := s.keeper.GetMinted(ctx, req.ContractId, classID)
   183  
   184  	return &collection.QueryFTMintedResponse{Minted: minted}, nil
   185  }
   186  
   187  func (s queryServer) FTBurnt(c context.Context, req *collection.QueryFTBurntRequest) (*collection.QueryFTBurntResponse, error) {
   188  	if req == nil {
   189  		return nil, status.Error(codes.InvalidArgument, "empty request")
   190  	}
   191  
   192  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   193  		return nil, status.Error(codes.InvalidArgument, err.Error())
   194  	}
   195  
   196  	if err := collection.ValidateTokenID(req.TokenId); err != nil {
   197  		return nil, status.Error(codes.InvalidArgument, err.Error())
   198  	}
   199  
   200  	classID := collection.SplitTokenID(req.TokenId)
   201  
   202  	ctx := sdk.UnwrapSDKContext(c)
   203  
   204  	if err := s.assertTokenIsFungible(ctx, req.ContractId, classID); err != nil {
   205  		return &collection.QueryFTBurntResponse{Burnt: sdk.ZeroInt()}, nil
   206  	}
   207  
   208  	burnt := s.keeper.GetBurnt(ctx, req.ContractId, classID)
   209  
   210  	return &collection.QueryFTBurntResponse{Burnt: burnt}, nil
   211  }
   212  
   213  func (s queryServer) NFTSupply(c context.Context, req *collection.QueryNFTSupplyRequest) (*collection.QueryNFTSupplyResponse, error) {
   214  	if req == nil {
   215  		return nil, status.Error(codes.InvalidArgument, "empty request")
   216  	}
   217  
   218  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   219  		return nil, status.Error(codes.InvalidArgument, err.Error())
   220  	}
   221  
   222  	classID := req.TokenType
   223  	if err := collection.ValidateClassID(classID); err != nil {
   224  		return nil, status.Error(codes.InvalidArgument, err.Error())
   225  	}
   226  
   227  	ctx := sdk.UnwrapSDKContext(c)
   228  
   229  	if err := s.assertTokenTypeIsNonFungible(ctx, req.ContractId, classID); err != nil {
   230  		return &collection.QueryNFTSupplyResponse{Supply: sdk.ZeroInt()}, nil
   231  	}
   232  
   233  	supply := s.keeper.GetSupply(ctx, req.ContractId, classID)
   234  
   235  	return &collection.QueryNFTSupplyResponse{Supply: supply}, nil
   236  }
   237  
   238  func (s queryServer) NFTMinted(c context.Context, req *collection.QueryNFTMintedRequest) (*collection.QueryNFTMintedResponse, error) {
   239  	if req == nil {
   240  		return nil, status.Error(codes.InvalidArgument, "empty request")
   241  	}
   242  
   243  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   244  		return nil, status.Error(codes.InvalidArgument, err.Error())
   245  	}
   246  
   247  	classID := req.TokenType
   248  	if err := collection.ValidateClassID(classID); err != nil {
   249  		return nil, status.Error(codes.InvalidArgument, err.Error())
   250  	}
   251  
   252  	ctx := sdk.UnwrapSDKContext(c)
   253  
   254  	if err := s.assertTokenTypeIsNonFungible(ctx, req.ContractId, classID); err != nil {
   255  		return &collection.QueryNFTMintedResponse{Minted: sdk.ZeroInt()}, nil
   256  	}
   257  
   258  	minted := s.keeper.GetMinted(ctx, req.ContractId, classID)
   259  
   260  	return &collection.QueryNFTMintedResponse{Minted: minted}, nil
   261  }
   262  
   263  func (s queryServer) NFTBurnt(c context.Context, req *collection.QueryNFTBurntRequest) (*collection.QueryNFTBurntResponse, error) {
   264  	if req == nil {
   265  		return nil, status.Error(codes.InvalidArgument, "empty request")
   266  	}
   267  
   268  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   269  		return nil, status.Error(codes.InvalidArgument, err.Error())
   270  	}
   271  
   272  	classID := req.TokenType
   273  	if err := collection.ValidateClassID(classID); err != nil {
   274  		return nil, status.Error(codes.InvalidArgument, err.Error())
   275  	}
   276  
   277  	ctx := sdk.UnwrapSDKContext(c)
   278  
   279  	if err := s.assertTokenTypeIsNonFungible(ctx, req.ContractId, classID); err != nil {
   280  		return &collection.QueryNFTBurntResponse{Burnt: sdk.ZeroInt()}, nil
   281  	}
   282  
   283  	burnt := s.keeper.GetBurnt(ctx, req.ContractId, classID)
   284  
   285  	return &collection.QueryNFTBurntResponse{Burnt: burnt}, nil
   286  }
   287  
   288  func (s queryServer) Contract(c context.Context, req *collection.QueryContractRequest) (*collection.QueryContractResponse, error) {
   289  	if req == nil {
   290  		return nil, status.Error(codes.InvalidArgument, "empty request")
   291  	}
   292  
   293  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   294  		return nil, status.Error(codes.InvalidArgument, err.Error())
   295  	}
   296  
   297  	ctx := sdk.UnwrapSDKContext(c)
   298  	contract, err := s.keeper.GetContract(ctx, req.ContractId)
   299  	if err != nil {
   300  		return nil, status.Error(codes.NotFound, err.Error())
   301  	}
   302  
   303  	return &collection.QueryContractResponse{Contract: *contract}, nil
   304  }
   305  
   306  // TokenClassTypeName queries the fully qualified message type name of a token class based on its class id.
   307  func (s queryServer) TokenClassTypeName(c context.Context, req *collection.QueryTokenClassTypeNameRequest) (*collection.QueryTokenClassTypeNameResponse, error) {
   308  	if req == nil {
   309  		return nil, status.Error(codes.InvalidArgument, "empty request")
   310  	}
   311  
   312  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   313  		return nil, status.Error(codes.InvalidArgument, err.Error())
   314  	}
   315  
   316  	if err := collection.ValidateClassID(req.ClassId); err != nil {
   317  		return nil, status.Error(codes.InvalidArgument, err.Error())
   318  	}
   319  
   320  	ctx := sdk.UnwrapSDKContext(c)
   321  	class, err := s.keeper.GetTokenClass(ctx, req.ContractId, req.ClassId)
   322  	if err != nil {
   323  		return nil, status.Error(codes.NotFound, err.Error())
   324  	}
   325  	name := proto.MessageName(class)
   326  
   327  	return &collection.QueryTokenClassTypeNameResponse{Name: name}, nil
   328  }
   329  
   330  func (s queryServer) TokenType(c context.Context, req *collection.QueryTokenTypeRequest) (*collection.QueryTokenTypeResponse, error) {
   331  	if req == nil {
   332  		return nil, status.Error(codes.InvalidArgument, "empty request")
   333  	}
   334  
   335  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   336  		return nil, status.Error(codes.InvalidArgument, err.Error())
   337  	}
   338  
   339  	classID := req.TokenType
   340  	if err := collection.ValidateClassID(classID); err != nil {
   341  		return nil, status.Error(codes.InvalidArgument, err.Error())
   342  	}
   343  
   344  	ctx := sdk.UnwrapSDKContext(c)
   345  	class, err := s.keeper.GetTokenClass(ctx, req.ContractId, classID)
   346  	if err != nil {
   347  		return nil, status.Error(codes.NotFound, err.Error())
   348  	}
   349  
   350  	nftClass, ok := class.(*collection.NFTClass)
   351  	if !ok {
   352  		return nil, status.Error(codes.NotFound, sdkerrors.ErrInvalidType.Wrapf("not a class of non-fungible token: %s", classID).Error())
   353  	}
   354  
   355  	tokenType := collection.TokenType{
   356  		ContractId: req.ContractId,
   357  		TokenType:  nftClass.Id,
   358  		Name:       nftClass.Name,
   359  		Meta:       nftClass.Meta,
   360  	}
   361  
   362  	return &collection.QueryTokenTypeResponse{TokenType: tokenType}, nil
   363  }
   364  
   365  func (s queryServer) getToken(ctx sdk.Context, contractID string, tokenID string) (collection.Token, error) {
   366  	switch {
   367  	case collection.ValidateNFTID(tokenID) == nil:
   368  		token, err := s.keeper.GetNFT(ctx, contractID, tokenID)
   369  		if err != nil {
   370  			return nil, err
   371  		}
   372  
   373  		owner := s.keeper.GetRootOwner(ctx, contractID, token.TokenId)
   374  		return &collection.OwnerNFT{
   375  			ContractId: contractID,
   376  			TokenId:    token.TokenId,
   377  			Name:       token.Name,
   378  			Meta:       token.Meta,
   379  			Owner:      owner.String(),
   380  		}, nil
   381  	case collection.ValidateFTID(tokenID) == nil:
   382  		classID := collection.SplitTokenID(tokenID)
   383  		class, err := s.keeper.GetTokenClass(ctx, contractID, classID)
   384  		if err != nil {
   385  			return nil, err
   386  		}
   387  
   388  		ftClass, ok := class.(*collection.FTClass)
   389  		if !ok {
   390  			panic(sdkerrors.ErrInvalidType.Wrapf("not a class of fungible token: %s", classID))
   391  		}
   392  
   393  		return &collection.FT{
   394  			ContractId: contractID,
   395  			TokenId:    collection.NewFTID(ftClass.Id),
   396  			Name:       ftClass.Name,
   397  			Meta:       ftClass.Meta,
   398  			Decimals:   ftClass.Decimals,
   399  			Mintable:   ftClass.Mintable,
   400  		}, nil
   401  	default:
   402  		panic("cannot reach here: token must be ft or nft")
   403  	}
   404  }
   405  
   406  func (s queryServer) Token(c context.Context, req *collection.QueryTokenRequest) (*collection.QueryTokenResponse, error) {
   407  	if req == nil {
   408  		return nil, status.Error(codes.InvalidArgument, "empty request")
   409  	}
   410  
   411  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   412  		return nil, status.Error(codes.InvalidArgument, err.Error())
   413  	}
   414  
   415  	if err := collection.ValidateTokenID(req.TokenId); err != nil {
   416  		return nil, status.Error(codes.InvalidArgument, err.Error())
   417  	}
   418  
   419  	ctx := sdk.UnwrapSDKContext(c)
   420  	legacyToken, err := s.getToken(ctx, req.ContractId, req.TokenId)
   421  	if err != nil {
   422  		return nil, status.Error(codes.NotFound, err.Error())
   423  	}
   424  
   425  	any, err := codectypes.NewAnyWithValue(legacyToken)
   426  	if err != nil {
   427  		panic(err)
   428  	}
   429  
   430  	return &collection.QueryTokenResponse{Token: *any}, nil
   431  }
   432  
   433  func (s queryServer) Root(c context.Context, req *collection.QueryRootRequest) (*collection.QueryRootResponse, error) {
   434  	if req == nil {
   435  		return nil, status.Error(codes.InvalidArgument, "empty request")
   436  	}
   437  
   438  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   439  		return nil, status.Error(codes.InvalidArgument, err.Error())
   440  	}
   441  
   442  	if err := collection.ValidateNFTID(req.TokenId); err != nil {
   443  		return nil, status.Error(codes.InvalidArgument, err.Error())
   444  	}
   445  
   446  	ctx := sdk.UnwrapSDKContext(c)
   447  	if err := s.keeper.hasNFT(ctx, req.ContractId, req.TokenId); err != nil {
   448  		return nil, status.Error(codes.NotFound, err.Error())
   449  	}
   450  
   451  	root := s.keeper.GetRoot(ctx, req.ContractId, req.TokenId)
   452  	token, err := s.keeper.GetNFT(ctx, req.ContractId, root)
   453  	if err != nil {
   454  		panic(err)
   455  	}
   456  
   457  	return &collection.QueryRootResponse{Root: *token}, nil
   458  }
   459  
   460  func (s queryServer) HasParent(c context.Context, req *collection.QueryHasParentRequest) (*collection.QueryHasParentResponse, error) {
   461  	if req == nil {
   462  		return nil, status.Error(codes.InvalidArgument, "empty request")
   463  	}
   464  
   465  	if err := collection.ValidateContractID(req.GetContractId()); err != nil {
   466  		return nil, status.Error(codes.InvalidArgument, err.Error())
   467  	}
   468  
   469  	if err := collection.ValidateNFTID(req.GetTokenId()); err != nil {
   470  		return nil, status.Error(codes.InvalidArgument, err.Error())
   471  	}
   472  
   473  	ctx := sdk.UnwrapSDKContext(c)
   474  	_, err := s.keeper.GetParent(ctx, req.ContractId, req.TokenId)
   475  	return &collection.QueryHasParentResponse{HasParent: (err == nil)}, nil
   476  }
   477  
   478  func (s queryServer) Parent(c context.Context, req *collection.QueryParentRequest) (*collection.QueryParentResponse, error) {
   479  	if req == nil {
   480  		return nil, status.Error(codes.InvalidArgument, "empty request")
   481  	}
   482  
   483  	if err := collection.ValidateContractID(req.GetContractId()); err != nil {
   484  		return nil, status.Error(codes.InvalidArgument, err.Error())
   485  	}
   486  
   487  	if err := collection.ValidateNFTID(req.GetTokenId()); err != nil {
   488  		return nil, status.Error(codes.InvalidArgument, err.Error())
   489  	}
   490  
   491  	ctx := sdk.UnwrapSDKContext(c)
   492  	parent, err := s.keeper.GetParent(ctx, req.ContractId, req.TokenId)
   493  	if err != nil {
   494  		return nil, status.Error(codes.NotFound, err.Error())
   495  	}
   496  
   497  	token, err := s.keeper.GetNFT(ctx, req.ContractId, *parent)
   498  	if err != nil {
   499  		panic(err)
   500  	}
   501  
   502  	return &collection.QueryParentResponse{Parent: *token}, nil
   503  }
   504  
   505  func (s queryServer) Children(c context.Context, req *collection.QueryChildrenRequest) (*collection.QueryChildrenResponse, error) {
   506  	if req == nil {
   507  		return nil, status.Error(codes.InvalidArgument, "empty request")
   508  	}
   509  
   510  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   511  		return nil, status.Error(codes.InvalidArgument, err.Error())
   512  	}
   513  
   514  	if err := collection.ValidateNFTID(req.TokenId); err != nil {
   515  		return nil, status.Error(codes.InvalidArgument, err.Error())
   516  	}
   517  
   518  	ctx := sdk.UnwrapSDKContext(c)
   519  	store := ctx.KVStore(s.keeper.storeKey)
   520  	childStore := prefix.NewStore(store, childKeyPrefixByTokenID(req.ContractId, req.TokenId))
   521  	var children []collection.NFT
   522  	pageRes, err := query.Paginate(childStore, req.Pagination, func(key []byte, _ []byte) error {
   523  		childID := string(key)
   524  		child, err := s.keeper.GetNFT(ctx, req.ContractId, childID)
   525  		if err != nil {
   526  			panic(err)
   527  		}
   528  
   529  		children = append(children, *child)
   530  		return nil
   531  	})
   532  	if err != nil {
   533  		return nil, err
   534  	}
   535  
   536  	return &collection.QueryChildrenResponse{Children: children, Pagination: pageRes}, nil
   537  }
   538  
   539  func (s queryServer) GranteeGrants(c context.Context, req *collection.QueryGranteeGrantsRequest) (*collection.QueryGranteeGrantsResponse, error) {
   540  	if req == nil {
   541  		return nil, status.Error(codes.InvalidArgument, "empty request")
   542  	}
   543  
   544  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   545  		return nil, status.Error(codes.InvalidArgument, err.Error())
   546  	}
   547  
   548  	granteeAddr, err := s.addressFromBech32GRPC(req.Grantee, "grantee")
   549  	if err != nil {
   550  		return nil, err
   551  	}
   552  
   553  	ctx := sdk.UnwrapSDKContext(c)
   554  	store := ctx.KVStore(s.keeper.storeKey)
   555  	grantStore := prefix.NewStore(store, grantKeyPrefixByGrantee(req.ContractId, granteeAddr))
   556  	var grants []collection.Grant
   557  	pageRes, err := query.Paginate(grantStore, req.Pagination, func(key []byte, _ []byte) error {
   558  		permission := collection.Permission(key[0])
   559  		grants = append(grants, collection.Grant{
   560  			Grantee:    req.Grantee,
   561  			Permission: permission,
   562  		})
   563  		return nil
   564  	})
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  
   569  	return &collection.QueryGranteeGrantsResponse{Grants: grants, Pagination: pageRes}, nil
   570  }
   571  
   572  func (s queryServer) IsOperatorFor(c context.Context, req *collection.QueryIsOperatorForRequest) (*collection.QueryIsOperatorForResponse, error) {
   573  	if req == nil {
   574  		return nil, status.Error(codes.InvalidArgument, "empty request")
   575  	}
   576  
   577  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   578  		return nil, status.Error(codes.InvalidArgument, err.Error())
   579  	}
   580  
   581  	operator, err := s.addressFromBech32GRPC(req.Operator, "operator")
   582  	if err != nil {
   583  		return nil, err
   584  	}
   585  	holder, err := s.addressFromBech32GRPC(req.Holder, "holder")
   586  	if err != nil {
   587  		return nil, err
   588  	}
   589  
   590  	ctx := sdk.UnwrapSDKContext(c)
   591  	_, err = s.keeper.GetAuthorization(ctx, req.ContractId, holder, operator)
   592  	authorized := (err == nil)
   593  
   594  	return &collection.QueryIsOperatorForResponse{Authorized: authorized}, nil
   595  }
   596  
   597  func (s queryServer) HoldersByOperator(c context.Context, req *collection.QueryHoldersByOperatorRequest) (*collection.QueryHoldersByOperatorResponse, error) {
   598  	if req == nil {
   599  		return nil, status.Error(codes.InvalidArgument, "empty request")
   600  	}
   601  
   602  	if err := collection.ValidateContractID(req.ContractId); err != nil {
   603  		return nil, status.Error(codes.InvalidArgument, err.Error())
   604  	}
   605  
   606  	operator, err := s.addressFromBech32GRPC(req.Operator, "operator")
   607  	if err != nil {
   608  		return nil, err
   609  	}
   610  
   611  	ctx := sdk.UnwrapSDKContext(c)
   612  	store := ctx.KVStore(s.keeper.storeKey)
   613  	authorizationStore := prefix.NewStore(store, authorizationKeyPrefixByOperator(req.ContractId, operator))
   614  	var holders []string
   615  	pageRes, err := query.Paginate(authorizationStore, req.Pagination, func(key []byte, value []byte) error {
   616  		holder := sdk.AccAddress(key)
   617  		holders = append(holders, holder.String())
   618  		return nil
   619  	})
   620  	if err != nil {
   621  		return nil, err
   622  	}
   623  
   624  	return &collection.QueryHoldersByOperatorResponse{Holders: holders, Pagination: pageRes}, nil
   625  }