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 }