From cc0a59aaf79af6ae8325244d9ccb6b22825f1dca Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Wed, 8 Dec 2021 14:04:19 +0000 Subject: [PATCH] add nft assets endpoint (#298) --- nft/domain/opensea.go | 103 +++ nft/examples.json | 100 ++- nft/handler/nft.go | 37 +- nft/handler/opensea.go | 227 +++++++ nft/main.go | 2 +- nft/proto/nft.pb.go | 1287 +++++++++++++++++++++++++++++++++++-- nft/proto/nft.pb.micro.go | 33 +- nft/proto/nft.proto | 144 ++++- nft/publicapi.json | 4 +- 9 files changed, 1829 insertions(+), 108 deletions(-) create mode 100644 nft/domain/opensea.go create mode 100644 nft/handler/opensea.go diff --git a/nft/domain/opensea.go b/nft/domain/opensea.go new file mode 100644 index 0000000..a10b0a3 --- /dev/null +++ b/nft/domain/opensea.go @@ -0,0 +1,103 @@ +package domain + +type AssetResponse struct { + Assets []*Asset `json:"assets"` +} + +type Asset struct { + Id int32 `json:"id"` + TokenId string `json:"token_id"` + Sales int32 `json:"num_sales"` + ImageUrl string `json:"image_url"` + Name string `json:"name"` + Description string `json:"description"` + Permalink string `json:"permalink"` + Contract *Contract `json:"asset_contract"` + Collection *Collection `json:"collection"` + Creator *User `json:"creator"` + Owner *User `json:"owner"` + LastSale *Sale `json:"last_sale,omitempty"` + Presale bool `json:"is_presale"` + ListingDate string `json:"listing_date,omitempty"` +} + +type Contract struct { + // name of contract + Name string `json:"name,omitempty"` + // ethereum address + Address string `json:"address,omitempty"` + // type of contract e.g "semi-fungible" + Type string `json:"asset_contract_type,omitempty"` + // timestamp of creation + CreatedAt string `json:"created_date,omitempty"` + // owner id + Owner int32 `json:"owner,omitempty"` + // aka "ERC1155" + Schema string `json:"schema_name,omitempty"` + // related symbol + Symbol string `json:"symbol,omitempty"` + // description of contract + Description string `json:"description,omitempty"` + // payout address + PayoutAddress string `json:"payout_address,omitempty"` + // seller fees + SellerFees string `json:"seller_fees_basis_points,omitempty"` +} + +type Collection struct { + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` + Slug string `json:"slug,omitempty"` + ImageUrl string `json:"image_url,omitempty"` + CreatedAt string `json:"created_date,omitempty"` + PayoutAddress string `json:"payout_address,omitempty"` +} + +type User struct { + User *Username `json:"user"` + ProfileUrl string `json:"profile_img_url,omitempty"` + Address string `json:"address,omitempty"` +} + +type Username struct { + Username string `json:"username",omitempty"` +} + +type SaleAsset struct { + TokenId string `json:"token_id"` + Decimals int32 `json:"decimals"` +} + +type Sale struct { + Asset *SaleAsset `json:"asset"` + EventType string `json:"event_type,omitempty"` + EventTimestamp string `json:"event_timestamp,omitempty"` + TotalPrice string `json:"total_price,omitempty"` + Quantity string `json:"quantity,omitempty"` + CreatedAt string `json:"created_date,omitempty"` + Transaction *Transaction `json:"transaction,omitempty"` + PaymentToken *Token `json:"payment_token,omitempty"` +} + +type Transaction struct { + Id int32 `json:"id,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + BlockHash string `json:"block_hash,omitempty"` + BlockNumber string `json:"block_number,omitempty"` + FromAccount *User `json:"from_account,omitempty"` + ToAccount *User `json:"to_account,omitempty"` + TransactionHash string `json:"transaction_hash,omitempty"` + TransactionIndex string `json:"transaction_index,omitempty"` +} + +type Token struct { + Id int32 `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Symbol string `json:"symbol,omitempty"` + Address string `json:"address,omitempty"` + ImageUrl string `json:"image_url,omitempty"` + Decimals int32 `json:"decimals,omitempty"` + EthPrice string `json:"eth_price,omitempty"` + UsdPrice string `json:"usd_price,omitempty"` +} + diff --git a/nft/examples.json b/nft/examples.json index e957989..0eaec80 100644 --- a/nft/examples.json +++ b/nft/examples.json @@ -1,13 +1,105 @@ { - "vote": [ + "assets": [ { - "title": "Vote for the API", + "title": "Get a list of assets", "run_check": false, "request": { - "message": "Launch it!" + "order_by": "sale_date", + "limit": 1 }, "response": { - "message": "Thanks for the vote!" + "assets": [ + { + "id": 96959754, + "token_id": "24", + "name": "The Bitcoin Price Crash", + "description": "\"The Bitcoin Price Crash\" from CryptoCards collection", + "image_url": "https://lh3.googleusercontent.com/QKCsg3W-71eFbFSHl_4sVV89gxzZvgX8sls9r8PCyJIwYkEKVm1VJVcWdCEm5IxpqYgpcLDNm9JaiF13jKxWvOflVnYW5KIs8GhIdXg", + "sales": 7, + "permalink": "https://opensea.io/assets/0x3a7dc718eaf31f0a55988161f3d75d7ca785b034/24", + "contract": { + "name": "Cryptocards", + "address": "0x3a7dc718eaf31f0a55988161f3d75d7ca785b034", + "type": "semi-fungible", + "created_at": "2021-11-10T08:31:01.405462", + "owner": 63182848, + "schema": "ERC1155", + "symbol": "CC", + "description": "The Cryptocards collection was created at the dawn of what is considered cryptoart or NFTs today being minted on January 2018. \n\nIt is the only NFT collection chronicling the history of Bitcoin including over 50 cards with a total supply of 7.851.\n\nThe initial CryptoCards were issued as a ERC-20, but we developed an ERC1155 wrapper to make them available on OpenSea.\n\nBeware of other fake collections on OS. Stay safe and join the community: https://discord.gg/m9nUKEzcWJ", + "payout_address": "0xb7e0c211fb088e42aa9fd936b0a9c52985fbd273", + "seller_fees": "" + }, + "collection": { + "name": "The CryptoCards Collection (2018)", + "description": "The Cryptocards collection was created at the dawn of what is considered cryptoart or NFTs today being minted on January 2018. \n\nIt is the only NFT collection chronicling the history of Bitcoin including over 50 cards with a total supply of 7.851.\n\nThe initial CryptoCards were issued as a ERC-20, but we developed an ERC1155 wrapper to make them available on OpenSea.\n\nBeware of other fake collections on OS. Stay safe and join the community: https://discord.gg/m9nUKEzcWJ", + "slug": "cryptocards-collection", + "image_url": "https://lh3.googleusercontent.com/ByPl14lqRv2VsFm_drrTcxM7qE4pDRH3A3beQ2lPtes2gJ7gl4qFH1-v-8Vn7I47yLDN4F_QGLT1-Qqi1IIsuM20AkXZUKZX9l800LI=s120", + "created_at": "2021-11-10T08:44:55.811633", + "payout_address": "0xb7e0c211fb088e42aa9fd936b0a9c52985fbd273" + }, + "creator": { + "username": "", + "profile_url": "", + "address": "" + }, + "owner": { + "username": "NullAddress", + "profile_url": "https://storage.googleapis.com/opensea-static/opensea-profile/1.png", + "address": "0x0000000000000000000000000000000000000000" + }, + "presale": false, + "last_sale": { + "asset_token_id": "24", + "asset_decimals": 0, + "event_type": "successful", + "event_timestamp": "2021-12-08T13:52:33", + "total_price": "75000000000000000", + "quantity": "1", + "created_at": "2021-12-08T13:52:54.475811", + "transaction": { + "id": 218344593, + "timestamp": "2021-12-08T13:52:33", + "block_hash": "0xe168736f9f7f9bf610c04c0705165ef2ca470ddfc5fedb6d0337701f1d083d32", + "block_number": "13765159", + "from_account": { + "username": "austincountach", + "profile_url": "https://storage.googleapis.com/opensea-static/opensea-profile/31.png", + "address": "0x12ad5707401114453020a92a7384f267c9c559f2" + }, + "to_account": { + "username": "OpenSea-Orders", + "profile_url": "https://storage.googleapis.com/opensea-static/opensea-profile/22.png", + "address": "0x7be8076f4ea4a4ad08075c2508e481d6c946d12b" + }, + "transaction_hash": "0xd3bfa0ffe5d37af6854f485606ceb349634e957ff5778570ea396288fe8914db", + "transaction_index": "176" + }, + "payment_token": { + "id": 1, + "name": "Ether", + "symbol": "ETH", + "address": "0x0000000000000000000000000000000000000000", + "image_url": "https://storage.opensea.io/files/6f8e2979d428180222796ff4a33ab929.svg", + "decimals": 18, + "eth_price": "1.000000000000000", + "usd_price": "4274.930000000000291000" + } + }, + "listing_date": "" + } + ] + } + } + ], + "create": [ + { + "title": "Create an NFT", + "run_check": false, + "request": { + "name": "Guybrush Threepwood", + "description": "The epic monkey island character" + }, + "response": {} } } ] diff --git a/nft/handler/nft.go b/nft/handler/nft.go index 26efcfc..15f615c 100644 --- a/nft/handler/nft.go +++ b/nft/handler/nft.go @@ -2,49 +2,14 @@ package handler import ( "context" - "sync" - "time" - "github.com/micro/services/pkg/tenant" - "github.com/micro/micro/v3/service/store" pb "github.com/micro/services/nft/proto" ) type Nft struct{} -var ( - mtx sync.RWMutex - - voteKey = "votes/" -) - -type Vote struct { - Id string `json:"id"` - Message string `json:"message"` - VotedAt time.Time `json:"voted_at"` -} - -func (n *Nft) Vote(ctx context.Context, req *pb.VoteRequest, rsp *pb.VoteResponse) error { - mtx.Lock() - defer mtx.Unlock() - - id, ok := tenant.FromContext(ctx) - if !ok { - id = "micro" - } - - rec := store.NewRecord(voteKey + id, &Vote{ - Id: id, - Message: req.Message, - VotedAt: time.Now(), - }) - - // we don't need to check the error - store.Write(rec) - - rsp.Message = "Thanks for the vote!" +func (n *Nft) Assets(ctx context.Context, req *pb.AssetsRequest, rsp *pb.AssetsResponse) error { return nil } - diff --git a/nft/handler/opensea.go b/nft/handler/opensea.go new file mode 100644 index 0000000..27adae8 --- /dev/null +++ b/nft/handler/opensea.go @@ -0,0 +1,227 @@ +package handler + +import ( + "context" + "fmt" + + "github.com/micro/services/pkg/api" + "github.com/micro/services/nft/domain" + "github.com/micro/micro/v3/service/config" + "github.com/micro/micro/v3/service/logger" + "github.com/micro/micro/v3/service/errors" + pb "github.com/micro/services/nft/proto" +) + +// OpenSea handler +type OpenSea struct { + apiKey string + // embed nft api + *Nft +} + +var ( + openseaURL = "https://api.opensea.io/api/v1" +) + + +func New() *OpenSea { + v, err := config.Get("nft.key") + if err != nil { + logger.Fatal("nft.key config not found: %v", err) + } + + key := v.String("") + if len(key) == 0 { + logger.Fatal("nft.key config not found") + } + + // set the api key + api.SetKey("X-API-KEY", key) + + return &OpenSea { + apiKey: key, + Nft: new(Nft), + } +} + +func (o *OpenSea) Assets(ctx context.Context, req *pb.AssetsRequest, rsp *pb.AssetsResponse) error { + uri := openseaURL + "/assets" + params := "?" + + limit := int32(20) + offset := int32(0) + order := "desc" + orderBy := "" + + if req.Limit > 0 { + limit = req.Limit + } + + if req.Offset > 0 { + offset = req.Offset + } + + if req.Order == "asc" { + order = "asc" + } + + switch req.OrderBy { + case "sale_date", "sale_count", "sale_price", "total_price": + orderBy = req.OrderBy + } + + params += fmt.Sprintf("limit=%d&offset=%d&order_direction=%s", + limit, offset, order) + + if len(orderBy) > 0 { + params += "&order_by=" + orderBy + } + + if len(req.Collection) > 0 { + params += "&collection=" + req.Collection + } + + var resp domain.AssetResponse + + if err := api.Get(uri + params, &resp); err != nil { + return errors.InternalServerError("nft.assets", "failed to get assets: %v", err) + } + + for _, asset := range resp.Assets { + if asset.Creator == nil { + asset.Creator = &domain.User{ + User: &domain.Username{}, + } + } + if asset.Creator.User == nil { + asset.Creator.User = &domain.Username{} + } + if asset.Owner == nil { + asset.Owner = &domain.User{ + User: &domain.Username{}, + } + } + if asset.Owner.User == nil { + asset.Owner.User = &domain.Username{} + } + if asset.Collection == nil { + asset.Collection = new(domain.Collection) + } + if asset.Contract == nil { + asset.Contract = new(domain.Contract) + } + + lastSale := new(pb.Sale) + + if asset.LastSale != nil { + if asset.LastSale.Transaction == nil { + asset.LastSale.Transaction = &domain.Transaction{ + FromAccount: &domain.User{User: new(domain.Username)}, + ToAccount: &domain.User{User: new(domain.Username)}, + } + } + if asset.LastSale.Transaction.FromAccount == nil { + asset.LastSale.Transaction.FromAccount = &domain.User{User: new(domain.Username)} + } + if asset.LastSale.Transaction.FromAccount.User == nil { + asset.LastSale.Transaction.FromAccount.User = new(domain.Username) + } + if asset.LastSale.Transaction.ToAccount == nil { + asset.LastSale.Transaction.ToAccount = &domain.User{User: new(domain.Username)} + } + if asset.LastSale.Transaction.ToAccount.User == nil { + asset.LastSale.Transaction.ToAccount.User = new(domain.Username) + } + if asset.LastSale.PaymentToken == nil { + asset.LastSale.PaymentToken = new(domain.Token) + } + + lastSale = &pb.Sale{ + AssetTokenId: asset.LastSale.Asset.TokenId, + AssetDecimals: asset.LastSale.Asset.Decimals, + EventType: asset.LastSale.EventType, + EventTimestamp: asset.LastSale.EventTimestamp, + TotalPrice: asset.LastSale.TotalPrice, + Quantity: asset.LastSale.Quantity, + CreatedAt: asset.LastSale.CreatedAt, + Transaction: &pb.Transaction{ + Id: asset.LastSale.Transaction.Id, + Timestamp: asset.LastSale.Transaction.Timestamp, + BlockHash: asset.LastSale.Transaction.BlockHash, + BlockNumber: asset.LastSale.Transaction.BlockNumber, + FromAccount: &pb.User{ + Username: asset.LastSale.Transaction.FromAccount.User.Username, + ProfileUrl: asset.LastSale.Transaction.FromAccount.ProfileUrl, + Address: asset.LastSale.Transaction.FromAccount.Address, + }, + ToAccount: &pb.User{ + Username: asset.LastSale.Transaction.ToAccount.User.Username, + ProfileUrl: asset.LastSale.Transaction.ToAccount.ProfileUrl, + Address: asset.LastSale.Transaction.ToAccount.Address, + }, + TransactionHash: asset.LastSale.Transaction.TransactionHash, + TransactionIndex: asset.LastSale.Transaction.TransactionIndex, + }, + PaymentToken: &pb.Token{ + Id: asset.LastSale.PaymentToken.Id, + Name: asset.LastSale.PaymentToken.Name, + Symbol: asset.LastSale.PaymentToken.Symbol, + Address: asset.LastSale.PaymentToken.Address, + ImageUrl: asset.LastSale.PaymentToken.ImageUrl, + Decimals: asset.LastSale.PaymentToken.Decimals, + EthPrice: asset.LastSale.PaymentToken.EthPrice, + UsdPrice: asset.LastSale.PaymentToken.UsdPrice, + }, + } + } + + rsp.Assets = append(rsp.Assets, &pb.Asset{ + Name: asset.Name, + Description: asset.Description, + Id: asset.Id, + TokenId: asset.TokenId, + ImageUrl: asset.ImageUrl, + Sales: asset.Sales, + Permalink: asset.Permalink, + Contract: &pb.Contract{ + Name: asset.Contract.Name, + Description: asset.Contract.Description, + Address: asset.Contract.Address, + Type: asset.Contract.Type, + CreatedAt: asset.Contract.CreatedAt, + Owner: asset.Contract.Owner, + Schema: asset.Contract.Schema, + Symbol: asset.Contract.Symbol, + PayoutAddress: asset.Contract.PayoutAddress, + SellerFees: asset.Contract.SellerFees, + }, + Collection: &pb.Collection{ + Name: asset.Collection.Name, + Description: asset.Collection.Description, + Slug: asset.Collection.Slug, + ImageUrl: asset.Collection.ImageUrl, + CreatedAt: asset.Collection.CreatedAt, + PayoutAddress: asset.Collection.PayoutAddress, + }, + Owner: &pb.User{ + Username: asset.Owner.User.Username, + ProfileUrl: asset.Owner.ProfileUrl, + Address: asset.Owner.Address, + }, + Creator: &pb.User{ + Username: asset.Creator.User.Username, + ProfileUrl: asset.Creator.ProfileUrl, + Address: asset.Creator.Address, + }, + LastSale: lastSale, + Presale: asset.Presale, + ListingDate: asset.ListingDate, + }) + } + + return nil +} + +func (o *OpenSea) Create(ctx context.Context, req *pb.CreateRequest, rsp *pb.CreateResponse) error { + return errors.BadRequest("nft.create", "coming soon") +} diff --git a/nft/main.go b/nft/main.go index c8843fd..7abe2cd 100644 --- a/nft/main.go +++ b/nft/main.go @@ -15,7 +15,7 @@ func main() { ) // Register handler - pb.RegisterNftHandler(srv.Server(), new(handler.Nft)) + pb.RegisterNftHandler(srv.Server(), handler.New()) // Run service if err := srv.Run(); err != nil { diff --git a/nft/proto/nft.pb.go b/nft/proto/nft.pb.go index 4bb0bd1..a71293e 100644 --- a/nft/proto/nft.pb.go +++ b/nft/proto/nft.pb.go @@ -20,18 +20,24 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -// Vote to have the NFT api launched faster! -type VoteRequest struct { +// Create your own NFT (coming soon) +type CreateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // optional message - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + // name of the NFT + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // description + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + // image data + Image []byte `protobuf:"bytes,3,opt,name=image,proto3" json:"image,omitempty"` + // data if not image + Data []byte `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` } -func (x *VoteRequest) Reset() { - *x = VoteRequest{} +func (x *CreateRequest) Reset() { + *x = CreateRequest{} if protoimpl.UnsafeEnabled { mi := &file_proto_nft_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -39,13 +45,13 @@ func (x *VoteRequest) Reset() { } } -func (x *VoteRequest) String() string { +func (x *CreateRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*VoteRequest) ProtoMessage() {} +func (*CreateRequest) ProtoMessage() {} -func (x *VoteRequest) ProtoReflect() protoreflect.Message { +func (x *CreateRequest) ProtoReflect() protoreflect.Message { mi := &file_proto_nft_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -57,29 +63,49 @@ func (x *VoteRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use VoteRequest.ProtoReflect.Descriptor instead. -func (*VoteRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateRequest.ProtoReflect.Descriptor instead. +func (*CreateRequest) Descriptor() ([]byte, []int) { return file_proto_nft_proto_rawDescGZIP(), []int{0} } -func (x *VoteRequest) GetMessage() string { +func (x *CreateRequest) GetName() string { if x != nil { - return x.Message + return x.Name } return "" } -type VoteResponse struct { +func (x *CreateRequest) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *CreateRequest) GetImage() []byte { + if x != nil { + return x.Image + } + return nil +} + +func (x *CreateRequest) GetData() []byte { + if x != nil { + return x.Data + } + return nil +} + +type CreateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // response message - Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + Asset *Asset `protobuf:"bytes,1,opt,name=asset,proto3" json:"asset,omitempty"` } -func (x *VoteResponse) Reset() { - *x = VoteResponse{} +func (x *CreateResponse) Reset() { + *x = CreateResponse{} if protoimpl.UnsafeEnabled { mi := &file_proto_nft_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -87,13 +113,13 @@ func (x *VoteResponse) Reset() { } } -func (x *VoteResponse) String() string { +func (x *CreateResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*VoteResponse) ProtoMessage() {} +func (*CreateResponse) ProtoMessage() {} -func (x *VoteResponse) ProtoReflect() protoreflect.Message { +func (x *CreateResponse) ProtoReflect() protoreflect.Message { mi := &file_proto_nft_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -105,33 +131,1066 @@ func (x *VoteResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use VoteResponse.ProtoReflect.Descriptor instead. -func (*VoteResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use CreateResponse.ProtoReflect.Descriptor instead. +func (*CreateResponse) Descriptor() ([]byte, []int) { return file_proto_nft_proto_rawDescGZIP(), []int{1} } -func (x *VoteResponse) GetMessage() string { +func (x *CreateResponse) GetAsset() *Asset { if x != nil { - return x.Message + return x.Asset + } + return nil +} + +type Asset struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // id of the asset + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + // the token id + TokenId string `protobuf:"bytes,2,opt,name=token_id,json=tokenId,proto3" json:"token_id,omitempty"` + // name of the asset + Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"` + // related description + Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"` + // the image url + ImageUrl string `protobuf:"bytes,5,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"` + // number of sales + Sales int32 `protobuf:"varint,6,opt,name=sales,proto3" json:"sales,omitempty"` + // the permalink + Permalink string `protobuf:"bytes,7,opt,name=permalink,proto3" json:"permalink,omitempty"` + // asset contract + Contract *Contract `protobuf:"bytes,8,opt,name=contract,proto3" json:"contract,omitempty"` + // associated collection + Collection *Collection `protobuf:"bytes,9,opt,name=collection,proto3" json:"collection,omitempty"` + // Creator of the NFT + Creator *User `protobuf:"bytes,10,opt,name=creator,proto3" json:"creator,omitempty"` + // Owner of the NFT + Owner *User `protobuf:"bytes,11,opt,name=owner,proto3" json:"owner,omitempty"` + // is it a presale + Presale bool `protobuf:"varint,12,opt,name=presale,proto3" json:"presale,omitempty"` + // last time sold + LastSale *Sale `protobuf:"bytes,13,opt,name=last_sale,json=lastSale,proto3" json:"last_sale,omitempty"` + // listing date + ListingDate string `protobuf:"bytes,14,opt,name=listing_date,json=listingDate,proto3" json:"listing_date,omitempty"` +} + +func (x *Asset) Reset() { + *x = Asset{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Asset) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Asset) ProtoMessage() {} + +func (x *Asset) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Asset.ProtoReflect.Descriptor instead. +func (*Asset) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{2} +} + +func (x *Asset) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Asset) GetTokenId() string { + if x != nil { + return x.TokenId } return "" } +func (x *Asset) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Asset) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Asset) GetImageUrl() string { + if x != nil { + return x.ImageUrl + } + return "" +} + +func (x *Asset) GetSales() int32 { + if x != nil { + return x.Sales + } + return 0 +} + +func (x *Asset) GetPermalink() string { + if x != nil { + return x.Permalink + } + return "" +} + +func (x *Asset) GetContract() *Contract { + if x != nil { + return x.Contract + } + return nil +} + +func (x *Asset) GetCollection() *Collection { + if x != nil { + return x.Collection + } + return nil +} + +func (x *Asset) GetCreator() *User { + if x != nil { + return x.Creator + } + return nil +} + +func (x *Asset) GetOwner() *User { + if x != nil { + return x.Owner + } + return nil +} + +func (x *Asset) GetPresale() bool { + if x != nil { + return x.Presale + } + return false +} + +func (x *Asset) GetLastSale() *Sale { + if x != nil { + return x.LastSale + } + return nil +} + +func (x *Asset) GetListingDate() string { + if x != nil { + return x.ListingDate + } + return "" +} + +type Contract struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // name of contract + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // ethereum address + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + // type of contract e.g "semi-fungible" + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + // timestamp of creation + CreatedAt string `protobuf:"bytes,4,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + // owner id + Owner int32 `protobuf:"varint,5,opt,name=owner,proto3" json:"owner,omitempty"` + // aka "ERC1155" + Schema string `protobuf:"bytes,6,opt,name=schema,proto3" json:"schema,omitempty"` + // related symbol + Symbol string `protobuf:"bytes,7,opt,name=symbol,proto3" json:"symbol,omitempty"` + // description of contract + Description string `protobuf:"bytes,8,opt,name=description,proto3" json:"description,omitempty"` + // payout address + PayoutAddress string `protobuf:"bytes,9,opt,name=payout_address,json=payoutAddress,proto3" json:"payout_address,omitempty"` + // seller fees + SellerFees string `protobuf:"bytes,10,opt,name=seller_fees,json=sellerFees,proto3" json:"seller_fees,omitempty"` +} + +func (x *Contract) Reset() { + *x = Contract{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Contract) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Contract) ProtoMessage() {} + +func (x *Contract) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Contract.ProtoReflect.Descriptor instead. +func (*Contract) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{3} +} + +func (x *Contract) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Contract) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *Contract) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *Contract) GetCreatedAt() string { + if x != nil { + return x.CreatedAt + } + return "" +} + +func (x *Contract) GetOwner() int32 { + if x != nil { + return x.Owner + } + return 0 +} + +func (x *Contract) GetSchema() string { + if x != nil { + return x.Schema + } + return "" +} + +func (x *Contract) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *Contract) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Contract) GetPayoutAddress() string { + if x != nil { + return x.PayoutAddress + } + return "" +} + +func (x *Contract) GetSellerFees() string { + if x != nil { + return x.SellerFees + } + return "" +} + +type Collection struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Description string `protobuf:"bytes,2,opt,name=description,proto3" json:"description,omitempty"` + Slug string `protobuf:"bytes,3,opt,name=slug,proto3" json:"slug,omitempty"` + ImageUrl string `protobuf:"bytes,4,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"` + CreatedAt string `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + PayoutAddress string `protobuf:"bytes,6,opt,name=payout_address,json=payoutAddress,proto3" json:"payout_address,omitempty"` +} + +func (x *Collection) Reset() { + *x = Collection{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Collection) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Collection) ProtoMessage() {} + +func (x *Collection) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Collection.ProtoReflect.Descriptor instead. +func (*Collection) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{4} +} + +func (x *Collection) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Collection) GetDescription() string { + if x != nil { + return x.Description + } + return "" +} + +func (x *Collection) GetSlug() string { + if x != nil { + return x.Slug + } + return "" +} + +func (x *Collection) GetImageUrl() string { + if x != nil { + return x.ImageUrl + } + return "" +} + +func (x *Collection) GetCreatedAt() string { + if x != nil { + return x.CreatedAt + } + return "" +} + +func (x *Collection) GetPayoutAddress() string { + if x != nil { + return x.PayoutAddress + } + return "" +} + +type User struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"` + ProfileUrl string `protobuf:"bytes,2,opt,name=profile_url,json=profileUrl,proto3" json:"profile_url,omitempty"` + Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"` +} + +func (x *User) Reset() { + *x = User{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *User) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*User) ProtoMessage() {} + +func (x *User) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use User.ProtoReflect.Descriptor instead. +func (*User) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{5} +} + +func (x *User) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *User) GetProfileUrl() string { + if x != nil { + return x.ProfileUrl + } + return "" +} + +func (x *User) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type Sale struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AssetTokenId string `protobuf:"bytes,1,opt,name=asset_token_id,json=assetTokenId,proto3" json:"asset_token_id,omitempty"` + AssetDecimals int32 `protobuf:"varint,2,opt,name=asset_decimals,json=assetDecimals,proto3" json:"asset_decimals,omitempty"` + EventType string `protobuf:"bytes,3,opt,name=event_type,json=eventType,proto3" json:"event_type,omitempty"` + EventTimestamp string `protobuf:"bytes,4,opt,name=event_timestamp,json=eventTimestamp,proto3" json:"event_timestamp,omitempty"` + TotalPrice string `protobuf:"bytes,5,opt,name=total_price,json=totalPrice,proto3" json:"total_price,omitempty"` + Quantity string `protobuf:"bytes,6,opt,name=quantity,proto3" json:"quantity,omitempty"` + CreatedAt string `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"` + Transaction *Transaction `protobuf:"bytes,8,opt,name=transaction,proto3" json:"transaction,omitempty"` + PaymentToken *Token `protobuf:"bytes,9,opt,name=payment_token,json=paymentToken,proto3" json:"payment_token,omitempty"` +} + +func (x *Sale) Reset() { + *x = Sale{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Sale) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Sale) ProtoMessage() {} + +func (x *Sale) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Sale.ProtoReflect.Descriptor instead. +func (*Sale) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{6} +} + +func (x *Sale) GetAssetTokenId() string { + if x != nil { + return x.AssetTokenId + } + return "" +} + +func (x *Sale) GetAssetDecimals() int32 { + if x != nil { + return x.AssetDecimals + } + return 0 +} + +func (x *Sale) GetEventType() string { + if x != nil { + return x.EventType + } + return "" +} + +func (x *Sale) GetEventTimestamp() string { + if x != nil { + return x.EventTimestamp + } + return "" +} + +func (x *Sale) GetTotalPrice() string { + if x != nil { + return x.TotalPrice + } + return "" +} + +func (x *Sale) GetQuantity() string { + if x != nil { + return x.Quantity + } + return "" +} + +func (x *Sale) GetCreatedAt() string { + if x != nil { + return x.CreatedAt + } + return "" +} + +func (x *Sale) GetTransaction() *Transaction { + if x != nil { + return x.Transaction + } + return nil +} + +func (x *Sale) GetPaymentToken() *Token { + if x != nil { + return x.PaymentToken + } + return nil +} + +type Transaction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Timestamp string `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + BlockHash string `protobuf:"bytes,3,opt,name=block_hash,json=blockHash,proto3" json:"block_hash,omitempty"` + BlockNumber string `protobuf:"bytes,4,opt,name=block_number,json=blockNumber,proto3" json:"block_number,omitempty"` + FromAccount *User `protobuf:"bytes,5,opt,name=from_account,json=fromAccount,proto3" json:"from_account,omitempty"` + ToAccount *User `protobuf:"bytes,6,opt,name=to_account,json=toAccount,proto3" json:"to_account,omitempty"` + TransactionHash string `protobuf:"bytes,7,opt,name=transaction_hash,json=transactionHash,proto3" json:"transaction_hash,omitempty"` + TransactionIndex string `protobuf:"bytes,8,opt,name=transaction_index,json=transactionIndex,proto3" json:"transaction_index,omitempty"` +} + +func (x *Transaction) Reset() { + *x = Transaction{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Transaction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Transaction) ProtoMessage() {} + +func (x *Transaction) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Transaction.ProtoReflect.Descriptor instead. +func (*Transaction) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{7} +} + +func (x *Transaction) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Transaction) GetTimestamp() string { + if x != nil { + return x.Timestamp + } + return "" +} + +func (x *Transaction) GetBlockHash() string { + if x != nil { + return x.BlockHash + } + return "" +} + +func (x *Transaction) GetBlockNumber() string { + if x != nil { + return x.BlockNumber + } + return "" +} + +func (x *Transaction) GetFromAccount() *User { + if x != nil { + return x.FromAccount + } + return nil +} + +func (x *Transaction) GetToAccount() *User { + if x != nil { + return x.ToAccount + } + return nil +} + +func (x *Transaction) GetTransactionHash() string { + if x != nil { + return x.TransactionHash + } + return "" +} + +func (x *Transaction) GetTransactionIndex() string { + if x != nil { + return x.TransactionIndex + } + return "" +} + +type Token struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Symbol string `protobuf:"bytes,3,opt,name=symbol,proto3" json:"symbol,omitempty"` + Address string `protobuf:"bytes,4,opt,name=address,proto3" json:"address,omitempty"` + ImageUrl string `protobuf:"bytes,5,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"` + Decimals int32 `protobuf:"varint,6,opt,name=decimals,proto3" json:"decimals,omitempty"` + EthPrice string `protobuf:"bytes,7,opt,name=eth_price,json=ethPrice,proto3" json:"eth_price,omitempty"` + UsdPrice string `protobuf:"bytes,8,opt,name=usd_price,json=usdPrice,proto3" json:"usd_price,omitempty"` +} + +func (x *Token) Reset() { + *x = Token{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Token) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Token) ProtoMessage() {} + +func (x *Token) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Token.ProtoReflect.Descriptor instead. +func (*Token) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{8} +} + +func (x *Token) GetId() int32 { + if x != nil { + return x.Id + } + return 0 +} + +func (x *Token) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Token) GetSymbol() string { + if x != nil { + return x.Symbol + } + return "" +} + +func (x *Token) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *Token) GetImageUrl() string { + if x != nil { + return x.ImageUrl + } + return "" +} + +func (x *Token) GetDecimals() int32 { + if x != nil { + return x.Decimals + } + return 0 +} + +func (x *Token) GetEthPrice() string { + if x != nil { + return x.EthPrice + } + return "" +} + +func (x *Token) GetUsdPrice() string { + if x != nil { + return x.UsdPrice + } + return "" +} + +// Return a list of NFT assets +type AssetsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // limit returned assets + Limit int32 `protobuf:"varint,1,opt,name=limit,proto3" json:"limit,omitempty"` + // offset for pagination + Offset int32 `protobuf:"varint,2,opt,name=offset,proto3" json:"offset,omitempty"` + // order "asc" or "desc" + Order string `protobuf:"bytes,3,opt,name=order,proto3" json:"order,omitempty"` + // order by "sale_date", "sale_count", "sale_price", "total_price" + OrderBy string `protobuf:"bytes,4,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"` + // limit to members of a collection by slug name (case sensitive) + Collection string `protobuf:"bytes,5,opt,name=collection,proto3" json:"collection,omitempty"` +} + +func (x *AssetsRequest) Reset() { + *x = AssetsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AssetsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssetsRequest) ProtoMessage() {} + +func (x *AssetsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AssetsRequest.ProtoReflect.Descriptor instead. +func (*AssetsRequest) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{9} +} + +func (x *AssetsRequest) GetLimit() int32 { + if x != nil { + return x.Limit + } + return 0 +} + +func (x *AssetsRequest) GetOffset() int32 { + if x != nil { + return x.Offset + } + return 0 +} + +func (x *AssetsRequest) GetOrder() string { + if x != nil { + return x.Order + } + return "" +} + +func (x *AssetsRequest) GetOrderBy() string { + if x != nil { + return x.OrderBy + } + return "" +} + +func (x *AssetsRequest) GetCollection() string { + if x != nil { + return x.Collection + } + return "" +} + +type AssetsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // list of assets + Assets []*Asset `protobuf:"bytes,1,rep,name=assets,proto3" json:"assets,omitempty"` +} + +func (x *AssetsResponse) Reset() { + *x = AssetsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_nft_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AssetsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AssetsResponse) ProtoMessage() {} + +func (x *AssetsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_nft_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AssetsResponse.ProtoReflect.Descriptor instead. +func (*AssetsResponse) Descriptor() ([]byte, []int) { + return file_proto_nft_proto_rawDescGZIP(), []int{10} +} + +func (x *AssetsResponse) GetAssets() []*Asset { + if x != nil { + return x.Assets + } + return nil +} + var File_proto_nft_proto protoreflect.FileDescriptor var file_proto_nft_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6e, 0x66, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x03, 0x6e, 0x66, 0x74, 0x22, 0x27, 0x0a, 0x0b, 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x28, 0x0a, 0x0c, 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x34, 0x0a, 0x03, 0x4e, 0x66, 0x74, - 0x12, 0x2d, 0x0a, 0x04, 0x56, 0x6f, 0x74, 0x65, 0x12, 0x10, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x56, - 0x6f, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x6e, 0x66, 0x74, - 0x2e, 0x56, 0x6f, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, - 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x6e, 0x66, 0x74, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x12, 0x03, 0x6e, 0x66, 0x74, 0x22, 0x6f, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, + 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x69, 0x6d, + 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x32, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x05, 0x61, 0x73, 0x73, + 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x41, + 0x73, 0x73, 0x65, 0x74, 0x52, 0x05, 0x61, 0x73, 0x73, 0x65, 0x74, 0x22, 0xc0, 0x03, 0x0a, 0x05, + 0x41, 0x73, 0x73, 0x65, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, + 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x55, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x61, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x05, 0x73, 0x61, 0x6c, 0x65, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, + 0x6d, 0x61, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x65, + 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x29, 0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x61, 0x63, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x6e, 0x66, 0x74, 0x2e, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x52, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, + 0x63, 0x74, 0x12, 0x2f, 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, + 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1f, 0x0a, 0x05, 0x6f, 0x77, 0x6e, 0x65, + 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x55, 0x73, + 0x65, 0x72, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x65, + 0x73, 0x61, 0x6c, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x72, 0x65, 0x73, + 0x61, 0x6c, 0x65, 0x12, 0x26, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x61, 0x6c, 0x65, + 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x53, 0x61, 0x6c, + 0x65, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x61, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x6c, + 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x22, 0x9b, + 0x02, 0x0a, 0x08, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x14, 0x0a, 0x05, + 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, + 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, + 0x6f, 0x6c, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x5f, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, + 0x79, 0x6f, 0x75, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x73, + 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x66, 0x65, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x73, 0x65, 0x6c, 0x6c, 0x65, 0x72, 0x46, 0x65, 0x65, 0x73, 0x22, 0xb9, 0x01, 0x0a, + 0x0a, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x73, 0x6c, 0x75, 0x67, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x75, + 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x55, + 0x72, 0x6c, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x70, 0x61, 0x79, 0x6f, 0x75, + 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x5d, 0x0a, 0x04, 0x55, 0x73, 0x65, 0x72, + 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x18, 0x0a, + 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0xdc, 0x02, 0x0a, 0x04, 0x53, 0x61, 0x6c, 0x65, + 0x12, 0x24, 0x0a, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x73, 0x73, 0x65, 0x74, 0x54, + 0x6f, 0x6b, 0x65, 0x6e, 0x49, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, + 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x44, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x12, 0x1d, 0x0a, + 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x27, 0x0a, 0x0f, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, + 0x72, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x71, 0x75, 0x61, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x32, 0x0a, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2f, 0x0a, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6e, + 0x66, 0x74, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0xad, 0x02, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x48, + 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x0c, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x61, + 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6e, + 0x66, 0x74, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0a, 0x74, 0x6f, 0x5f, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x09, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x55, + 0x73, 0x65, 0x72, 0x52, 0x09, 0x74, 0x6f, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x29, + 0x0a, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x61, 0x73, 0x68, 0x12, 0x2b, 0x0a, 0x11, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x22, 0xd0, 0x01, 0x0a, 0x05, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, + 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x55, 0x72, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x12, + 0x1b, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x65, 0x74, 0x68, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x09, + 0x75, 0x73, 0x64, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x75, 0x73, 0x64, 0x50, 0x72, 0x69, 0x63, 0x65, 0x22, 0x8e, 0x01, 0x0a, 0x0d, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x12, + 0x19, 0x0a, 0x08, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x34, 0x0a, 0x0e, 0x41, 0x73, + 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x06, + 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e, 0x6e, + 0x66, 0x74, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x52, 0x06, 0x61, 0x73, 0x73, 0x65, 0x74, 0x73, + 0x32, 0x6f, 0x0a, 0x03, 0x4e, 0x66, 0x74, 0x12, 0x33, 0x0a, 0x06, 0x41, 0x73, 0x73, 0x65, 0x74, + 0x73, 0x12, 0x12, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x41, 0x73, 0x73, 0x65, + 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x33, 0x0a, 0x06, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x6e, 0x66, 0x74, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x66, 0x74, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x6e, 0x66, 0x74, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -146,19 +1205,41 @@ func file_proto_nft_proto_rawDescGZIP() []byte { return file_proto_nft_proto_rawDescData } -var file_proto_nft_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_proto_nft_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_proto_nft_proto_goTypes = []interface{}{ - (*VoteRequest)(nil), // 0: nft.VoteRequest - (*VoteResponse)(nil), // 1: nft.VoteResponse + (*CreateRequest)(nil), // 0: nft.CreateRequest + (*CreateResponse)(nil), // 1: nft.CreateResponse + (*Asset)(nil), // 2: nft.Asset + (*Contract)(nil), // 3: nft.Contract + (*Collection)(nil), // 4: nft.Collection + (*User)(nil), // 5: nft.User + (*Sale)(nil), // 6: nft.Sale + (*Transaction)(nil), // 7: nft.Transaction + (*Token)(nil), // 8: nft.Token + (*AssetsRequest)(nil), // 9: nft.AssetsRequest + (*AssetsResponse)(nil), // 10: nft.AssetsResponse } var file_proto_nft_proto_depIdxs = []int32{ - 0, // 0: nft.Nft.Vote:input_type -> nft.VoteRequest - 1, // 1: nft.Nft.Vote:output_type -> nft.VoteResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 2, // 0: nft.CreateResponse.asset:type_name -> nft.Asset + 3, // 1: nft.Asset.contract:type_name -> nft.Contract + 4, // 2: nft.Asset.collection:type_name -> nft.Collection + 5, // 3: nft.Asset.creator:type_name -> nft.User + 5, // 4: nft.Asset.owner:type_name -> nft.User + 6, // 5: nft.Asset.last_sale:type_name -> nft.Sale + 7, // 6: nft.Sale.transaction:type_name -> nft.Transaction + 8, // 7: nft.Sale.payment_token:type_name -> nft.Token + 5, // 8: nft.Transaction.from_account:type_name -> nft.User + 5, // 9: nft.Transaction.to_account:type_name -> nft.User + 2, // 10: nft.AssetsResponse.assets:type_name -> nft.Asset + 9, // 11: nft.Nft.Assets:input_type -> nft.AssetsRequest + 0, // 12: nft.Nft.Create:input_type -> nft.CreateRequest + 10, // 13: nft.Nft.Assets:output_type -> nft.AssetsResponse + 1, // 14: nft.Nft.Create:output_type -> nft.CreateResponse + 13, // [13:15] is the sub-list for method output_type + 11, // [11:13] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_proto_nft_proto_init() } @@ -168,7 +1249,7 @@ func file_proto_nft_proto_init() { } if !protoimpl.UnsafeEnabled { file_proto_nft_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VoteRequest); i { + switch v := v.(*CreateRequest); i { case 0: return &v.state case 1: @@ -180,7 +1261,115 @@ func file_proto_nft_proto_init() { } } file_proto_nft_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VoteResponse); i { + switch v := v.(*CreateResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Asset); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Contract); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Collection); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*User); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Sale); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Transaction); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Token); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AssetsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_nft_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AssetsResponse); i { case 0: return &v.state case 1: @@ -198,7 +1387,7 @@ func file_proto_nft_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_nft_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 11, NumExtensions: 0, NumServices: 1, }, diff --git a/nft/proto/nft.pb.micro.go b/nft/proto/nft.pb.micro.go index 8075ddc..03c34f9 100644 --- a/nft/proto/nft.pb.micro.go +++ b/nft/proto/nft.pb.micro.go @@ -42,7 +42,8 @@ func NewNftEndpoints() []*api.Endpoint { // Client API for Nft service type NftService interface { - Vote(ctx context.Context, in *VoteRequest, opts ...client.CallOption) (*VoteResponse, error) + Assets(ctx context.Context, in *AssetsRequest, opts ...client.CallOption) (*AssetsResponse, error) + Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) } type nftService struct { @@ -57,9 +58,19 @@ func NewNftService(name string, c client.Client) NftService { } } -func (c *nftService) Vote(ctx context.Context, in *VoteRequest, opts ...client.CallOption) (*VoteResponse, error) { - req := c.c.NewRequest(c.name, "Nft.Vote", in) - out := new(VoteResponse) +func (c *nftService) Assets(ctx context.Context, in *AssetsRequest, opts ...client.CallOption) (*AssetsResponse, error) { + req := c.c.NewRequest(c.name, "Nft.Assets", in) + out := new(AssetsResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *nftService) Create(ctx context.Context, in *CreateRequest, opts ...client.CallOption) (*CreateResponse, error) { + req := c.c.NewRequest(c.name, "Nft.Create", in) + out := new(CreateResponse) err := c.c.Call(ctx, req, out, opts...) if err != nil { return nil, err @@ -70,12 +81,14 @@ func (c *nftService) Vote(ctx context.Context, in *VoteRequest, opts ...client.C // Server API for Nft service type NftHandler interface { - Vote(context.Context, *VoteRequest, *VoteResponse) error + Assets(context.Context, *AssetsRequest, *AssetsResponse) error + Create(context.Context, *CreateRequest, *CreateResponse) error } func RegisterNftHandler(s server.Server, hdlr NftHandler, opts ...server.HandlerOption) error { type nft interface { - Vote(ctx context.Context, in *VoteRequest, out *VoteResponse) error + Assets(ctx context.Context, in *AssetsRequest, out *AssetsResponse) error + Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error } type Nft struct { nft @@ -88,6 +101,10 @@ type nftHandler struct { NftHandler } -func (h *nftHandler) Vote(ctx context.Context, in *VoteRequest, out *VoteResponse) error { - return h.NftHandler.Vote(ctx, in, out) +func (h *nftHandler) Assets(ctx context.Context, in *AssetsRequest, out *AssetsResponse) error { + return h.NftHandler.Assets(ctx, in, out) +} + +func (h *nftHandler) Create(ctx context.Context, in *CreateRequest, out *CreateResponse) error { + return h.NftHandler.Create(ctx, in, out) } diff --git a/nft/proto/nft.proto b/nft/proto/nft.proto index 39fb5c7..de4ad46 100644 --- a/nft/proto/nft.proto +++ b/nft/proto/nft.proto @@ -5,16 +5,144 @@ package nft; option go_package = "./proto;nft"; service Nft { - rpc Vote(VoteRequest) returns (VoteResponse) {} + rpc Assets(AssetsRequest) returns (AssetsResponse) {} + rpc Create(CreateRequest) returns (CreateResponse) {} } -// Vote to have the NFT api launched faster! -message VoteRequest { - // optional message - string message = 1; +// Create your own NFT (coming soon) +message CreateRequest { + // name of the NFT + string name = 1; + // description + string description = 2; + // image data + bytes image = 3; + // data if not image + bytes data = 4; } -message VoteResponse { - // response message - string message = 2; +message CreateResponse { + Asset asset = 1; +} + +message Asset { + // id of the asset + int32 id = 1; + // the token id + string token_id = 2; + // name of the asset + string name = 3; + // related description + string description = 4; + // the image url + string image_url = 5; + // number of sales + int32 sales = 6; + // the permalink + string permalink = 7; + // asset contract + Contract contract = 8; + // associated collection + Collection collection = 9; + // Creator of the NFT + User creator = 10; + // Owner of the NFT + User owner = 11; + // is it a presale + bool presale = 12; + // last time sold + Sale last_sale = 13; + // listing date + string listing_date = 14; +} + +message Contract { + // name of contract + string name = 1; + // ethereum address + string address = 2; + // type of contract e.g "semi-fungible" + string type = 3; + // timestamp of creation + string created_at = 4; + // owner id + int32 owner = 5; + // aka "ERC1155" + string schema = 6; + // related symbol + string symbol = 7; + // description of contract + string description = 8; + // payout address + string payout_address = 9; + // seller fees + string seller_fees = 10; +} + +message Collection { + string name = 1; + string description = 2; + string slug = 3; + string image_url = 4; + string created_at = 5; + string payout_address = 6; +} + +message User { + string username = 1; + string profile_url = 2; + string address = 3; +} + +message Sale { + string asset_token_id = 1; + int32 asset_decimals = 2; + string event_type = 3; + string event_timestamp = 4; + string total_price = 5; + string quantity = 6; + string created_at = 7; + Transaction transaction = 8; + Token payment_token = 9; +} + +message Transaction { + int32 id = 1; + string timestamp = 2; + string block_hash = 3; + string block_number = 4; + User from_account = 5; + User to_account = 6; + string transaction_hash = 7; + string transaction_index = 8; +} + +message Token { + int32 id = 1; + string name = 2; + string symbol = 3; + string address = 4; + string image_url = 5; + int32 decimals = 6; + string eth_price = 7; + string usd_price = 8; +} + +// Return a list of NFT assets +message AssetsRequest { + // limit returned assets + int32 limit = 1; + // offset for pagination + int32 offset = 2; + // order "asc" or "desc" + string order = 3; + // order by "sale_date", "sale_count", "sale_price", "total_price" + string order_by = 4; + // limit to members of a collection by slug name (case sensitive) + string collection = 5; +} + +message AssetsResponse { + // list of assets + repeated Asset assets = 1; } diff --git a/nft/publicapi.json b/nft/publicapi.json index 844710a..974d5fd 100644 --- a/nft/publicapi.json +++ b/nft/publicapi.json @@ -1,6 +1,6 @@ { "name": "nft", "icon": "🪙", - "category": "coming soon", - "display_name": "NFTs (Coming Soon)" + "category": "crypto", + "display_name": "NFTs" }