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

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