Skip to content

Commit

Permalink
add onft module queries
Browse files Browse the repository at this point in the history
  • Loading branch information
slandymani committed Oct 7, 2024
1 parent 23f707a commit cdc21dd
Show file tree
Hide file tree
Showing 7 changed files with 4,542 additions and 0 deletions.
54 changes: 54 additions & 0 deletions proto/onft/v1/onft.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
syntax = "proto3";
package onft.v1;

import "google/protobuf/any.proto";

option go_package = "github.com/ODIN-PROTOCOL/odin-core/x/onft/types";

// Class defines the class of the nft type.
message Class {
// id defines the unique identifier of the NFT classification, similar to the contract address of ERC721
string id = 1;

// name defines the human-readable name of the NFT classification. Optional
string name = 2;

// symbol is an abbreviated name for nft classification. Optional
string symbol = 3;

// description is a brief description of nft classification. Optional
string description = 4;

// uri for the class metadata stored off chain. It can define schema for Class and NFT `Data` attributes. Optional
string uri = 5;

// uri_hash is a hash of the document pointed by uri. Optional
string uri_hash = 6;

// data is the app specific metadata of the NFT class. Optional
google.protobuf.Any data = 7;

// owner is the owner address of the following nft class
string owner = 8;
}

// NFT defines the NFT.
message NFT {
// class_id associated with the NFT, similar to the contract address of ERC721
string class_id = 1;

// id is a unique identifier of the NFT
string id = 2;

// uri for the NFT metadata stored off chain
string uri = 3;

// uri_hash is a hash of the document pointed by uri
string uri_hash = 4;

// owner is the owner address of the following nft
string owner = 5;

// data is an app specific data of the NFT. Optional
google.protobuf.Any data = 10;
}
112 changes: 112 additions & 0 deletions proto/onft/v1/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
syntax = "proto3";
package onft.v1;

option go_package = "github.com/ODIN-PROTOCOL/odin-core/x/onft/types";

import "google/api/annotations.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "onft/v1/onft.proto";

// Query defines the gRPC querier service.
service Query {
// ClassOwner queries the owner of the NFT class
rpc ClassOwner(QueryClassOwnerRequest) returns (QueryClassOwnerResponse) {
option (google.api.http).get = "/onft/v1/class/{class_id}/owner";
}

// NFTs queries all NFTs with owners of a given class or owner,choose at least one of the two,
// similar to tokenByIndex in ERC721Enumerable
rpc NFTs(QueryNFTsRequest) returns (QueryNFTsResponse) {
option (google.api.http).get = "/onft/v1/nfts";
}

// NFT queries an NFT with owner based on its class and id
rpc NFT(QueryNFTRequest) returns (QueryNFTResponse) {
option (google.api.http).get = "/onft/v1/nfts/{class_id}/{id}";
}

// Class queries an NFT class based on its id with class owner
rpc Class(QueryClassRequest) returns (QueryClassResponse) {
option (google.api.http).get = "/onft/v1/classes/{class_id}";
}

// Classes queries all NFT classes with owners
rpc Classes(QueryClassesRequest) returns (QueryClassesResponse) {
option (google.api.http).get = "/onft/v1/classes";
}
}

// QueryClassOwnerRequest is the request type for the Query/Owner RPC method
message QueryClassOwnerRequest {
// class_id associated with nft collection
string class_id = 1;
}

// QueryClassOwnerResponse is the response type for the Query/Owner RPC method
message QueryClassOwnerResponse {
// owner is the owner address of the nft class
string owner = 1;
}

// QueryNFTstRequest is the request type for the Query/NFTs RPC method
message QueryNFTsRequest {
// class_id associated with the nft
string class_id = 1;

// owner is the owner address of the nft
string owner = 2;

// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 3;
}

// QueryNFTsResponse is the response type for the Query/NFTs RPC methods
message QueryNFTsResponse {
// NFT defines the NFT
repeated NFT nfts = 1;

// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}

// QueryNFTRequest is the request type for the Query/NFT RPC method
message QueryNFTRequest {
// class_id associated with the nft
string class_id = 1;

// id is a unique identifier of the NFT
string id = 2;
}

// QueryNFTResponse is the response type for the Query/NFT RPC method
message QueryNFTResponse {
// owner is the owner address of the nft
NFT nft = 1;
}

// QueryClassRequest is the request type for the Query/Class RPC method
message QueryClassRequest {
// class_id associated with the nft
string class_id = 1;
}

// QueryClassResponse is the response type for the Query/Class RPC method
message QueryClassResponse {
// class defines the class of the nft type.
Class class = 1;
}

// QueryClassesRequest is the request type for the Query/Classes RPC method
message QueryClassesRequest {
// pagination defines an optional pagination for the request.
cosmos.base.query.v1beta1.PageRequest pagination = 1;
}

// QueryClassesResponse is the response type for the Query/Classes RPC method
message QueryClassesResponse {
// class defines the class of the nft type.
repeated Class classes = 1;

// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
180 changes: 180 additions & 0 deletions x/onft/keeper/grpc_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package keeper

import (
"context"

"cosmossdk.io/x/nft"
onfttypes "github.com/ODIN-PROTOCOL/odin-core/x/onft/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)

var _ onfttypes.QueryServer = Keeper{}

func (k Keeper) ClassOwner(ctx context.Context, r *onfttypes.QueryClassOwnerRequest) (*onfttypes.QueryClassOwnerResponse, error) {
if r == nil {
return nil, sdkerrors.ErrInvalidRequest.Wrap("empty request")
}

if len(r.ClassId) == 0 {
return nil, nft.ErrEmptyClassID
}

owner, err := k.ClassOwners.Get(ctx, r.ClassId)
if err != nil {
return nil, err
}

ownerStr, err := k.addressCodec.BytesToString(owner)
if err != nil {
return nil, err
}

return &onfttypes.QueryClassOwnerResponse{Owner: ownerStr}, nil
}

func (k Keeper) NFTs(ctx context.Context, r *onfttypes.QueryNFTsRequest) (*onfttypes.QueryNFTsResponse, error) {
if r == nil {
return nil, sdkerrors.ErrInvalidRequest.Wrap("empty request")
}

nfts, err := k.nftKeeper.NFTs(ctx, &nft.QueryNFTsRequest{Pagination: r.Pagination})
if err != nil {
return nil, err
}

nftsOwners := make([]*onfttypes.NFT, len(nfts.Nfts))
for i, n := range nfts.Nfts {
ownerStr := ""
owner := k.nftKeeper.GetOwner(ctx, n.ClassId, n.Id)
if !owner.Empty() {
ownerStr, _ = k.addressCodec.BytesToString(owner)
}

nftsOwners[i] = &onfttypes.NFT{
Id: n.Id,
ClassId: n.ClassId,
Uri: n.Uri,
UriHash: n.UriHash,
Data: n.Data,
Owner: ownerStr,
}
}

return &onfttypes.QueryNFTsResponse{
Nfts: nftsOwners,
Pagination: nfts.Pagination,
}, nil
}

func (k Keeper) NFT(ctx context.Context, r *onfttypes.QueryNFTRequest) (*onfttypes.QueryNFTResponse, error) {
if r == nil {
return nil, sdkerrors.ErrInvalidRequest.Wrap("empty request")
}

if len(r.ClassId) == 0 {
return nil, nft.ErrEmptyClassID
}
if len(r.Id) == 0 {
return nil, nft.ErrEmptyNFTID
}

n, has := k.nftKeeper.GetNFT(ctx, r.ClassId, r.Id)
if !has {
return nil, nft.ErrNFTNotExists.Wrapf("not found nft: class: %s, id: %s", r.ClassId, r.Id)
}

owner := k.nftKeeper.GetOwner(ctx, r.ClassId, r.Id)
ownerStr, err := k.addressCodec.BytesToString(owner)
if err != nil {
return nil, err
}

resp := &onfttypes.QueryNFTResponse{
Nft: &onfttypes.NFT{
ClassId: n.ClassId,
Id: n.Id,
Uri: n.Uri,
UriHash: n.UriHash,
Owner: ownerStr,
Data: n.Data,
},
}

return resp, nil
}

func (k Keeper) Class(ctx context.Context, r *onfttypes.QueryClassRequest) (*onfttypes.QueryClassResponse, error) {
if r == nil {
return nil, sdkerrors.ErrInvalidRequest.Wrap("empty request")
}

if len(r.ClassId) == 0 {
return nil, nft.ErrEmptyClassID
}

class, has := k.nftKeeper.GetClass(ctx, r.ClassId)
if !has {
return nil, nft.ErrClassNotExists.Wrapf("not found class: %s", r.ClassId)
}

owner, err := k.ClassOwners.Get(ctx, r.ClassId)
if err != nil {
return nil, err
}

ownerStr, err := k.addressCodec.BytesToString(owner)
if err != nil {
return nil, err
}

resp := &onfttypes.QueryClassResponse{
Class: &onfttypes.Class{
Id: class.Id,
Name: class.Name,
Symbol: class.Symbol,
Description: class.Description,
Uri: class.Uri,
UriHash: class.UriHash,
Data: class.Data,
Owner: ownerStr,
},
}

return resp, nil
}

func (k Keeper) Classes(ctx context.Context, r *onfttypes.QueryClassesRequest) (*onfttypes.QueryClassesResponse, error) {
if r == nil {
return nil, sdkerrors.ErrInvalidRequest.Wrap("empty request")
}

classes, err := k.nftKeeper.Classes(ctx, &nft.QueryClassesRequest{Pagination: r.Pagination})
if err != nil {
return nil, err
}

classesOwners := make([]*onfttypes.Class, len(classes.Classes))
for i, class := range classes.Classes {
ownerStr := ""
owner, err := k.ClassOwners.Get(ctx, class.Id)
if err == nil {
ownerStr, _ = k.addressCodec.BytesToString(owner)
}

classesOwners[i] = &onfttypes.Class{
Id: class.Id,
Name: class.Name,
Symbol: class.Symbol,
Description: class.Description,
Uri: class.Uri,
UriHash: class.UriHash,
Data: class.Data,
Owner: ownerStr,
}
}

return &onfttypes.QueryClassesResponse{
Classes: classesOwners,
Pagination: classes.Pagination,
}, nil
}
2 changes: 2 additions & 0 deletions x/onft/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ import sdkerrors "cosmossdk.io/errors"
var (
ErrClassNotFound = sdkerrors.New(ModuleName, 1, "class not found")
ErrSenderNotAuthorized = sdkerrors.Register(ModuleName, 2, "sender not authorized")
ErrEmptyClassID = sdkerrors.Register(ModuleName, 3, "empty class id")
ErrEmptyNFTID = sdkerrors.Register(ModuleName, 4, "empty nft id")
)
Loading

0 comments on commit cdc21dd

Please sign in to comment.