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 }