diff --git a/cmd/live.go b/cmd/live.go index 7877c4b..edc08da 100644 --- a/cmd/live.go +++ b/cmd/live.go @@ -16,8 +16,6 @@ import ( "github.com/benleb/gloomberg/internal/gbl" "github.com/benleb/gloomberg/internal/jobs" "github.com/benleb/gloomberg/internal/nemo/gloomberg" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/remote" "github.com/benleb/gloomberg/internal/nemo/provider" "github.com/benleb/gloomberg/internal/nemo/token" "github.com/benleb/gloomberg/internal/nemo/totra" @@ -95,12 +93,10 @@ func runGloomberg(_ *cobra.Command, _ []string) { gloomberg.PrModf("exp", "active experiments ๐Ÿงช %s", style.BoldAlmostWhite(strings.Join(activeExperiments.ToSlice(), style.GrayStyle.Render(" ยท ")))) - go func() { - gb.DegenDB = degendb.NewDegenDB() - }() - - // cleanup for redis db/cache - // defer gb.Rdb.Close() + // disabled for now + // go func() { + // gb.DegenDB = degendb.NewDegenDB() + // }() // compatibility with old config key var providerConfig interface{} @@ -374,14 +370,10 @@ func runGloomberg(_ *cobra.Command, _ []string) { // // subscribe to OpenSea API - if viper.GetBool("seawatcher.local") || viper.GetBool("grpc.client.enabled") || viper.GetBool("pubsub.client.enabled") { + if viper.GetBool("seawatcher.local") || viper.GetBool("pubsub.client.enabled") { go trapri.SeaWatcherEventsHandler(gb) } - if viper.GetBool("grpc.server.enabled") { - gbgrpc.StartServer(gb, seawa) - } - // // subscribe to redis pubsub channel to receive events from gloomberg central if viper.GetBool("pubsub.client.enabled") { @@ -391,12 +383,12 @@ func runGloomberg(_ *cobra.Command, _ []string) { go pusu.SubscribeToListingsViaRedis(gb) // initially send all our slugs & events to subscribe to - go gb.SendSlugsToServer() + go gb.PublishOwnCollectionsSlugs() // subscribe to redis pubsub mgmt channel to listen for "SendSlugs" events go func() { err := gb.Rdb.Receive(context.Background(), gb.Rdb.B().Subscribe().Channel(internal.PubSubSeaWatcherMgmt).Build(), func(msg rueidis.PubSubMessage) { - gbl.Log.Debug(fmt.Sprintf("๐Ÿš‡ received msg on %s: %s", msg.Channel, msg.Message)) + gbl.Log.Debug(fmt.Sprintf("๐Ÿ‘” received msg on %s: %s", msg.Channel, msg.Message)) var mgmtEvent *seawaModels.MgmtEvent @@ -405,8 +397,8 @@ func runGloomberg(_ *cobra.Command, _ []string) { } if mgmtEvent.Action == seawaModels.SendSlugs { - gbl.Log.Info(fmt.Sprintf("๐Ÿš‡ SendSlugs received on channel %s", msg.Channel)) - gb.SendSlugsToServer() + gbl.Log.Info(fmt.Sprintf("๐Ÿ‘” SendSlugs received on channel %s", msg.Channel)) + gb.PublishOwnCollectionsSlugs() } }) if err != nil { diff --git a/cmd/root.go b/cmd/root.go index 69f3fcb..b4ab1d2 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -2,7 +2,6 @@ package cmd import ( "fmt" - "net" "os" "strings" "time" @@ -26,10 +25,6 @@ var ( certPath string keyPath string - // grpc. - grpcServerListen net.IP - grpcClientHost net.IP - gb *gloomberg.Gloomberg ) @@ -138,15 +133,6 @@ func init() { viper.SetDefault("cache.salira_ttl", 1*time.Hour) viper.SetDefault("cache.slug_ttl", 3*24*time.Hour) viper.SetDefault("cache.notifications_lock_ttl", time.Millisecond*1337) - - // grpc server - viper.SetDefault("grpc.server.enabled", false) - viper.SetDefault("grpc.server.listenAddress", net.IPv4(127, 0, 0, 1)) - viper.SetDefault("grpc.server.port", 31337) - // grpc client - viper.SetDefault("grpc.client.enabled", false) - viper.SetDefault("grpc.client.host", net.IPv4(127, 0, 0, 1)) - viper.SetDefault("grpc.client.port", 31337) } // initConfig reads in config file and ENV variables if set. diff --git a/cmd/seawatcherCmd.go b/cmd/seawatcherCmd.go index 473877b..55b1ca1 100644 --- a/cmd/seawatcherCmd.go +++ b/cmd/seawatcherCmd.go @@ -3,7 +3,6 @@ package cmd import ( "net" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc" "github.com/benleb/gloomberg/internal/seawa" "github.com/benleb/gloomberg/internal/trapri" "github.com/benleb/gloomberg/internal/web" @@ -28,16 +27,6 @@ func init() { viper.SetDefault("manifoldSNS.enabled", false) viper.SetDefault("manifoldSNS.host", net.IPv4(127, 0, 0, 1)) viper.SetDefault("manifoldSNS.port", viper.GetUint16("web.port")-1) - - // grpc - seaWatcherCmd.Flags().Uint16("grpc-port", 31337, "gRPC server port") - _ = viper.BindPFlag("grpc.port", seaWatcherCmd.Flags().Lookup("grpc-port")) - // grpc server - seaWatcherCmd.Flags().IPVar(&grpcServerListen, "grpc-listen", nil, "gRPC server listen address") - _ = viper.BindPFlag("grpc.listen", seaWatcherCmd.Flags().Lookup("grpc-listen")) - // grpc client - seaWatcherCmd.Flags().IPVar(&grpcClientHost, "grpc", nil, "server gRPC client connects to") - _ = viper.BindPFlag("grpc.client.host", seaWatcherCmd.Flags().Lookup("grpc")) } func runSeawatcher(_ *cobra.Command, _ []string) { @@ -68,11 +57,6 @@ func runSeawatcher(_ *cobra.Command, _ []string) { sw.Pr("requested slugs from clientsโ€ฆ") } - // sw.Prf("seaWatcherCmd.CalledAs(): %+v", cmd.CalledAs()) - if viper.GetBool("grpc.server.enabled") { - gbgrpc.StartServer(gb, sw) - } - // // manifold SNS receiver if viper.GetBool("manifoldSNS.enabled") { diff --git a/internal/degendb/event_type.go b/internal/degendb/event_type.go index 30ef82a..1d8f82a 100644 --- a/internal/degendb/event_type.go +++ b/internal/degendb/event_type.go @@ -47,7 +47,7 @@ var ( Unknown = &GBEventType{name: "Unknown", actionName: "did something", icon: "โ“", openseaEventName: ""} Transfer = &GBEventType{name: "Transfer", actionName: "transferred", icon: "๐Ÿ“ฆ", openseaEventName: "item_transferred"} Sale = &GBEventType{name: "Sale", actionName: "sold", icon: "๐Ÿ’ฐ", openseaEventName: "item_sold"} - Purchase = &GBEventType{name: "Purchase", actionName: "purchased", icon: "๐Ÿ’ฐ", openseaEventName: "item_sold"} + Purchase = &GBEventType{name: "Purchase", actionName: "purchased", icon: "๐Ÿ›๏ธ", openseaEventName: "item_sold"} Mint = &GBEventType{name: "Mint", actionName: "minted", icon: "โ“‚๏ธ", openseaEventName: ""} Airdrop = &GBEventType{name: "Airdrop", actionName: "got airdropped", icon: "๐ŸŽ", openseaEventName: ""} Burn = &GBEventType{name: "Burn", actionName: "burned", icon: "๐Ÿ”ฅ", openseaEventName: ""} @@ -57,11 +57,10 @@ var ( Listing = &GBEventType{name: "Listing", actionName: "listed", icon: "๐Ÿ“ข", openseaEventName: "item_listed"} Bid = &GBEventType{name: "Bid", actionName: "(got) bid", icon: "๐Ÿ’ฆ", openseaEventName: "item_received_bid"} OwnBid = &GBEventType{name: "OwnBid", actionName: "bid", icon: "๐Ÿค‘", openseaEventName: ""} - Offer = &GBEventType{name: "Offer", actionName: "(got) offered", icon: "๐Ÿ’ฆ", openseaEventName: "item_received_offer"} AcceptedOffer = &GBEventType{name: "AcceptedOffer", actionName: "accepted offer", icon: "๐Ÿค", openseaEventName: ""} - CollectionOffer = &GBEventType{name: "CollectionOffer", actionName: "(got) collection-offered", icon: "๐ŸงŠ", openseaEventName: "collection_offer"} + CollectionOffer = &GBEventType{name: "CollectionOffer", actionName: "(got) collection-offered", icon: "โ˜‚๏ธ", openseaEventName: "collection_offer"} // ๐ŸงŠ AcceptedCollectionOffer = &GBEventType{name: "AcceptedCollectionOffer", actionName: "accepted collection offer", icon: "๐Ÿค", openseaEventName: ""} - MetadataUpdate = &GBEventType{name: "MetadataUpdate", actionName: "metadata updated", icon: "โ™ป๏ธ", openseaEventName: "item_metadata_updated"} + MetadataUpdated = &GBEventType{name: "MetadataUpdated", actionName: "metadata updated", icon: "โ™ป๏ธ", openseaEventName: "item_metadata_updated"} Cancelled = &GBEventType{name: "Cancelled", actionName: "cancelled", icon: "โŒ", openseaEventName: "item_cancelled"} // event type sets. @@ -73,8 +72,7 @@ var ( "item_sold": Sale, "item_listed": Listing, "item_received_bid": Bid, - "item_received_offer": Offer, - "item_metadata_updated": MetadataUpdate, + "item_metadata_updated": MetadataUpdated, "item_cancelled": Cancelled, "collection_offer": CollectionOffer, } diff --git a/internal/nemo/gloomberg/gbgrpc/client.go b/internal/nemo/gloomberg/gbgrpc/client.go deleted file mode 100644 index c682895..0000000 --- a/internal/nemo/gloomberg/gbgrpc/client.go +++ /dev/null @@ -1,212 +0,0 @@ -package gbgrpc - -import ( - context "context" - "errors" - "fmt" - "io" - "math" - "math/big" - "strings" - "time" - - "github.com/benleb/gloomberg/internal/degendb" - "github.com/benleb/gloomberg/internal/gbl" - "github.com/benleb/gloomberg/internal/nemo/gloomberg" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc/gen" - seawaModels "github.com/benleb/gloomberg/internal/seawa/models" - "github.com/benleb/gloomberg/internal/style" - "github.com/charmbracelet/log" - "github.com/ethereum/go-ethereum/common" - "github.com/spf13/viper" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/protobuf/encoding/protojson" -) - -var GRPCClient gen.GloombergClient - -// type grpcClient struct { -// gen.GloombergClient -// } - -// NewClient to grpc server. -func NewClient(grpcAddress string) gen.GloombergClient { - if grpcAddress == "" { - grpcAddress = fmt.Sprintf("%s:%d", viper.GetString("grpc.client.host"), viper.GetUint("grpc.client.port")) - } - - // grpc options - var opts []grpc.DialOption - - if creds := gloomberg.GetTLSClientCredentials(); creds != nil { - opts = append(opts, grpc.WithTransportCredentials(creds)) - } else { - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - - // connect - gloomberg.Prf("connecting to gRPC %s...", style.BoldAlmostWhite(grpcAddress)) - conn, err := grpc.Dial(grpcAddress, opts...) - if err != nil { - gbl.Log.Warnf("fail to dial: %v", err) - - return nil - } - - // time needed to establish connection - time.Sleep(time.Millisecond * 337) - - // check connection state - if conn.GetState() != connectivity.Ready { - return nil - } - - // return client - GRPCClient = gen.NewGloombergClient(conn) - - return GRPCClient // gen.NewGloombergClient(conn) -} - -func FetchEvents(gb *gloomberg.Gloomberg) { - // server to connect to - grpcAddress := fmt.Sprintf("%s:%d", viper.GetString("grpc.client.host"), viper.GetUint("grpc.client.port")) - - // grpc options - var opts []grpc.DialOption - - if creds := gloomberg.GetTLSClientCredentials(); creds != nil { - opts = append(opts, grpc.WithTransportCredentials(creds)) - } else { - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - - failedAttempts := 0 - - for { - if failedAttempts > 0 { - // exponential backoff - waitTime := time.Second * time.Duration(math.Pow(2.0, float64(int(math.Min(float64(failedAttempts), 5))))) - - log.Printf("retrying to connect to gRPC %s in %.0f seconds...", style.BoldAlmostWhite(grpcAddress), waitTime.Seconds()) - - time.Sleep(waitTime) - } - - // connect - gloomberg.Prf("connecting to gRPC %s...", style.BoldAlmostWhite(grpcAddress)) - conn, err := grpc.Dial(grpcAddress, opts...) - if err != nil { - gbl.Log.Warnf("fail to dial: %v", err) - - continue - } - - // time needed to establish connection - time.Sleep(time.Millisecond * 337) - - // check connection state - if conn.GetState() != connectivity.Ready { - log.Errorf("fail to connect to gRPC %s", style.BoldAlmostWhite(grpcAddress)) - } - - // return client - grpcClient := gen.NewGloombergClient(conn) - - if grpcClient == nil { - log.Errorf("fail to connect to gRPC %s", style.BoldAlmostWhite(grpcAddress)) - - failedAttempts++ - - continue - } - - // get eventstream - gloomberg.Prf("subscribing via grpc to: %s", style.BoldAlmostWhite(degendb.Listing.OpenseaEventName())) - - subsriptionRequest := &gen.SubscriptionRequest{EventTypes: []gen.EventType{gen.EventType_ITEM_LISTED}, Collections: gb.CollectionDB.OpenseaSlugs()} //nolint:nosnakecase - stream, err := grpcClient.GetEvents(context.Background(), subsriptionRequest) - if err != nil { - log.Errorf("getting stream failed: %v, retrying", err) - - failedAttempts++ - - continue - } - - // reset failed attempts - failedAttempts = 0 - - for { - // get events - event, err := stream.Recv() - log.Debugf("๐Ÿ” client received: %+v", event) - - if err != nil { - if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) { - log.Errorf("io.EOF error: %v", err) - } - - if event == nil { - log.Errorf("receiving event failed: %v | %+v", err, event) - } - - err := stream.CloseSend() - if err != nil { - log.Errorf("closing stream failed: %v", err) - } - - break - } - - basePrice, ok := new(big.Int).SetString(event.Payload.GetItemListed().Payload.BasePrice, 10) - if !ok { - log.Errorf("error parsing base price: %v", err) - - continue - } - - var itemListed seawaModels.ItemListed - - log.Debugf("๐Ÿ” creating itemListed struct: %+v", protojson.Format(event)) - - // transform event back to seawaModel.ItemListed - itemListed = seawaModels.ItemListed{ - EventType: strings.ToLower(event.EventType.String()), - SentAt: event.Payload.GetItemListed().SentAt.AsTime(), - Payload: seawaModels.ItemListedPayload{ - Item: seawaModels.Item{ - NftID: *seawaModels.ParseNftID(event.Payload.GetItemListed().Payload.Item.NftId), - Chain: seawaModels.Chain{Name: event.Payload.GetItemListed().Payload.Item.Chain.Name}, - Permalink: event.Payload.GetItemListed().Payload.Item.Permalink, - Metadata: seawaModels.Metadata{ - Name: event.Payload.GetItemListed().Payload.Item.Metadata.Name, - ImageURL: event.Payload.GetItemListed().Payload.Item.Metadata.ImageUrl, - AnimationURL: event.Payload.GetItemListed().Payload.Item.Metadata.AnimationUrl, - MetadataURL: event.Payload.GetItemListed().Payload.Item.Metadata.MetadataUrl, - }, - }, - IsPrivate: event.Payload.GetItemListed().Payload.IsPrivate, - ListingDate: event.Payload.GetItemListed().Payload.ListingDate.AsTime(), - EventPayload: seawaModels.EventPayload{ - EventTimestamp: event.Payload.GetItemListed().Payload.EventTimestamp.AsTime(), - BasePrice: basePrice, - Maker: seawaModels.Account{Address: common.HexToAddress(event.Payload.GetItemListed().Payload.Maker.Address)}, - Taker: seawaModels.Account{Address: common.HexToAddress(event.Payload.GetItemListed().Payload.Taker.Address)}, - Quantity: int(event.Payload.GetItemListed().Payload.Quantity), - OrderHash: common.HexToHash(event.Payload.GetItemListed().Payload.OrderHash), - ExpirationDate: event.Payload.GetItemListed().Payload.ExpirationDate.AsTime(), - CollectionCriteria: seawaModels.CollectionCriteria{Slug: event.Payload.GetItemListed().Payload.Collection.Slug}, - PaymentToken: seawaModels.PaymentToken{Address: common.HexToAddress(event.Payload.GetItemListed().Payload.PaymentToken.Address), Symbol: event.Payload.GetItemListed().Payload.PaymentToken.Symbol, Decimals: int(event.Payload.GetItemListed().Payload.PaymentToken.Decimals)}, - }, - }, - } - - // send event to the eventhub - gb.In.ItemListed <- &itemListed - - log.Debugf("๐Ÿ” sent to eventHub: %+v", itemListed) - } - } -} diff --git a/internal/nemo/gloomberg/gbgrpc/gen/gloomberg.pb.go b/internal/nemo/gloomberg/gbgrpc/gen/gloomberg.pb.go deleted file mode 100644 index 9039678..0000000 --- a/internal/nemo/gloomberg/gbgrpc/gen/gloomberg.pb.go +++ /dev/null @@ -1,1909 +0,0 @@ -// dakma-dev | gloomberg.proto - -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.31.0 -// protoc v4.23.4 -// source: internal/nemo/gloomberg/gbgrpc/gen/gloomberg.proto - -package gen - -import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - anypb "google.golang.org/protobuf/types/known/anypb" - emptypb "google.golang.org/protobuf/types/known/emptypb" - timestamppb "google.golang.org/protobuf/types/known/timestamppb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type EventType int32 - -const ( - EventType_UNKNOWN EventType = 0 - EventType_ITEM_LISTED EventType = 1 - EventType_ITEM_RECEIVED_BID EventType = 2 - EventType_METADATA_UPDATED EventType = 3 - EventType_COLLECTION_OFFER EventType = 4 -) - -// Enum value maps for EventType. -var ( - EventType_name = map[int32]string{ - 0: "UNKNOWN", - 1: "ITEM_LISTED", - 2: "ITEM_RECEIVED_BID", - 3: "METADATA_UPDATED", - 4: "COLLECTION_OFFER", - } - EventType_value = map[string]int32{ - "UNKNOWN": 0, - "ITEM_LISTED": 1, - "ITEM_RECEIVED_BID": 2, - "METADATA_UPDATED": 3, - "COLLECTION_OFFER": 4, - } -) - -func (x EventType) Enum() *EventType { - p := new(EventType) - *p = x - return p -} - -func (x EventType) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (EventType) Descriptor() protoreflect.EnumDescriptor { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_enumTypes[0].Descriptor() -} - -func (EventType) Type() protoreflect.EnumType { - return &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_enumTypes[0] -} - -func (x EventType) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use EventType.Descriptor instead. -func (EventType) EnumDescriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{0} -} - -type SubscriptionRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - EventTypes []EventType `protobuf:"varint,1,rep,packed,name=eventTypes,proto3,enum=gen.EventType" json:"eventTypes,omitempty"` - Collections []string `protobuf:"bytes,2,rep,name=collections,proto3" json:"collections,omitempty"` -} - -func (x *SubscriptionRequest) Reset() { - *x = SubscriptionRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *SubscriptionRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*SubscriptionRequest) ProtoMessage() {} - -func (x *SubscriptionRequest) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[0] - 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 SubscriptionRequest.ProtoReflect.Descriptor instead. -func (*SubscriptionRequest) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{0} -} - -func (x *SubscriptionRequest) GetEventTypes() []EventType { - if x != nil { - return x.EventTypes - } - return nil -} - -func (x *SubscriptionRequest) GetCollections() []string { - if x != nil { - return x.Collections - } - return nil -} - -type Event struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The name of the feature. - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // The event type. - EventType EventType `protobuf:"varint,2,opt,name=eventType,proto3,enum=gen.EventType" json:"eventType,omitempty"` - // The event payload. - Payload *EventPayload `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` -} - -func (x *Event) Reset() { - *x = Event{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Event) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Event) ProtoMessage() {} - -func (x *Event) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[1] - 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 Event.ProtoReflect.Descriptor instead. -func (*Event) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{1} -} - -func (x *Event) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *Event) GetEventType() EventType { - if x != nil { - return x.EventType - } - return EventType_UNKNOWN -} - -func (x *Event) GetPayload() *EventPayload { - if x != nil { - return x.Payload - } - return nil -} - -type EventPayload struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // The kind of value. - // - // Types that are assignable to Kind: - // - // *EventPayload_ItemListed - // *EventPayload_ItemReceivedBid - Kind isEventPayload_Kind `protobuf_oneof:"kind"` -} - -func (x *EventPayload) Reset() { - *x = EventPayload{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *EventPayload) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*EventPayload) ProtoMessage() {} - -func (x *EventPayload) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 EventPayload.ProtoReflect.Descriptor instead. -func (*EventPayload) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{2} -} - -func (m *EventPayload) GetKind() isEventPayload_Kind { - if m != nil { - return m.Kind - } - return nil -} - -func (x *EventPayload) GetItemListed() *ItemListed { - if x, ok := x.GetKind().(*EventPayload_ItemListed); ok { - return x.ItemListed - } - return nil -} - -func (x *EventPayload) GetItemReceivedBid() *ItemReceivedBid { - if x, ok := x.GetKind().(*EventPayload_ItemReceivedBid); ok { - return x.ItemReceivedBid - } - return nil -} - -type isEventPayload_Kind interface { - isEventPayload_Kind() -} - -type EventPayload_ItemListed struct { - ItemListed *ItemListed `protobuf:"bytes,1,opt,name=item_listed,json=itemListed,proto3,oneof"` -} - -type EventPayload_ItemReceivedBid struct { - ItemReceivedBid *ItemReceivedBid `protobuf:"bytes,2,opt,name=item_received_bid,json=itemReceivedBid,proto3,oneof"` -} - -func (*EventPayload_ItemListed) isEventPayload_Kind() {} - -func (*EventPayload_ItemReceivedBid) isEventPayload_Kind() {} - -type ItemReceivedBid struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *ItemReceivedBid) Reset() { - *x = ItemReceivedBid{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemReceivedBid) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemReceivedBid) ProtoMessage() {} - -func (x *ItemReceivedBid) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 ItemReceivedBid.ProtoReflect.Descriptor instead. -func (*ItemReceivedBid) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{3} -} - -// Event emitted when an item is listed for sale. -// -// This event is emitted when an item is listed for sale on opensea. -type ItemListed struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - EventType EventType `protobuf:"varint,1,opt,name=event_type,json=eventType,proto3,enum=gen.EventType" json:"event_type,omitempty"` - Payload *ItemListed_ItemListedPayload `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` - SentAt *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=sent_at,json=sentAt,proto3" json:"sent_at,omitempty"` -} - -func (x *ItemListed) Reset() { - *x = ItemListed{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed) ProtoMessage() {} - -func (x *ItemListed) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 ItemListed.ProtoReflect.Descriptor instead. -func (*ItemListed) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4} -} - -func (x *ItemListed) GetEventType() EventType { - if x != nil { - return x.EventType - } - return EventType_UNKNOWN -} - -func (x *ItemListed) GetPayload() *ItemListed_ItemListedPayload { - if x != nil { - return x.Payload - } - return nil -} - -func (x *ItemListed) GetSentAt() *timestamppb.Timestamp { - if x != nil { - return x.SentAt - } - return nil -} - -type ItemReceivedBid_CollectionMuh struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Muh string `protobuf:"bytes,1,opt,name=muh,proto3" json:"muh,omitempty"` -} - -func (x *ItemReceivedBid_CollectionMuh) Reset() { - *x = ItemReceivedBid_CollectionMuh{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemReceivedBid_CollectionMuh) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemReceivedBid_CollectionMuh) ProtoMessage() {} - -func (x *ItemReceivedBid_CollectionMuh) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 ItemReceivedBid_CollectionMuh.ProtoReflect.Descriptor instead. -func (*ItemReceivedBid_CollectionMuh) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{3, 0} -} - -func (x *ItemReceivedBid_CollectionMuh) GetMuh() string { - if x != nil { - return x.Muh - } - return "" -} - -type ItemReceivedBid_ChainMoep struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Mia string `protobuf:"bytes,1,opt,name=mia,proto3" json:"mia,omitempty"` -} - -func (x *ItemReceivedBid_ChainMoep) Reset() { - *x = ItemReceivedBid_ChainMoep{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemReceivedBid_ChainMoep) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemReceivedBid_ChainMoep) ProtoMessage() {} - -func (x *ItemReceivedBid_ChainMoep) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 ItemReceivedBid_ChainMoep.ProtoReflect.Descriptor instead. -func (*ItemReceivedBid_ChainMoep) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{3, 1} -} - -func (x *ItemReceivedBid_ChainMoep) GetMia() string { - if x != nil { - return x.Mia - } - return "" -} - -type ItemListed_Collection struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Slug string `protobuf:"bytes,1,opt,name=slug,proto3" json:"slug,omitempty"` -} - -func (x *ItemListed_Collection) Reset() { - *x = ItemListed_Collection{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_Collection) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_Collection) ProtoMessage() {} - -func (x *ItemListed_Collection) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 ItemListed_Collection.ProtoReflect.Descriptor instead. -func (*ItemListed_Collection) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 0} -} - -func (x *ItemListed_Collection) GetSlug() string { - if x != nil { - return x.Slug - } - return "" -} - -type ItemListed_Chain struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *ItemListed_Chain) Reset() { - *x = ItemListed_Chain{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_Chain) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_Chain) ProtoMessage() {} - -func (x *ItemListed_Chain) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 ItemListed_Chain.ProtoReflect.Descriptor instead. -func (*ItemListed_Chain) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 1} -} - -func (x *ItemListed_Chain) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type ItemListed_Metadata struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - AnimationUrl string `protobuf:"bytes,1,opt,name=animation_url,json=animationUrl,proto3" json:"animation_url,omitempty"` - ImageUrl string `protobuf:"bytes,2,opt,name=image_url,json=imageUrl,proto3" json:"image_url,omitempty"` - MetadataUrl string `protobuf:"bytes,3,opt,name=metadata_url,json=metadataUrl,proto3" json:"metadata_url,omitempty"` - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *ItemListed_Metadata) Reset() { - *x = ItemListed_Metadata{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_Metadata) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_Metadata) ProtoMessage() {} - -func (x *ItemListed_Metadata) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 ItemListed_Metadata.ProtoReflect.Descriptor instead. -func (*ItemListed_Metadata) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 2} -} - -func (x *ItemListed_Metadata) GetAnimationUrl() string { - if x != nil { - return x.AnimationUrl - } - return "" -} - -func (x *ItemListed_Metadata) GetImageUrl() string { - if x != nil { - return x.ImageUrl - } - return "" -} - -func (x *ItemListed_Metadata) GetMetadataUrl() string { - if x != nil { - return x.MetadataUrl - } - return "" -} - -func (x *ItemListed_Metadata) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type ItemListed_Item struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Chain *ItemListed_Chain `protobuf:"bytes,1,opt,name=chain,proto3" json:"chain,omitempty"` - Metadata *ItemListed_Metadata `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` - NftId string `protobuf:"bytes,3,opt,name=nft_id,json=nftId,proto3" json:"nft_id,omitempty"` - Permalink string `protobuf:"bytes,4,opt,name=permalink,proto3" json:"permalink,omitempty"` -} - -func (x *ItemListed_Item) Reset() { - *x = ItemListed_Item{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[10] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_Item) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_Item) ProtoMessage() {} - -func (x *ItemListed_Item) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_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 ItemListed_Item.ProtoReflect.Descriptor instead. -func (*ItemListed_Item) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 3} -} - -func (x *ItemListed_Item) GetChain() *ItemListed_Chain { - if x != nil { - return x.Chain - } - return nil -} - -func (x *ItemListed_Item) GetMetadata() *ItemListed_Metadata { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *ItemListed_Item) GetNftId() string { - if x != nil { - return x.NftId - } - return "" -} - -func (x *ItemListed_Item) GetPermalink() string { - if x != nil { - return x.Permalink - } - return "" -} - -type ItemListed_Account struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` -} - -func (x *ItemListed_Account) Reset() { - *x = ItemListed_Account{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[11] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_Account) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_Account) ProtoMessage() {} - -func (x *ItemListed_Account) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[11] - 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 ItemListed_Account.ProtoReflect.Descriptor instead. -func (*ItemListed_Account) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 4} -} - -func (x *ItemListed_Account) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -type ItemListed_PaymentToken struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` - Decimals uint32 `protobuf:"varint,2,opt,name=decimals,proto3" json:"decimals,omitempty"` - EthPrice string `protobuf:"bytes,3,opt,name=eth_price,json=ethPrice,proto3" json:"eth_price,omitempty"` - Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` - Symbol string `protobuf:"bytes,5,opt,name=symbol,proto3" json:"symbol,omitempty"` - UsdPrice string `protobuf:"bytes,6,opt,name=usd_price,json=usdPrice,proto3" json:"usd_price,omitempty"` -} - -func (x *ItemListed_PaymentToken) Reset() { - *x = ItemListed_PaymentToken{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[12] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_PaymentToken) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_PaymentToken) ProtoMessage() {} - -func (x *ItemListed_PaymentToken) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[12] - 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 ItemListed_PaymentToken.ProtoReflect.Descriptor instead. -func (*ItemListed_PaymentToken) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 5} -} - -func (x *ItemListed_PaymentToken) GetAddress() string { - if x != nil { - return x.Address - } - return "" -} - -func (x *ItemListed_PaymentToken) GetDecimals() uint32 { - if x != nil { - return x.Decimals - } - return 0 -} - -func (x *ItemListed_PaymentToken) GetEthPrice() string { - if x != nil { - return x.EthPrice - } - return "" -} - -func (x *ItemListed_PaymentToken) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *ItemListed_PaymentToken) GetSymbol() string { - if x != nil { - return x.Symbol - } - return "" -} - -func (x *ItemListed_PaymentToken) GetUsdPrice() string { - if x != nil { - return x.UsdPrice - } - return "" -} - -type ItemListed_Consideration struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - EndAmount uint64 `protobuf:"varint,1,opt,name=endAmount,proto3" json:"endAmount,omitempty"` - IdentifierOrCriteria uint32 `protobuf:"varint,2,opt,name=identifierOrCriteria,proto3" json:"identifierOrCriteria,omitempty"` - ItemType uint32 `protobuf:"varint,3,opt,name=itemType,proto3" json:"itemType,omitempty"` - Recipient string `protobuf:"bytes,4,opt,name=recipient,proto3" json:"recipient,omitempty"` - StartAmount uint64 `protobuf:"varint,5,opt,name=startAmount,proto3" json:"startAmount,omitempty"` - Token string `protobuf:"bytes,6,opt,name=token,proto3" json:"token,omitempty"` -} - -func (x *ItemListed_Consideration) Reset() { - *x = ItemListed_Consideration{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_Consideration) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_Consideration) ProtoMessage() {} - -func (x *ItemListed_Consideration) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[13] - 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 ItemListed_Consideration.ProtoReflect.Descriptor instead. -func (*ItemListed_Consideration) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 6} -} - -func (x *ItemListed_Consideration) GetEndAmount() uint64 { - if x != nil { - return x.EndAmount - } - return 0 -} - -func (x *ItemListed_Consideration) GetIdentifierOrCriteria() uint32 { - if x != nil { - return x.IdentifierOrCriteria - } - return 0 -} - -func (x *ItemListed_Consideration) GetItemType() uint32 { - if x != nil { - return x.ItemType - } - return 0 -} - -func (x *ItemListed_Consideration) GetRecipient() string { - if x != nil { - return x.Recipient - } - return "" -} - -func (x *ItemListed_Consideration) GetStartAmount() uint64 { - if x != nil { - return x.StartAmount - } - return 0 -} - -func (x *ItemListed_Consideration) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -type ItemListed_Offer struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - EndAmount uint32 `protobuf:"varint,1,opt,name=endAmount,proto3" json:"endAmount,omitempty"` - IdentifierOrCriteria uint64 `protobuf:"varint,2,opt,name=identifierOrCriteria,proto3" json:"identifierOrCriteria,omitempty"` - ItemType uint32 `protobuf:"varint,3,opt,name=itemType,proto3" json:"itemType,omitempty"` - StartAmount uint32 `protobuf:"varint,4,opt,name=startAmount,proto3" json:"startAmount,omitempty"` - Token string `protobuf:"bytes,5,opt,name=token,proto3" json:"token,omitempty"` -} - -func (x *ItemListed_Offer) Reset() { - *x = ItemListed_Offer{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_Offer) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_Offer) ProtoMessage() {} - -func (x *ItemListed_Offer) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[14] - 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 ItemListed_Offer.ProtoReflect.Descriptor instead. -func (*ItemListed_Offer) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 7} -} - -func (x *ItemListed_Offer) GetEndAmount() uint32 { - if x != nil { - return x.EndAmount - } - return 0 -} - -func (x *ItemListed_Offer) GetIdentifierOrCriteria() uint64 { - if x != nil { - return x.IdentifierOrCriteria - } - return 0 -} - -func (x *ItemListed_Offer) GetItemType() uint32 { - if x != nil { - return x.ItemType - } - return 0 -} - -func (x *ItemListed_Offer) GetStartAmount() uint32 { - if x != nil { - return x.StartAmount - } - return 0 -} - -func (x *ItemListed_Offer) GetToken() string { - if x != nil { - return x.Token - } - return "" -} - -type ItemListed_Parameters struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - ConduitKey string `protobuf:"bytes,1,opt,name=conduitKey,proto3" json:"conduitKey,omitempty"` - Consideration []*ItemListed_Consideration `protobuf:"bytes,2,rep,name=consideration,proto3" json:"consideration,omitempty"` - Counter uint32 `protobuf:"varint,3,opt,name=counter,proto3" json:"counter,omitempty"` - EndTime uint32 `protobuf:"varint,4,opt,name=endTime,proto3" json:"endTime,omitempty"` - Offer []*ItemListed_Offer `protobuf:"bytes,5,rep,name=offer,proto3" json:"offer,omitempty"` - Offerer string `protobuf:"bytes,6,opt,name=offerer,proto3" json:"offerer,omitempty"` - OrderType uint32 `protobuf:"varint,7,opt,name=orderType,proto3" json:"orderType,omitempty"` - Salt uint64 `protobuf:"varint,8,opt,name=salt,proto3" json:"salt,omitempty"` - StartTime uint32 `protobuf:"varint,9,opt,name=startTime,proto3" json:"startTime,omitempty"` - TotalOriginalConsiderationItems uint32 `protobuf:"varint,10,opt,name=totalOriginalConsiderationItems,proto3" json:"totalOriginalConsiderationItems,omitempty"` - Zone string `protobuf:"bytes,11,opt,name=zone,proto3" json:"zone,omitempty"` - ZoneHash string `protobuf:"bytes,12,opt,name=zoneHash,proto3" json:"zoneHash,omitempty"` -} - -func (x *ItemListed_Parameters) Reset() { - *x = ItemListed_Parameters{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[15] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_Parameters) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_Parameters) ProtoMessage() {} - -func (x *ItemListed_Parameters) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[15] - 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 ItemListed_Parameters.ProtoReflect.Descriptor instead. -func (*ItemListed_Parameters) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 8} -} - -func (x *ItemListed_Parameters) GetConduitKey() string { - if x != nil { - return x.ConduitKey - } - return "" -} - -func (x *ItemListed_Parameters) GetConsideration() []*ItemListed_Consideration { - if x != nil { - return x.Consideration - } - return nil -} - -func (x *ItemListed_Parameters) GetCounter() uint32 { - if x != nil { - return x.Counter - } - return 0 -} - -func (x *ItemListed_Parameters) GetEndTime() uint32 { - if x != nil { - return x.EndTime - } - return 0 -} - -func (x *ItemListed_Parameters) GetOffer() []*ItemListed_Offer { - if x != nil { - return x.Offer - } - return nil -} - -func (x *ItemListed_Parameters) GetOfferer() string { - if x != nil { - return x.Offerer - } - return "" -} - -func (x *ItemListed_Parameters) GetOrderType() uint32 { - if x != nil { - return x.OrderType - } - return 0 -} - -func (x *ItemListed_Parameters) GetSalt() uint64 { - if x != nil { - return x.Salt - } - return 0 -} - -func (x *ItemListed_Parameters) GetStartTime() uint32 { - if x != nil { - return x.StartTime - } - return 0 -} - -func (x *ItemListed_Parameters) GetTotalOriginalConsiderationItems() uint32 { - if x != nil { - return x.TotalOriginalConsiderationItems - } - return 0 -} - -func (x *ItemListed_Parameters) GetZone() string { - if x != nil { - return x.Zone - } - return "" -} - -func (x *ItemListed_Parameters) GetZoneHash() string { - if x != nil { - return x.ZoneHash - } - return "" -} - -type ItemListed_ProtocolData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Parameters *ItemListed_Parameters `protobuf:"bytes,1,opt,name=parameters,proto3" json:"parameters,omitempty"` - Signature string `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` - UseLazyMintAdapterForSharedStorefront bool `protobuf:"varint,3,opt,name=use_lazy_mint_adapter_for_shared_storefront,json=useLazyMintAdapterForSharedStorefront,proto3" json:"use_lazy_mint_adapter_for_shared_storefront,omitempty"` -} - -func (x *ItemListed_ProtocolData) Reset() { - *x = ItemListed_ProtocolData{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[16] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_ProtocolData) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_ProtocolData) ProtoMessage() {} - -func (x *ItemListed_ProtocolData) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[16] - 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 ItemListed_ProtocolData.ProtoReflect.Descriptor instead. -func (*ItemListed_ProtocolData) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 9} -} - -func (x *ItemListed_ProtocolData) GetParameters() *ItemListed_Parameters { - if x != nil { - return x.Parameters - } - return nil -} - -func (x *ItemListed_ProtocolData) GetSignature() string { - if x != nil { - return x.Signature - } - return "" -} - -func (x *ItemListed_ProtocolData) GetUseLazyMintAdapterForSharedStorefront() bool { - if x != nil { - return x.UseLazyMintAdapterForSharedStorefront - } - return false -} - -type ItemListed_ItemListedPayload struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BasePrice string `protobuf:"bytes,1,opt,name=base_price,json=basePrice,proto3" json:"base_price,omitempty"` - Collection *ItemListed_Collection `protobuf:"bytes,2,opt,name=collection,proto3" json:"collection,omitempty"` - EventTimestamp *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=event_timestamp,json=eventTimestamp,proto3" json:"event_timestamp,omitempty"` - ExpirationDate *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expiration_date,json=expirationDate,proto3" json:"expiration_date,omitempty"` - IsPrivate bool `protobuf:"varint,5,opt,name=is_private,json=isPrivate,proto3" json:"is_private,omitempty"` - Item *ItemListed_Item `protobuf:"bytes,6,opt,name=item,proto3" json:"item,omitempty"` - ListingDate *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=listing_date,json=listingDate,proto3" json:"listing_date,omitempty"` - ListingType *anypb.Any `protobuf:"bytes,8,opt,name=listing_type,json=listingType,proto3" json:"listing_type,omitempty"` - Maker *ItemListed_Account `protobuf:"bytes,9,opt,name=maker,proto3" json:"maker,omitempty"` - OrderHash string `protobuf:"bytes,10,opt,name=order_hash,json=orderHash,proto3" json:"order_hash,omitempty"` - PaymentToken *ItemListed_PaymentToken `protobuf:"bytes,11,opt,name=payment_token,json=paymentToken,proto3" json:"payment_token,omitempty"` - ProtocolData *ItemListed_ProtocolData `protobuf:"bytes,12,opt,name=protocol_data,json=protocolData,proto3" json:"protocol_data,omitempty"` - Quantity uint32 `protobuf:"varint,13,opt,name=quantity,proto3" json:"quantity,omitempty"` - Taker *ItemListed_Account `protobuf:"bytes,14,opt,name=taker,proto3" json:"taker,omitempty"` -} - -func (x *ItemListed_ItemListedPayload) Reset() { - *x = ItemListed_ItemListedPayload{} - if protoimpl.UnsafeEnabled { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[17] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *ItemListed_ItemListedPayload) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*ItemListed_ItemListedPayload) ProtoMessage() {} - -func (x *ItemListed_ItemListedPayload) ProtoReflect() protoreflect.Message { - mi := &file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[17] - 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 ItemListed_ItemListedPayload.ProtoReflect.Descriptor instead. -func (*ItemListed_ItemListedPayload) Descriptor() ([]byte, []int) { - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP(), []int{4, 10} -} - -func (x *ItemListed_ItemListedPayload) GetBasePrice() string { - if x != nil { - return x.BasePrice - } - return "" -} - -func (x *ItemListed_ItemListedPayload) GetCollection() *ItemListed_Collection { - if x != nil { - return x.Collection - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetEventTimestamp() *timestamppb.Timestamp { - if x != nil { - return x.EventTimestamp - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetExpirationDate() *timestamppb.Timestamp { - if x != nil { - return x.ExpirationDate - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetIsPrivate() bool { - if x != nil { - return x.IsPrivate - } - return false -} - -func (x *ItemListed_ItemListedPayload) GetItem() *ItemListed_Item { - if x != nil { - return x.Item - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetListingDate() *timestamppb.Timestamp { - if x != nil { - return x.ListingDate - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetListingType() *anypb.Any { - if x != nil { - return x.ListingType - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetMaker() *ItemListed_Account { - if x != nil { - return x.Maker - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetOrderHash() string { - if x != nil { - return x.OrderHash - } - return "" -} - -func (x *ItemListed_ItemListedPayload) GetPaymentToken() *ItemListed_PaymentToken { - if x != nil { - return x.PaymentToken - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetProtocolData() *ItemListed_ProtocolData { - if x != nil { - return x.ProtocolData - } - return nil -} - -func (x *ItemListed_ItemListedPayload) GetQuantity() uint32 { - if x != nil { - return x.Quantity - } - return 0 -} - -func (x *ItemListed_ItemListedPayload) GetTaker() *ItemListed_Account { - if x != nil { - return x.Taker - } - return nil -} - -var File_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto protoreflect.FileDescriptor - -var file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDesc = []byte{ - 0x0a, 0x32, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x6e, 0x65, 0x6d, 0x6f, 0x2f, - 0x67, 0x6c, 0x6f, 0x6f, 0x6d, 0x62, 0x65, 0x72, 0x67, 0x2f, 0x67, 0x62, 0x67, 0x72, 0x70, 0x63, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6c, 0x6f, 0x6f, 0x6d, 0x62, 0x65, 0x72, 0x67, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x03, 0x67, 0x65, 0x6e, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x67, 0x0a, 0x13, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x0a, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0e, 0x32, 0x0e, 0x2e, - 0x67, 0x65, 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x76, 0x0a, 0x05, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x2c, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x67, 0x65, - 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x22, 0x8e, 0x01, 0x0a, 0x0c, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x32, 0x0a, 0x0b, 0x69, 0x74, 0x65, 0x6d, 0x5f, 0x6c, 0x69, 0x73, - 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x67, 0x65, 0x6e, 0x2e, - 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0a, 0x69, 0x74, - 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x11, 0x69, 0x74, 0x65, 0x6d, - 0x5f, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, - 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x42, 0x69, 0x64, 0x48, 0x00, 0x52, 0x0f, 0x69, 0x74, 0x65, - 0x6d, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x42, 0x69, 0x64, 0x42, 0x06, 0x0a, 0x04, - 0x6b, 0x69, 0x6e, 0x64, 0x22, 0x53, 0x0a, 0x0f, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x63, 0x65, - 0x69, 0x76, 0x65, 0x64, 0x42, 0x69, 0x64, 0x1a, 0x21, 0x0a, 0x0d, 0x43, 0x6f, 0x6c, 0x6c, 0x65, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x75, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x75, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x75, 0x68, 0x1a, 0x1d, 0x0a, 0x09, 0x43, 0x68, - 0x61, 0x69, 0x6e, 0x4d, 0x6f, 0x65, 0x70, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x61, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x61, 0x22, 0xca, 0x13, 0x0a, 0x0a, 0x49, 0x74, - 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x0a, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x67, - 0x65, 0x6e, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x09, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3b, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, - 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, - 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x33, 0x0a, 0x07, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x74, 0x41, 0x74, 0x1a, 0x20, 0x0a, 0x0a, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x73, 0x6c, 0x75, 0x67, 0x1a, 0x1b, 0x0a, 0x05, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x1a, 0x83, 0x01, 0x0a, 0x08, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x23, 0x0a, 0x0d, 0x61, 0x6e, 0x69, 0x6d, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x61, 0x6e, - 0x69, 0x6d, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x6d, - 0x61, 0x67, 0x65, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x69, - 0x6d, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x1a, 0x9e, - 0x01, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x2b, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, - 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x05, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x12, 0x34, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, - 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x6e, 0x66, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, 0x66, 0x74, 0x49, - 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x70, 0x65, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x6e, 0x6b, 0x1a, - 0x23, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x1a, 0xab, 0x01, 0x0a, 0x0d, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x12, 0x1a, 0x0a, 0x08, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x64, 0x65, 0x63, 0x69, 0x6d, 0x61, 0x6c, 0x73, 0x12, 0x1b, 0x0a, 0x09, - 0x65, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x74, 0x68, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x64, 0x5f, 0x70, 0x72, 0x69, - 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x64, 0x50, 0x72, 0x69, - 0x63, 0x65, 0x1a, 0xd3, 0x01, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x41, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x14, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, - 0x4f, 0x72, 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x14, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4f, 0x72, 0x43, 0x72, - 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x74, 0x65, 0x6d, 0x54, 0x79, - 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x74, 0x65, 0x6d, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, - 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0xad, 0x01, 0x0a, 0x05, 0x4f, 0x66, 0x66, - 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x65, 0x6e, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x65, 0x6e, 0x64, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, - 0x12, 0x32, 0x0a, 0x14, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4f, 0x72, - 0x43, 0x72, 0x69, 0x74, 0x65, 0x72, 0x69, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, - 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x4f, 0x72, 0x43, 0x72, 0x69, 0x74, - 0x65, 0x72, 0x69, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x69, 0x74, 0x65, 0x6d, 0x54, 0x79, 0x70, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x74, 0x65, 0x6d, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x41, 0x6d, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x1a, 0xb6, 0x03, 0x0a, 0x0a, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x64, 0x75, - 0x69, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x6f, 0x6e, - 0x64, 0x75, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x73, 0x69, - 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, - 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, - 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54, 0x69, 0x6d, 0x65, - 0x12, 0x2b, 0x0a, 0x05, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, - 0x2e, 0x4f, 0x66, 0x66, 0x65, 0x72, 0x52, 0x05, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x12, 0x18, 0x0a, - 0x07, 0x6f, 0x66, 0x66, 0x65, 0x72, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6f, 0x66, 0x66, 0x65, 0x72, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x61, 0x6c, 0x74, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x04, 0x73, 0x61, 0x6c, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x61, - 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x48, 0x0a, 0x1f, 0x74, 0x6f, 0x74, 0x61, 0x6c, - 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x1f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x43, - 0x6f, 0x6e, 0x73, 0x69, 0x64, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x74, 0x65, 0x6d, - 0x73, 0x12, 0x12, 0x0a, 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x7a, 0x6f, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x7a, 0x6f, 0x6e, 0x65, 0x48, 0x61, 0x73, - 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x7a, 0x6f, 0x6e, 0x65, 0x48, 0x61, 0x73, - 0x68, 0x1a, 0xc5, 0x01, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, - 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, - 0x65, 0x72, 0x73, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, - 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x5a, 0x0a, - 0x2b, 0x75, 0x73, 0x65, 0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x5f, 0x6d, 0x69, 0x6e, 0x74, 0x5f, 0x61, - 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x73, 0x68, 0x61, 0x72, 0x65, - 0x64, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x25, 0x75, 0x73, 0x65, 0x4c, 0x61, 0x7a, 0x79, 0x4d, 0x69, 0x6e, 0x74, 0x41, - 0x64, 0x61, 0x70, 0x74, 0x65, 0x72, 0x46, 0x6f, 0x72, 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, - 0x74, 0x6f, 0x72, 0x65, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x1a, 0xda, 0x05, 0x0a, 0x11, 0x49, 0x74, - 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, - 0x1d, 0x0a, 0x0a, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x70, 0x72, 0x69, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x61, 0x73, 0x65, 0x50, 0x72, 0x69, 0x63, 0x65, 0x12, 0x3a, - 0x0a, 0x0a, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x73, - 0x74, 0x65, 0x64, 0x2e, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x43, 0x0a, 0x0f, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, - 0x43, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, - 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x69, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x44, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x70, 0x72, 0x69, 0x76, 0x61, - 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x50, 0x72, 0x69, 0x76, - 0x61, 0x74, 0x65, 0x12, 0x28, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, - 0x65, 0x64, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x3d, 0x0a, - 0x0c, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x0b, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x44, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x0c, - 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52, 0x0b, 0x6c, 0x69, 0x73, 0x74, 0x69, 0x6e, - 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x6d, 0x61, 0x6b, 0x65, 0x72, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x4c, - 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x05, 0x6d, - 0x61, 0x6b, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x5f, 0x68, 0x61, - 0x73, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x48, - 0x61, 0x73, 0x68, 0x12, 0x42, 0x0a, 0x0d, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x74, - 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, - 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, 0x50, 0x61, 0x79, 0x6d, - 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x0c, 0x70, 0x61, 0x79, 0x6d, 0x65, - 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x42, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x63, 0x6f, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, - 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0c, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x71, - 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x71, - 0x75, 0x61, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x05, 0x74, 0x61, 0x6b, 0x65, 0x72, - 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x49, 0x74, 0x65, - 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x64, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, - 0x05, 0x74, 0x61, 0x6b, 0x65, 0x72, 0x2a, 0x6c, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, - 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x54, 0x45, 0x4d, 0x5f, 0x4c, 0x49, 0x53, 0x54, 0x45, 0x44, 0x10, - 0x01, 0x12, 0x15, 0x0a, 0x11, 0x49, 0x54, 0x45, 0x4d, 0x5f, 0x52, 0x45, 0x43, 0x45, 0x49, 0x56, - 0x45, 0x44, 0x5f, 0x42, 0x49, 0x44, 0x10, 0x02, 0x12, 0x14, 0x0a, 0x10, 0x4d, 0x45, 0x54, 0x41, - 0x44, 0x41, 0x54, 0x41, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x10, 0x03, 0x12, 0x14, - 0x0a, 0x10, 0x43, 0x4f, 0x4c, 0x4c, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4f, 0x46, 0x46, - 0x45, 0x52, 0x10, 0x04, 0x32, 0x83, 0x01, 0x0a, 0x09, 0x47, 0x6c, 0x6f, 0x6f, 0x6d, 0x62, 0x65, - 0x72, 0x67, 0x12, 0x3f, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, - 0x18, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x00, 0x12, 0x35, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x12, 0x18, 0x2e, 0x67, 0x65, 0x6e, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0a, 0x2e, 0x67, 0x65, 0x6e, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x00, 0x30, 0x01, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x65, 0x6e, 0x6c, 0x65, 0x62, 0x2f, - 0x67, 0x6c, 0x6f, 0x6f, 0x6d, 0x62, 0x65, 0x72, 0x67, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, - 0x61, 0x6c, 0x2f, 0x6e, 0x65, 0x6d, 0x6f, 0x2f, 0x67, 0x6c, 0x6f, 0x6f, 0x6d, 0x62, 0x65, 0x72, - 0x67, 0x2f, 0x67, 0x62, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescOnce sync.Once - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescData = file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDesc -) - -func file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescGZIP() []byte { - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescOnce.Do(func() { - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescData) - }) - return file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDescData -} - -var file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes = make([]protoimpl.MessageInfo, 18) -var file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_goTypes = []interface{}{ - (EventType)(0), // 0: gen.EventType - (*SubscriptionRequest)(nil), // 1: gen.SubscriptionRequest - (*Event)(nil), // 2: gen.Event - (*EventPayload)(nil), // 3: gen.EventPayload - (*ItemReceivedBid)(nil), // 4: gen.ItemReceivedBid - (*ItemListed)(nil), // 5: gen.ItemListed - (*ItemReceivedBid_CollectionMuh)(nil), // 6: gen.ItemReceivedBid.CollectionMuh - (*ItemReceivedBid_ChainMoep)(nil), // 7: gen.ItemReceivedBid.ChainMoep - (*ItemListed_Collection)(nil), // 8: gen.ItemListed.Collection - (*ItemListed_Chain)(nil), // 9: gen.ItemListed.Chain - (*ItemListed_Metadata)(nil), // 10: gen.ItemListed.Metadata - (*ItemListed_Item)(nil), // 11: gen.ItemListed.Item - (*ItemListed_Account)(nil), // 12: gen.ItemListed.Account - (*ItemListed_PaymentToken)(nil), // 13: gen.ItemListed.Payment_token - (*ItemListed_Consideration)(nil), // 14: gen.ItemListed.Consideration - (*ItemListed_Offer)(nil), // 15: gen.ItemListed.Offer - (*ItemListed_Parameters)(nil), // 16: gen.ItemListed.Parameters - (*ItemListed_ProtocolData)(nil), // 17: gen.ItemListed.Protocol_data - (*ItemListed_ItemListedPayload)(nil), // 18: gen.ItemListed.ItemListedPayload - (*timestamppb.Timestamp)(nil), // 19: google.protobuf.Timestamp - (*anypb.Any)(nil), // 20: google.protobuf.Any - (*emptypb.Empty)(nil), // 21: google.protobuf.Empty -} -var file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_depIdxs = []int32{ - 0, // 0: gen.SubscriptionRequest.eventTypes:type_name -> gen.EventType - 0, // 1: gen.Event.eventType:type_name -> gen.EventType - 3, // 2: gen.Event.payload:type_name -> gen.EventPayload - 5, // 3: gen.EventPayload.item_listed:type_name -> gen.ItemListed - 4, // 4: gen.EventPayload.item_received_bid:type_name -> gen.ItemReceivedBid - 0, // 5: gen.ItemListed.event_type:type_name -> gen.EventType - 18, // 6: gen.ItemListed.payload:type_name -> gen.ItemListed.ItemListedPayload - 19, // 7: gen.ItemListed.sent_at:type_name -> google.protobuf.Timestamp - 9, // 8: gen.ItemListed.Item.chain:type_name -> gen.ItemListed.Chain - 10, // 9: gen.ItemListed.Item.metadata:type_name -> gen.ItemListed.Metadata - 14, // 10: gen.ItemListed.Parameters.consideration:type_name -> gen.ItemListed.Consideration - 15, // 11: gen.ItemListed.Parameters.offer:type_name -> gen.ItemListed.Offer - 16, // 12: gen.ItemListed.Protocol_data.parameters:type_name -> gen.ItemListed.Parameters - 8, // 13: gen.ItemListed.ItemListedPayload.collection:type_name -> gen.ItemListed.Collection - 19, // 14: gen.ItemListed.ItemListedPayload.event_timestamp:type_name -> google.protobuf.Timestamp - 19, // 15: gen.ItemListed.ItemListedPayload.expiration_date:type_name -> google.protobuf.Timestamp - 11, // 16: gen.ItemListed.ItemListedPayload.item:type_name -> gen.ItemListed.Item - 19, // 17: gen.ItemListed.ItemListedPayload.listing_date:type_name -> google.protobuf.Timestamp - 20, // 18: gen.ItemListed.ItemListedPayload.listing_type:type_name -> google.protobuf.Any - 12, // 19: gen.ItemListed.ItemListedPayload.maker:type_name -> gen.ItemListed.Account - 13, // 20: gen.ItemListed.ItemListedPayload.payment_token:type_name -> gen.ItemListed.Payment_token - 17, // 21: gen.ItemListed.ItemListedPayload.protocol_data:type_name -> gen.ItemListed.Protocol_data - 12, // 22: gen.ItemListed.ItemListedPayload.taker:type_name -> gen.ItemListed.Account - 1, // 23: gen.Gloomberg.Subscribe:input_type -> gen.SubscriptionRequest - 1, // 24: gen.Gloomberg.GetEvents:input_type -> gen.SubscriptionRequest - 21, // 25: gen.Gloomberg.Subscribe:output_type -> google.protobuf.Empty - 2, // 26: gen.Gloomberg.GetEvents:output_type -> gen.Event - 25, // [25:27] is the sub-list for method output_type - 23, // [23:25] is the sub-list for method input_type - 23, // [23:23] is the sub-list for extension type_name - 23, // [23:23] is the sub-list for extension extendee - 0, // [0:23] is the sub-list for field type_name -} - -func init() { file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_init() } -func file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_init() { - if File_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*SubscriptionRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Event); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*EventPayload); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemReceivedBid); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemReceivedBid_CollectionMuh); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemReceivedBid_ChainMoep); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_Collection); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_Chain); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_Metadata); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_Item); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_Account); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_PaymentToken); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_Consideration); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_Offer); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_Parameters); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_ProtocolData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ItemListed_ItemListedPayload); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes[2].OneofWrappers = []interface{}{ - (*EventPayload_ItemListed)(nil), - (*EventPayload_ItemReceivedBid)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDesc, - NumEnums: 1, - NumMessages: 18, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_goTypes, - DependencyIndexes: file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_depIdxs, - EnumInfos: file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_enumTypes, - MessageInfos: file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_msgTypes, - }.Build() - File_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto = out.File - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_rawDesc = nil - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_goTypes = nil - file_internal_nemo_gloomberg_gbgrpc_gen_gloomberg_proto_depIdxs = nil -} diff --git a/internal/nemo/gloomberg/gbgrpc/gen/gloomberg.proto b/internal/nemo/gloomberg/gbgrpc/gen/gloomberg.proto deleted file mode 100644 index e0ce706..0000000 --- a/internal/nemo/gloomberg/gbgrpc/gen/gloomberg.proto +++ /dev/null @@ -1,160 +0,0 @@ -// dakma-dev | gloomberg.proto - -syntax = "proto3"; - -import "google/protobuf/timestamp.proto"; -import "google/protobuf/any.proto"; -import "google/protobuf/empty.proto"; - -option go_package = "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc/gen"; - -package gen; - -// Interface exported by the server. -service Gloomberg { - rpc Subscribe(SubscriptionRequest) returns (google.protobuf.Empty) {} - rpc GetEvents(SubscriptionRequest) returns (stream Event) {} -} - -enum EventType { - UNKNOWN = 0; - ITEM_LISTED = 1; - ITEM_RECEIVED_BID = 2; - METADATA_UPDATED = 3; - COLLECTION_OFFER = 4; -} - -message SubscriptionRequest { - repeated EventType eventTypes = 1; - repeated string collections = 2; -} - -message Event { - // The name of the feature. - string name = 1; - - // The event type. - EventType eventType = 2; - - // The event payload. - EventPayload payload = 3; -} - -message EventPayload { - // The kind of value. - oneof kind { - ItemListed item_listed = 1; - ItemReceivedBid item_received_bid = 2; - // MetadataUpdated metadata_updated = 3; - // CollectionOffer collection_offer = 4; - } -} - -message ItemReceivedBid { - message CollectionMuh { - string muh = 1; - } - - message ChainMoep { - string mia = 1; - } -} - -// Event emitted when an item is listed for sale. -// -// This event is emitted when an item is listed for sale on opensea. -message ItemListed { - message Collection { - string slug = 1; - } - - message Chain { - string name = 1; - } - - message Metadata { - string animation_url = 1; - string image_url = 2; - string metadata_url = 3; - string name = 4; - } - - message Item { - Chain chain = 1; - Metadata metadata = 2; - string nft_id = 3; - string permalink = 4; - } - - message Account { - string address = 1; - } - - message Payment_token { - string address = 1; - uint32 decimals = 2; - string eth_price = 3; - string name = 4; - string symbol = 5; - string usd_price = 6; - } - - message Consideration { - uint64 endAmount = 1; - uint32 identifierOrCriteria = 2; - uint32 itemType = 3; - string recipient = 4; - uint64 startAmount = 5; - string token = 6; - } - - message Offer { - uint32 endAmount = 1; - uint64 identifierOrCriteria = 2; - uint32 itemType = 3; - uint32 startAmount = 4; - string token = 5; - } - - message Parameters { - string conduitKey = 1; - repeated Consideration consideration = 2; - uint32 counter = 3; - uint32 endTime = 4; - repeated Offer offer = 5; - string offerer = 6; - uint32 orderType = 7; - uint64 salt = 8; - uint32 startTime = 9; - uint32 totalOriginalConsiderationItems = 10; - string zone = 11; - string zoneHash = 12; - } - - message Protocol_data { - Parameters parameters = 1; - string signature = 2; - bool use_lazy_mint_adapter_for_shared_storefront = 3; - } - - message ItemListedPayload { - string base_price = 1; - Collection collection = 2; - google.protobuf.Timestamp event_timestamp = 3; - google.protobuf.Timestamp expiration_date = 4; - bool is_private = 5; - Item item = 6; - google.protobuf.Timestamp listing_date = 7; - google.protobuf.Any listing_type = 8; - Account maker = 9; - string order_hash = 10; - Payment_token payment_token = 11; - Protocol_data protocol_data = 12; - uint32 quantity = 13; - Account taker = 14; - } - - EventType event_type = 1; - ItemListedPayload payload = 2; - google.protobuf.Timestamp sent_at = 3; -} diff --git a/internal/nemo/gloomberg/gbgrpc/gen/gloomberg_grpc.pb.go b/internal/nemo/gloomberg/gbgrpc/gen/gloomberg_grpc.pb.go deleted file mode 100644 index cfa504d..0000000 --- a/internal/nemo/gloomberg/gbgrpc/gen/gloomberg_grpc.pb.go +++ /dev/null @@ -1,177 +0,0 @@ -// dakma-dev | gloomberg.proto - -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v4.23.4 -// source: internal/nemo/gloomberg/gbgrpc/gen/gloomberg.proto - -package gen - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - emptypb "google.golang.org/protobuf/types/known/emptypb" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.32.0 or later. -const _ = grpc.SupportPackageIsVersion7 - -const ( - Gloomberg_Subscribe_FullMethodName = "/gen.Gloomberg/Subscribe" - Gloomberg_GetEvents_FullMethodName = "/gen.Gloomberg/GetEvents" -) - -// GloombergClient is the client API for Gloomberg service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type GloombergClient interface { - Subscribe(ctx context.Context, in *SubscriptionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - GetEvents(ctx context.Context, in *SubscriptionRequest, opts ...grpc.CallOption) (Gloomberg_GetEventsClient, error) -} - -type gloombergClient struct { - cc grpc.ClientConnInterface -} - -func NewGloombergClient(cc grpc.ClientConnInterface) GloombergClient { - return &gloombergClient{cc} -} - -func (c *gloombergClient) Subscribe(ctx context.Context, in *SubscriptionRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, Gloomberg_Subscribe_FullMethodName, in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *gloombergClient) GetEvents(ctx context.Context, in *SubscriptionRequest, opts ...grpc.CallOption) (Gloomberg_GetEventsClient, error) { - stream, err := c.cc.NewStream(ctx, &Gloomberg_ServiceDesc.Streams[0], Gloomberg_GetEvents_FullMethodName, opts...) - if err != nil { - return nil, err - } - x := &gloombergGetEventsClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type Gloomberg_GetEventsClient interface { - Recv() (*Event, error) - grpc.ClientStream -} - -type gloombergGetEventsClient struct { - grpc.ClientStream -} - -func (x *gloombergGetEventsClient) Recv() (*Event, error) { - m := new(Event) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// GloombergServer is the server API for Gloomberg service. -// All implementations must embed UnimplementedGloombergServer -// for forward compatibility -type GloombergServer interface { - Subscribe(context.Context, *SubscriptionRequest) (*emptypb.Empty, error) - GetEvents(*SubscriptionRequest, Gloomberg_GetEventsServer) error - mustEmbedUnimplementedGloombergServer() -} - -// UnimplementedGloombergServer must be embedded to have forward compatible implementations. -type UnimplementedGloombergServer struct { -} - -func (UnimplementedGloombergServer) Subscribe(context.Context, *SubscriptionRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method Subscribe not implemented") -} -func (UnimplementedGloombergServer) GetEvents(*SubscriptionRequest, Gloomberg_GetEventsServer) error { - return status.Errorf(codes.Unimplemented, "method GetEvents not implemented") -} -func (UnimplementedGloombergServer) mustEmbedUnimplementedGloombergServer() {} - -// UnsafeGloombergServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to GloombergServer will -// result in compilation errors. -type UnsafeGloombergServer interface { - mustEmbedUnimplementedGloombergServer() -} - -func RegisterGloombergServer(s grpc.ServiceRegistrar, srv GloombergServer) { - s.RegisterService(&Gloomberg_ServiceDesc, srv) -} - -func _Gloomberg_Subscribe_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SubscriptionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(GloombergServer).Subscribe(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: Gloomberg_Subscribe_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(GloombergServer).Subscribe(ctx, req.(*SubscriptionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Gloomberg_GetEvents_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(SubscriptionRequest) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(GloombergServer).GetEvents(m, &gloombergGetEventsServer{stream}) -} - -type Gloomberg_GetEventsServer interface { - Send(*Event) error - grpc.ServerStream -} - -type gloombergGetEventsServer struct { - grpc.ServerStream -} - -func (x *gloombergGetEventsServer) Send(m *Event) error { - return x.ServerStream.SendMsg(m) -} - -// Gloomberg_ServiceDesc is the grpc.ServiceDesc for Gloomberg service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var Gloomberg_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "gen.Gloomberg", - HandlerType: (*GloombergServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "Subscribe", - Handler: _Gloomberg_Subscribe_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "GetEvents", - Handler: _Gloomberg_GetEvents_Handler, - ServerStreams: true, - }, - }, - Metadata: "internal/nemo/gloomberg/gbgrpc/gen/gloomberg.proto", -} diff --git a/internal/nemo/gloomberg/gbgrpc/grpc.go b/internal/nemo/gloomberg/gbgrpc/grpc.go deleted file mode 100644 index 70352f8..0000000 --- a/internal/nemo/gloomberg/gbgrpc/grpc.go +++ /dev/null @@ -1,255 +0,0 @@ -package gbgrpc - -import ( - context "context" - "fmt" - "net" - "time" - - "github.com/benleb/gloomberg/internal/degendb" - "github.com/benleb/gloomberg/internal/nemo/gloomberg" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc/gen" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/remote" - "github.com/benleb/gloomberg/internal/nemo/price" - "github.com/benleb/gloomberg/internal/seawa" - "github.com/benleb/gloomberg/internal/style" - "github.com/charmbracelet/lipgloss" - "github.com/charmbracelet/log" - "github.com/spf13/viper" - "google.golang.org/grpc" - emptypb "google.golang.org/protobuf/types/known/emptypb" - "google.golang.org/protobuf/types/known/timestamppb" -) - -type GloombergGRPC struct { - gb *gloomberg.Gloomberg - sw *seawa.SeaWatcher - - // server - *remote.ClientGRPC - - gen.UnimplementedGloombergServer - - // grpcServer *grpc.Server -} - -// new GloombergGRPC. -func NewGloombergGRPC(gb *gloomberg.Gloomberg, sw *seawa.SeaWatcher) *GloombergGRPC { - gloombergGRPC := &GloombergGRPC{ - gb: gb, - sw: sw, - } - - // get server config - listenHost := viper.GetString("grpc.server.listenAddress") - port := viper.GetUint16("grpc.server.port") - serverAddress := fmt.Sprintf("%s:%d", listenHost, port) - - // run grpc server - go func() { - for { - log.Error(listen(serverAddress, gloombergGRPC)) - - time.Sleep(time.Second * 2) - } - }() - - if viper.GetBool("grpc.client.enabled") { - gloomberg.Prf("starting grpc client...") - - // go seawa.GetEvents() - - // clientGRPC := remote.NewClient(fmt.Sprintf("%s:%d", viper.GetString("grpc.client.host"), viper.GetUint("grpc.client.port"))) - // log.Printf("clientGRPC: %+v", clientGRPC) - // clientGRPC.GetEvents() - } - - return gloombergGRPC -} - -// connect to grpc server. -func listen(serverAddress string, gloombergGRPC *GloombergGRPC) error { - // configure listener - grpcListener, err := net.Listen("tcp", serverAddress) - if err != nil { - log.Errorf("failed to listen: %v", err) - } - - // configure server - var opts []grpc.ServerOption - if creds, err := gloomberg.GetTLSCredentialsWithoutClientAuth(); err == nil { - opts = []grpc.ServerOption{grpc.Creds(creds)} - } - - // run grpc server - grpcServer := grpc.NewServer(opts...) - - gen.RegisterGloombergServer(grpcServer, gloombergGRPC) - - return grpcServer.Serve(grpcListener) -} - -func StartServer(gb *gloomberg.Gloomberg, sw *seawa.SeaWatcher) { - gloombergGRPC := &GloombergGRPC{ - gb: gb, - sw: sw, - } - - // get config - listenHost := viper.GetString("grpc.server.listenAddress") - port := viper.GetUint16("grpc.server.port") - serverAddress := fmt.Sprintf("%s:%d", listenHost, port) - - go func() { - for { - log.Error(listen(serverAddress, gloombergGRPC)) - - time.Sleep(time.Second * 2) - } - }() - // // configure listener - // grpcListener, err := net.Listen("tcp", serverAddress) - // if err != nil { - // log.Errorf("failed to listen: %v", err) - // } - - // // configure server - // var opts []grpc.ServerOption - // if creds, err := gloomberg.GetTLSCredentialsWithoutClientAuth(); err == nil { - // opts = []grpc.ServerOption{grpc.Creds(creds)} - // } - - // // run grpc server - // go func() { - // grpcServer := grpc.NewServer(opts...) - - // gen.RegisterGloombergServer(grpcServer, gloombergGRPC) - - // go log.Fatal(grpcServer.Serve(grpcListener)) - // }() - - sw.Prf("grpc server running on %+v", style.BoldAlmostWhite(serverAddress)) -} - -func (gg *GloombergGRPC) Subscribe(_ context.Context, req *gen.SubscriptionRequest) (*emptypb.Empty, error) { - availableEventTypes := []gen.EventType{gen.EventType_ITEM_LISTED, gen.EventType_METADATA_UPDATED, gen.EventType_ITEM_RECEIVED_BID, gen.EventType_COLLECTION_OFFER} //nolint:nosnakecase // ItemMetadataUpdated} // ItemMetadataUpdated, ItemCancelled - - req.EventTypes = availableEventTypes - - gg.sw.Prf("received subscription request for %s collections/slugs (%s types each)...", style.BoldAlmostWhite(fmt.Sprint(len(req.Collections))), style.BoldAlmostWhite(fmt.Sprint(len(req.EventTypes)))) - - newEventSubscriptions := uint64(0) - - go func() { - newEventSubscriptions = gg.sw.SubscribeForSlugs(req.Collections, req.EventTypes) - - gg.sw.Prf( - "successfully subscribed to %s new collections/slugs | total subscribed collections: %s", - style.AlmostWhiteStyle.Render(fmt.Sprint(newEventSubscriptions)), - style.AlmostWhiteStyle.Render(fmt.Sprint(len(gg.sw.ActiveSubscriptions()))), - ) - }() - - return &emptypb.Empty{}, nil -} - -func (gg *GloombergGRPC) GetEvents(req *gen.SubscriptionRequest, stream gen.Gloomberg_GetEventsServer) error { //nolint:nosnakecase - // availableEventTypes := []gen.EventType{gen.EventType_ITEM_LISTED, gen.EventType_METADATA_UPDATED, gen.EventType_ITEM_RECEIVED_BID, gen.EventType_COLLECTION_OFFER} //nolint:nosnakecase // ItemMetadataUpdated} // ItemMetadataUpdated, ItemCancelled - - // req.EventTypes = availableEventTypes - - // gg.sw.Prf("received subscription request for %s collections/slugs (%s types each)...", style.BoldAlmostWhite(fmt.Sprint(len(req.Collections))), style.BoldAlmostWhite(fmt.Sprint(len(req.EventTypes)))) - - // go func() { - // newEventSubscriptions := gg.sw.SubscribeForSlugs(req.Collections, req.EventTypes) - - // gg.sw.Prf( - // "successfully subscribed to %s new collections/slugs | total subscribed collections: %s", - // style.AlmostWhiteStyle.Render(fmt.Sprint(newEventSubscriptions)), - // style.AlmostWhiteStyle.Render(fmt.Sprint(len(gg.sw.ActiveSubscriptions()))), - // ) - // }() - - go gg.Subscribe(context.Background(), req) //nolint:errcheck - - chanItemListed := gg.gb.SubscribeItemListed() - defer gg.gb.UnsubscribeItemListed(chanItemListed) - - for event := range chanItemListed { - // fix missing collection name in item name (example: INS1D3RS) - if collectionName, _ := gg.gb.Rueidi.GetCachedContractName(context.Background(), event.Payload.Item.ContractAddress()); collectionName != "" { - event.Payload.Item.Name = fmt.Sprintf("%s %s%s", collectionName, "#", event.Payload.Item.TokenID()) - } - - // transform *models.ItemListed event to ItemListed grpc message - itemListed := &gen.ItemListed{ - EventType: gen.EventType(gen.EventType_value[event.EventType]), //nolint:nosnakecase - SentAt: ×tamppb.Timestamp{Seconds: event.SentAt.Unix()}, - - Payload: &gen.ItemListed_ItemListedPayload{ //nolint:nosnakecase - Item: &gen.ItemListed_Item{ //nolint:nosnakecase - Chain: &gen.ItemListed_Chain{Name: "ethereum"}, //nolint:nosnakecase - NftId: event.Payload.Item.String(), - Permalink: event.Payload.Item.Permalink, - Metadata: &gen.ItemListed_Metadata{ //nolint:nosnakecase - Name: event.Payload.Item.Name, - ImageUrl: event.Payload.Item.ImageURL, - AnimationUrl: event.Payload.Item.AnimationURL, - MetadataUrl: event.Payload.Item.MetadataURL, - }, - }, - BasePrice: event.Payload.BasePrice.String(), - Collection: &gen.ItemListed_Collection{Slug: event.Payload.Slug}, //nolint:nosnakecase - IsPrivate: event.Payload.IsPrivate, - ListingDate: ×tamppb.Timestamp{Seconds: event.Payload.ListingDate.Unix()}, - EventTimestamp: ×tamppb.Timestamp{Seconds: event.Payload.EventTimestamp.Unix()}, - Quantity: uint32(event.Payload.Quantity), - Maker: &gen.ItemListed_Account{Address: event.Payload.Maker.Address.String()}, //nolint:nosnakecase - Taker: &gen.ItemListed_Account{Address: event.Payload.Taker.Address.String()}, //nolint:nosnakecase - ExpirationDate: ×tamppb.Timestamp{Seconds: event.Payload.ExpirationDate.Unix()}, - OrderHash: event.Payload.OrderHash.String(), - PaymentToken: &gen.ItemListed_PaymentToken{ //nolint:nosnakecase - Address: event.Payload.Address.String(), - Symbol: event.Payload.Symbol, - Name: event.Payload.Name, - Decimals: uint32(event.Payload.Decimals), - UsdPrice: event.Payload.UsdPrice, - }, - }, - } - - ev := &gen.Event{ - EventType: gen.EventType_ITEM_LISTED, //nolint:nosnakecase - Payload: &gen.EventPayload{ - Kind: &gen.EventPayload_ItemListed{ //nolint:nosnakecase - ItemListed: itemListed, - }, - }, - } - - // gg.sw.Prf("sending event: %+v", ev) - - if err := stream.Send(ev); err != nil { - log.Printf("โŒ error sending event to grpc client: %s", err) - - return err - } - - // output to terminal - collectionPrimaryStyle := lipgloss.NewStyle().Foreground(style.GenerateColorWithSeed(event.Payload.Item.NftID.ContractAddress().Hash().Big().Int64())) - - price := price.NewPrice(event.Payload.BasePrice) - fmtCurrencySymbol := collectionPrimaryStyle.Bold(false).Render("ฮž") - fmtPrice := style.BoldAlmostWhite(fmt.Sprintf("%5.2f", price.Ether())) + fmtCurrencySymbol - - fmtItemName := collectionPrimaryStyle.Bold(true).Render(event.Payload.Item.Name) - - fmtItemLink := style.TerminalLink(event.Payload.Item.Permalink, fmtItemName) - - eventType := degendb.EventType(degendb.GetEventType(event.EventType)) - - gg.sw.Prf("%s %s %s", eventType.Icon(), fmtPrice, fmtItemLink) - } - - return nil -} diff --git a/internal/nemo/gloomberg/gloomberg.go b/internal/nemo/gloomberg/gloomberg.go index 16b20d9..0acca4e 100644 --- a/internal/nemo/gloomberg/gloomberg.go +++ b/internal/nemo/gloomberg/gloomberg.go @@ -46,9 +46,6 @@ type Gloomberg struct { CurrentGasPriceGwei uint64 CurrentOnlineWebUsers uint64 - // grpc - // grpcClient *remote.ClientGRPC - *eventHub // GloomHub *gloomhub Jobs *jobs.Runner @@ -181,7 +178,15 @@ func New() *Gloomberg { return gb } -func (gb *Gloomberg) SendSlugsToServer() { +func (gb *Gloomberg) PublishOwnCollectionsSlugs() { + gb.publisheCollectionSlugsViaRedis(gb.CollectionDB.OpenseaSlugs()) +} + +func (gb *Gloomberg) PublishCollectionsSlugs(slugs []string) { + gb.publisheCollectionSlugsViaRedis(slugs) +} + +func (gb *Gloomberg) publisheCollectionSlugsViaRedis(slugs []string) { // to enable multiple users to use the central gloomberg instance for events from opensea, // we first send the slugs of 'our' collections to the events-subscriptions channel. // the central gloomberg instance then creates a subscription on the opensea @@ -193,14 +198,13 @@ func (gb *Gloomberg) SendSlugsToServer() { return } - slugs := gb.CollectionDB.OpenseaSlugs() if len(slugs) == 0 { gbl.Log.Warn("โŒ no slugs to send to gloomberg server") return } - log.Debugf("๐Ÿ“ข sending %s collection slugs to gloomberg server", style.BoldStyle.Render(fmt.Sprint(len(slugs)))) + log.Debugf("๐Ÿ‘” sending %s collection slugs to gloomberg server", style.BoldStyle.Render(fmt.Sprint(len(slugs)))) mgmtEvent := &models.MgmtEvent{Action: models.Subscribe, Slugs: slugs} @@ -219,7 +223,7 @@ func (gb *Gloomberg) SendSlugsToServer() { if gb.Rdb.Do(context.Background(), gb.Rdb.B().Publish().Channel(internal.PubSubSeaWatcherMgmt).Message(string(jsonMgmtEvent)).Build()).Error() != nil { gbl.Log.Warnf("error publishing event to redis: %s", err.Error()) } else { - gbl.Log.Infof("๐Ÿ“ข sent %s collection slugs to %s", style.BoldStyle.Render(fmt.Sprint(len(slugs))), style.BoldStyle.Render(internal.PubSubSeaWatcherMgmt)) + gbl.Log.Infof("๐Ÿ‘” sent %s collection slugs to %s", style.BoldStyle.Render(fmt.Sprint(len(slugs))), style.BoldStyle.Render(internal.PubSubSeaWatcherMgmt)) } } } diff --git a/internal/nemo/gloomberg/remote/client.go b/internal/nemo/gloomberg/remote/client.go deleted file mode 100644 index 12f6113..0000000 --- a/internal/nemo/gloomberg/remote/client.go +++ /dev/null @@ -1,200 +0,0 @@ -package remote - -import ( - context "context" - "errors" - "fmt" - "io" - "math" - "math/big" - "strings" - "time" - - "github.com/benleb/gloomberg/internal/degendb" - "github.com/benleb/gloomberg/internal/gbl" - "github.com/benleb/gloomberg/internal/nemo/gloomberg" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc/gen" - seawaModels "github.com/benleb/gloomberg/internal/seawa/models" - "github.com/benleb/gloomberg/internal/style" - "github.com/charmbracelet/log" - "github.com/ethereum/go-ethereum/common" - "github.com/spf13/viper" - "google.golang.org/grpc" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/protobuf/encoding/protojson" -) - -type ClientGRPC struct { - grpcAddress string - opts []grpc.DialOption - - gen.GloombergClient -} - -var clientGRPC *ClientGRPC - -// New client. -func NewClient() *ClientGRPC { - if clientGRPC != nil { - return clientGRPC - } - - grpcAddress := fmt.Sprintf("%s:%d", viper.GetString("grpc.client.host"), viper.GetUint("grpc.client.port")) - - // grpc options - var opts []grpc.DialOption - - if creds := gloomberg.GetTLSClientCredentials(); creds != nil { - opts = append(opts, grpc.WithTransportCredentials(creds)) - } else { - opts = append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - - client := connect(grpcAddress, opts) - if client == nil { - return nil - } - - clientGRPC = &ClientGRPC{grpcAddress, opts, client} - - return clientGRPC -} - -func FetchEvents(gb *gloomberg.Gloomberg) { - // server to connect to - - failedAttempts := 0 - - for { - if failedAttempts > 0 { - // exponential backoff - waitTime := time.Second * time.Duration(math.Pow(2.0, float64(int(math.Min(float64(failedAttempts), 5))))) - - log.Printf("retrying to connect to gRPC in %.0f seconds...", waitTime.Seconds()) - - time.Sleep(waitTime) - } - - // connect - grpcClient := NewClient() - // grpcClient := connect(grpcAddress, opts) - - if grpcClient == nil { - log.Errorf("fail to connect to gRPC %s", style.BoldAlmostWhite(grpcClient.grpcAddress)) - - failedAttempts++ - - continue - } - - // get eventstream - gloomberg.Prf("subscribing via grpc to: %s", style.BoldAlmostWhite(degendb.Listing.OpenseaEventName())) - - subsriptionRequest := &gen.SubscriptionRequest{EventTypes: []gen.EventType{gen.EventType_ITEM_LISTED}, Collections: gb.CollectionDB.OpenseaSlugs()} //nolint:nosnakecase - stream, err := grpcClient.GetEvents(context.Background(), subsriptionRequest, grpc.WaitForReady(true)) - if err != nil { - log.Errorf("getting stream failed: %v, retrying", err) - - failedAttempts++ - - continue - } - - // reset failed attempts - failedAttempts = 0 - - for { - // get events - event, err := stream.Recv() - log.Debugf("๐Ÿ” client received: %+v", event) - - if err != nil { - if errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) { - log.Errorf("io.EOF error: %v", err) - } - - if event == nil { - log.Errorf("receiving event failed: %v | %+v", err, event) - } - - err := stream.CloseSend() - if err != nil { - log.Errorf("closing stream failed: %v", err) - } - - break - } - - basePrice, ok := new(big.Int).SetString(event.Payload.GetItemListed().Payload.BasePrice, 10) - if !ok { - log.Errorf("error parsing base price: %v", err) - - continue - } - - var itemListed seawaModels.ItemListed - - log.Debugf("๐Ÿ” creating itemListed struct: %+v", protojson.Format(event)) - - // transform event back to seawaModel.ItemListed - itemListed = seawaModels.ItemListed{ - EventType: strings.ToLower(event.EventType.String()), - SentAt: event.Payload.GetItemListed().SentAt.AsTime(), - Payload: seawaModels.ItemListedPayload{ - Item: seawaModels.Item{ - NftID: *seawaModels.ParseNftID(event.Payload.GetItemListed().Payload.Item.NftId), - Chain: seawaModels.Chain{Name: event.Payload.GetItemListed().Payload.Item.Chain.Name}, - Permalink: event.Payload.GetItemListed().Payload.Item.Permalink, - Metadata: seawaModels.Metadata{ - Name: event.Payload.GetItemListed().Payload.Item.Metadata.Name, - ImageURL: event.Payload.GetItemListed().Payload.Item.Metadata.ImageUrl, - AnimationURL: event.Payload.GetItemListed().Payload.Item.Metadata.AnimationUrl, - MetadataURL: event.Payload.GetItemListed().Payload.Item.Metadata.MetadataUrl, - }, - }, - IsPrivate: event.Payload.GetItemListed().Payload.IsPrivate, - ListingDate: event.Payload.GetItemListed().Payload.ListingDate.AsTime(), - EventPayload: seawaModels.EventPayload{ - EventTimestamp: event.Payload.GetItemListed().Payload.EventTimestamp.AsTime(), - BasePrice: basePrice, - Maker: seawaModels.Account{Address: common.HexToAddress(event.Payload.GetItemListed().Payload.Maker.Address)}, - Taker: seawaModels.Account{Address: common.HexToAddress(event.Payload.GetItemListed().Payload.Taker.Address)}, - Quantity: int(event.Payload.GetItemListed().Payload.Quantity), - OrderHash: common.HexToHash(event.Payload.GetItemListed().Payload.OrderHash), - ExpirationDate: event.Payload.GetItemListed().Payload.ExpirationDate.AsTime(), - CollectionCriteria: seawaModels.CollectionCriteria{Slug: event.Payload.GetItemListed().Payload.Collection.Slug}, - PaymentToken: seawaModels.PaymentToken{Address: common.HexToAddress(event.Payload.GetItemListed().Payload.PaymentToken.Address), Symbol: event.Payload.GetItemListed().Payload.PaymentToken.Symbol, Decimals: int(event.Payload.GetItemListed().Payload.PaymentToken.Decimals)}, - }, - }, - } - - // send event to the eventhub - gb.In.ItemListed <- &itemListed - - log.Debugf("๐Ÿ” sent to eventHub: %+v", itemListed) - } - } -} - -func connect(grpcAddress string, opts []grpc.DialOption) gen.GloombergClient { - // connect - gloomberg.Prf("connecting to gRPC %s...", style.BoldAlmostWhite(grpcAddress)) - conn, err := grpc.Dial(grpcAddress, opts...) - if err != nil { - gbl.Log.Warnf("fail to dial: %v", err) - - return nil - } - - // time needed to establish connection - time.Sleep(time.Millisecond * 337) - - // check connection state - if conn.GetState() != connectivity.Ready { - log.Errorf("fail to connect to gRPC %s", style.BoldAlmostWhite(grpcAddress)) - } - - // return client - return gen.NewGloombergClient(conn) -} diff --git a/internal/pusu/pusu.go b/internal/pusu/pusu.go index 9acb835..6ed9c1d 100644 --- a/internal/pusu/pusu.go +++ b/internal/pusu/pusu.go @@ -3,13 +3,18 @@ package pusu import ( "context" "encoding/json" + "fmt" + "strings" + "github.com/apex/log" "github.com/benleb/gloomberg/internal" + "github.com/benleb/gloomberg/internal/degendb" "github.com/benleb/gloomberg/internal/gbl" "github.com/benleb/gloomberg/internal/nemo/gloomberg" "github.com/benleb/gloomberg/internal/nemo/totra" - seawaModels "github.com/benleb/gloomberg/internal/seawa/models" - "github.com/ethereum/go-ethereum/common" + "github.com/benleb/gloomberg/internal/seawa/models" + "github.com/benleb/gloomberg/internal/style" + "github.com/mitchellh/mapstructure" "github.com/redis/rueidis" ) @@ -54,16 +59,22 @@ func SubscribeToListingsViaRedis(gb *gloomberg.Gloomberg) { // create a list of channels to subscribe to channels := make([]string, 0) - for _, collectionAddress := range slugAddresses { - channelPattern := internal.PubSubSeaWatcherListings + "/" + collectionAddress.Hex() + "/*" + // TODO investigate why this seems not to work in go (but in the redis cli) ๐Ÿคจ + // for _, collectionAddress := range slugAddresses { + // // channelPattern := internal.PubSubSeaWatcherListings + "/" + collectionAddress.Hex() + "/*" + // // channelPattern := internal.PubSubSeaWatcher + "/*/" + collectionAddress.Hex() + // // channelPattern := internal.PubSubSeaWatcher + "/" + collectionAddress.Hex() + "/*" + // channels = append(channels, channelPattern) + // } - channels = append(channels, channelPattern) - } + channels = append(channels, internal.PubSubSeaWatcher+"/*/*") + + gbl.Log.Infof("๐Ÿš‡ subscribing to redis channels %s", channels) err := gb.Rdb.Receive(context.Background(), gb.Rdb.B().Psubscribe().Pattern(channels...).Build(), func(msg rueidis.PubSubMessage) { - gbl.Log.Debugf("๐Ÿš‡ received msg on channel %s: %s", msg.Channel, msg.Message) + gbl.Log.Debugf("๐Ÿš‡ received msg on channel %s", msg.Channel) - var itemListedEvent seawaModels.ItemListed + var rawEvent map[string]interface{} // validate json if !json.Valid([]byte(msg.Message)) { @@ -72,25 +83,103 @@ func SubscribeToListingsViaRedis(gb *gloomberg.Gloomberg) { return } - // unmarshal - if err := json.Unmarshal([]byte(msg.Message), &itemListedEvent); err != nil { + // unmarshal json + if err := json.Unmarshal([]byte(msg.Message), &rawEvent); err != nil { gbl.Log.Errorf("โŒ error json.Unmarshal: %+v\n", err.Error()) return } - // nftID is a string in the format // - nftID := itemListedEvent.Payload.Item.NftID - // - // discard listings for ignored collections - if collection, ok := gb.CollectionDB.Collections[common.HexToAddress(nftID[1])]; ok && collection.IgnorePrinting { - gbl.Log.Debugf("๐Ÿ—‘๏ธ ignoring printing for collection %s", collection.Name) + // decode event to general event + var generalEvent models.GeneralEvent + + // decode event + rawDecoderConfig := models.GetEventDecoderConfig() + rawDecoderConfig.Result = &generalEvent + decoder, _ := mapstructure.NewDecoder(&rawDecoderConfig) + + err := decoder.Decode(rawEvent) + if err != nil { + log.Infof("โš“๏ธโŒ decoding incoming event failed: %+v | %+v", msg.Message, err) return } - // print - gb.In.ItemListed <- &itemListedEvent + // decoder config + decoderConfig := models.GetEventDecoderConfig() + + switch degendb.GetEventType(generalEvent.EventType) { + case degendb.Listing: + var itemListed models.ItemListed + + decoderConfig.Result = &itemListed + decoder, _ := mapstructure.NewDecoder(&decoderConfig) + + err := decoder.Decode(rawEvent) + if err != nil { + log.Infof("โš“๏ธโŒ decoding incoming %s event failed: %s", generalEvent, err) + + return + } + + // push to event hub + gb.In.ItemListed <- &itemListed + + case degendb.Bid: + var itemReceivedBid models.ItemReceivedBid + + decoderConfig.Result = &itemReceivedBid + decoder, _ := mapstructure.NewDecoder(&decoderConfig) + + err := decoder.Decode(rawEvent) + if err != nil { + log.Infof("โš“๏ธโŒ decoding incoming %s event failed: %s", generalEvent, err) + + return + } + + // push to event hub + gb.In.ItemReceivedBid <- &itemReceivedBid + + case degendb.CollectionOffer: + var collectionOffer models.CollectionOffer + + // decoderConfig := models.GetEventDecoderConfig() + decoderConfig.Result = &collectionOffer + decoder, _ := mapstructure.NewDecoder(&decoderConfig) + + err := decoder.Decode(rawEvent) + if err != nil { + log.Infof("โš“๏ธโŒ decoding incoming event failed: %+v %+v", collectionOffer, err) + + return + } + + // push to event hub + gb.In.CollectionOffer <- &collectionOffer + + case degendb.MetadataUpdated: + var itemMetadataUpdated models.ItemMetadataUpdated + + decoderConfig.Result = &itemMetadataUpdated + decoder, _ := mapstructure.NewDecoder(&decoderConfig) + + err := decoder.Decode(rawEvent) + if err != nil { + log.Infof("โš“๏ธโŒ decoding incoming event failed: %+v %+v", itemMetadataUpdated, err) + + return + } + + // push to event hub + gb.In.ItemMetadataUpdated <- &itemMetadataUpdated + + default: + gbl.Log.Warnf("โ—๏ธ unknown event type: %s", generalEvent.EventType) + gbl.Log.Warnf("โ—๏ธ %#v", generalEvent) + } + + logEvent(generalEvent) }) if err != nil { gbl.Log.Errorf("โŒ error subscribing to redis channels %s: %s", channels, err.Error()) @@ -115,3 +204,23 @@ func Publish(gb *gloomberg.Gloomberg, channel string, event any) { gbl.Log.Debug("published event to redis") } } + +func logEvent(generalEvent models.GeneralEvent) { + primaryStyle, _ := style.GenerateAddressStyles(generalEvent.ContractAddress()) + + fmtCurrencySymbol := primaryStyle.Bold(false).Render("ฮž") + fmtPrice := style.BoldAlmostWhite(fmt.Sprintf("%7.4f", generalEvent.BasePrice().Ether())) + fmtCurrencySymbol + + fmtItem := primaryStyle.Bold(true).Render(generalEvent.ItemName()) + + fmtFrom := style.FormatAddress(&generalEvent.Payload.Maker.Address) + + out := strings.Builder{} + out.WriteString(degendb.GetEventType(generalEvent.EventType).Icon()) + out.WriteString(" " + fmtPrice) + out.WriteString(" " + fmtItem) + out.WriteString(" " + style.DividerArrowLeft.String()) + out.WriteString(fmtFrom) + + gbl.Log.Info(out.String()) +} diff --git a/internal/seawa/models/common.go b/internal/seawa/models/common.go index 56a2f45..63d5821 100644 --- a/internal/seawa/models/common.go +++ b/internal/seawa/models/common.go @@ -82,6 +82,10 @@ type EventPayload struct { } func (ep EventPayload) GetPrice() *price.Price { + if ep.BasePrice == nil { + return price.NewPrice(big.NewInt(0)) + } + return price.NewPrice(ep.BasePrice) } diff --git a/internal/seawa/models/generic.go b/internal/seawa/models/generic.go index 7daf4b5..3781716 100644 --- a/internal/seawa/models/generic.go +++ b/internal/seawa/models/generic.go @@ -1,24 +1,64 @@ package models -import "time" +import ( + "math/big" + "time" -type ItemGeneric struct { + "github.com/benleb/gloomberg/internal/degendb" + "github.com/benleb/gloomberg/internal/nemo/price" + "github.com/ethereum/go-ethereum/common" +) + +type GeneralEvent struct { EventType string `json:"event_type" mapstructure:"event_type"` SentAt time.Time `json:"sent_at" mapstructure:"sent_at"` Payload ItemGenericPayload `json:"payload" mapstructure:"payload"` Other map[string]interface{} `mapstructure:",remain"` + + address common.Address `mapstructure:"-"` +} + +func (e *GeneralEvent) ItemName() string { + if degendb.GetEventType(e.EventType) == degendb.CollectionOffer { + return e.Payload.CollectionCriteria.Slug + } + + return e.Payload.Item.Metadata.Name +} + +func (e *GeneralEvent) ContractAddress() *common.Address { + if e.address != (common.Address{}) { + return &e.address + } + + if degendb.GetEventType(e.EventType) == degendb.CollectionOffer { + e.address = e.Payload.Address + } else { + e.address = e.Payload.Item.NftID.ContractAddress() + } + + return &e.address +} + +func (e *GeneralEvent) BasePrice() *price.Price { + basePrice := price.NewPrice(big.NewInt(0)) + if degendb.GetEventType(e.EventType) != degendb.MetadataUpdated { + basePrice = price.NewPrice(e.Payload.BasePrice) + } + + return basePrice } type ItemGenericPayload struct { - Item `json:"item" mapstructure:"item"` - CreatedDate time.Time `json:"created_date" mapstructure:"created_date"` + Item `json:"item" mapstructure:"item,omitempty"` + CreatedDate time.Time `json:"created_date" mapstructure:"created_date,omitempty"` CollectionCriteria `json:"collection_criteria" mapstructure:"collection_criteria,omitempty"` ContractCriteria `json:"asset_contract_criteria" mapstructure:"asset_contract_criteria,omitempty"` TraitCriteria `json:"trait_criteria" mapstructure:"trait_criteria,omitempty"` - EventPayload `json:"payload" mapstructure:",squash"` + EventPayload `json:"payload" mapstructure:",squash,omitempty"` IsPrivate bool `json:"is_private" mapstructure:"is_private"` ListingDate time.Time `json:"listing_date" mapstructure:"listing_date"` diff --git a/internal/seawa/models/models.go b/internal/seawa/models/models.go index 52c76d5..f2d9fa2 100644 --- a/internal/seawa/models/models.go +++ b/internal/seawa/models/models.go @@ -1,7 +1,11 @@ package models import ( + "time" + "github.com/benleb/gloomberg/internal/nemo/osmodels" + "github.com/benleb/gloomberg/internal/utils/hooks" + "github.com/mitchellh/mapstructure" ) type MgmtAction int64 @@ -35,3 +39,18 @@ type MgmtEvent struct { Events []osmodels.EventType `json:"events"` Slugs []string `json:"slugs"` } + +func GetEventDecoderConfig() mapstructure.DecoderConfig { + return mapstructure.DecoderConfig{ + DecodeHook: mapstructure.ComposeDecodeHookFunc( + hooks.StringToAddressHookFunc(), + hooks.StringToHashHookFunc(), + hooks.StringToBigIntHookFunc(), + StringToNftIDHookFunc(), + mapstructure.OrComposeDecodeHookFunc( + hooks.StringToUnixTimeHookFunc(), + mapstructure.StringToTimeHookFunc(time.RFC3339), + ), + ), + } +} diff --git a/internal/seawa/seawa.proto b/internal/seawa/seawa.proto deleted file mode 100644 index 9f8fa65..0000000 --- a/internal/seawa/seawa.proto +++ /dev/null @@ -1,162 +0,0 @@ -// dakma-dev | seawatcher.proto - -syntax = "proto3"; - -import "google/protobuf/timestamp.proto"; -import "google/protobuf/any.proto"; -import "google/protobuf/empty.proto"; - -import "internal/nemo/gloomberg/gbgrpc/gloomberg.proto"; - -option go_package = "github.com/benleb/gloomberg/internal/seawa"; - -package seawatcher; - -// Interface exported by the server. -service SeaWatcher { - rpc Subscribe(gbgrpc.SubscriptionRequest) returns (google.protobuf.Empty) {} - rpc GetItemListedEvents(SubscriptionRequest) returns (stream Event) {} -} - -enum EventType { - UNKNOWN = 0; - ITEM_LISTED = 1; - ITEM_RECEIVED_BID = 2; - METADATA_UPDATED = 3; - COLLECTION_OFFER = 4; -} - -message SubscriptionRequest { - repeated EventType eventTypes = 1; - repeated string collections = 2; -} - -message Event { - // The name of the feature. - string name = 1; - - // The event type. - EventType eventType = 2; - - // The event payload. - EventPayload payload = 3; -} - -message EventPayload { - // The kind of value. - oneof kind { - ItemListed item_listed = 1; - ItemReceivedBid item_received_bid = 2; - // MetadataUpdated metadata_updated = 3; - // CollectionOffer collection_offer = 4; - } -} - -message ItemReceivedBid { - message CollectionMuh { - string muh = 1; - } - - message ChainMoep { - string mia = 1; - } -} - -// Event emitted when an item is listed for sale. -// -// This event is emitted when an item is listed for sale on opensea. -message ItemListed { - message Collection { - string slug = 1; - } - - message Chain { - string name = 1; - } - - message Metadata { - string animation_url = 1; - string image_url = 2; - string metadata_url = 3; - string name = 4; - } - - message Item { - Chain chain = 1; - Metadata metadata = 2; - string nft_id = 3; - string permalink = 4; - } - - message Account { - string address = 1; - } - - message Payment_token { - string address = 1; - uint32 decimals = 2; - string eth_price = 3; - string name = 4; - string symbol = 5; - string usd_price = 6; - } - - message Consideration { - uint64 endAmount = 1; - uint32 identifierOrCriteria = 2; - uint32 itemType = 3; - string recipient = 4; - uint64 startAmount = 5; - string token = 6; - } - - message Offer { - uint32 endAmount = 1; - uint64 identifierOrCriteria = 2; - uint32 itemType = 3; - uint32 startAmount = 4; - string token = 5; - } - - message Parameters { - string conduitKey = 1; - repeated Consideration consideration = 2; - uint32 counter = 3; - uint32 endTime = 4; - repeated Offer offer = 5; - string offerer = 6; - uint32 orderType = 7; - uint64 salt = 8; - uint32 startTime = 9; - uint32 totalOriginalConsiderationItems = 10; - string zone = 11; - string zoneHash = 12; - } - - message Protocol_data { - Parameters parameters = 1; - string signature = 2; - bool use_lazy_mint_adapter_for_shared_storefront = 3; - } - - message ItemListedPayload { - string base_price = 1; - Collection collection = 2; - google.protobuf.Timestamp event_timestamp = 3; - google.protobuf.Timestamp expiration_date = 4; - bool is_private = 5; - Item item = 6; - google.protobuf.Timestamp listing_date = 7; - google.protobuf.Any listing_type = 8; - Account maker = 9; - string order_hash = 10; - Payment_token payment_token = 11; - Protocol_data protocol_data = 12; - uint32 quantity = 13; - Account taker = 14; - } - - EventType event_type = 1; - ItemListedPayload payload = 2; - google.protobuf.Timestamp sent_at = 3; -} diff --git a/internal/seawa/seawatcher.go b/internal/seawa/seawatcher.go index 4768660..6b68bf0 100644 --- a/internal/seawa/seawatcher.go +++ b/internal/seawa/seawatcher.go @@ -12,15 +12,18 @@ import ( "time" "github.com/benleb/gloomberg/internal" + "github.com/benleb/gloomberg/internal/degendb" "github.com/benleb/gloomberg/internal/gbl" "github.com/benleb/gloomberg/internal/nemo/gloomberg" - gbgrpc "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc/gen" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/remote" "github.com/benleb/gloomberg/internal/nemo/osmodels" + "github.com/benleb/gloomberg/internal/nemo/price" + "github.com/benleb/gloomberg/internal/pusu" "github.com/benleb/gloomberg/internal/seawa/models" "github.com/benleb/gloomberg/internal/style" "github.com/benleb/gloomberg/internal/utils/hooks" "github.com/charmbracelet/log" + mapset "github.com/deckarep/golang-set/v2" + "github.com/ethereum/go-ethereum/common" "github.com/mitchellh/mapstructure" "github.com/nshafer/phx" "github.com/prometheus/client_golang/prometheus" @@ -28,8 +31,6 @@ import ( "github.com/redis/rueidis" "github.com/spf13/viper" "go.uber.org/zap" - "google.golang.org/grpc" - emptypb "google.golang.org/protobuf/types/known/emptypb" ) type SeaWatcher struct { @@ -45,25 +46,20 @@ type SeaWatcher struct { channels map[string]*phx.Channel // subscribed slugs/events - // subscriptions map[osmodels.EventType]map[string]func() - // subscriptions map[string]map[osmodels.EventType]func() - subscriptions map[string]map[gbgrpc.EventType]func() + subscriptions map[string]map[degendb.EventType]func() runLocal bool runPubsubServer bool - runGRPCServer bool // redis client rdb rueidis.Client gb *gloomberg.Gloomberg - mu *sync.Mutex + mu *sync.RWMutex } -// availableEventTypes = []osmodels.EventType{osmodels.ItemListed, osmodels.ItemMetadataUpdated, osmodels.ItemReceivedBid, osmodels.CollectionOffer} // , osmodels.ItemMetadataUpdated} // ItemMetadataUpdated, ItemCancelled. -var availableEventTypes = []gbgrpc.EventType{gbgrpc.EventType_ITEM_LISTED} //nolint:nosnakecase -// , gbgrpc.EventType_METADATA_UPDATED, gbgrpc.EventType_ITEM_RECEIVED_BID, gbgrpc.EventType_COLLECTION_OFFER} // ItemMetadataUpdated} // ItemMetadataUpdated, ItemCancelled +var availableEventTypes = mapset.NewSet[degendb.EventType](degendb.Listing, degendb.CollectionOffer, degendb.Bid) var eventsReceivedCounter = promauto.NewCounter(prometheus.CounterOpts{ Name: "gloomberg_seawatcher_events_received_count_total", @@ -72,7 +68,7 @@ var eventsReceivedCounter = promauto.NewCounter(prometheus.CounterOpts{ // func NewSeaWatcher(apiToken string, rdb rueidis.Client) *SeaWatcher {. func NewSeaWatcher(apiToken string, gb *gloomberg.Gloomberg) *SeaWatcher { - // we might not connect to the stream api locally if we use grpc or other ways to get the events + // we might not connect to the stream api locally if we use other ways to get the events runLocalAPIClient := viper.GetBool("seawatcher.local") if runLocalAPIClient && apiToken == "" { @@ -92,19 +88,17 @@ func NewSeaWatcher(apiToken string, gb *gloomberg.Gloomberg) *SeaWatcher { sw := &SeaWatcher{ receivedEvents: make(chan map[string]interface{}, 1024), - // subscriptions: make(map[string]map[osmodels.EventType]func()), - subscriptions: make(map[string]map[gbgrpc.EventType]func()), + subscriptions: make(map[string]map[degendb.EventType]func()), channels: make(map[string]*phx.Channel), runLocal: runLocalAPIClient, runPubsubServer: viper.GetBool("seawatcher.pubsub"), - runGRPCServer: viper.IsSet("seawatcher.grpc.listen"), gb: gb, rdb: gb.Rdb, - mu: &sync.Mutex{}, + mu: &sync.RWMutex{}, } // create phoenix socket @@ -154,39 +148,10 @@ func NewSeaWatcher(apiToken string, gb *gloomberg.Gloomberg) *SeaWatcher { } // start worker for managing subscriptions - if viper.GetBool("pubsub.client.enabled") || viper.GetBool("seawatcher.pubsub") || viper.GetBool("grpc.client.enabled") { + if viper.GetBool("pubsub.client.enabled") || viper.GetBool("seawatcher.pubsub") { go sw.WorkerMgmtChannel() } - // if viper.GetBool("seawatcher.grpc.server.enabled") { - // sw.Prf("starting grpc server...") - - // listenHost := viper.GetString("seawatcher.grpc.listen") - // port := viper.GetUint16("seawatcher.grpc.port") - // serverAddress := fmt.Sprintf("%s:%d", listenHost, port) - - // // configure grpc server - // go func() { - // grpcListener, err := net.Listen("tcp", serverAddress) - // if err != nil { - // log.Errorf("failed to listen: %v", err) - // } - - // var opts []grpc.ServerOption - // if creds, err := gloomberg.GetTLSCredentialsWithoutClientAuth(); err == nil { - // opts = []grpc.ServerOption{grpc.Creds(creds)} - // } - - // // start grpc server - // grpcServer := grpc.NewServer(opts...) - // RegisterSeaWatcherServer(grpcServer, sw) - - // go log.Error(grpcServer.Serve(grpcListener)) - // }() - - // sw.Prf("grpc server started on %+v", style.BoldAlmostWhite(serverAddress)) - // } - return sw } @@ -205,8 +170,18 @@ func (sw *SeaWatcher) EventChannel() chan map[string]interface{} { } // func (sw *SeaWatcher) ActiveSubscriptions() map[string]map[osmodels.EventType]func() {. -func (sw *SeaWatcher) ActiveSubscriptions() map[string]map[gbgrpc.EventType]func() { - return sw.subscriptions +func (sw *SeaWatcher) ActiveSubscriptions() map[string]map[degendb.EventType]func() { + totalSubscriptions := 0 + + sw.mu.Lock() + slugSubscriptions := sw.subscriptions + sw.mu.Unlock() + + for _, eventSubscriptions := range slugSubscriptions { + totalSubscriptions += len(eventSubscriptions) + } + + return slugSubscriptions } // eventHandler handles incoming stream api events and forwards them as map. @@ -228,32 +203,34 @@ func (sw *SeaWatcher) eventHandler(response any) { } // decode event - var metadata *mapstructure.Metadata + var generalEvent models.GeneralEvent // decoder config - decoderConfig := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.ComposeDecodeHookFunc( - hooks.StringToAddressHookFunc(), - hooks.StringToHashHookFunc(), - hooks.StringToBigIntHookFunc(), - models.StringToNftIDHookFunc(), - mapstructure.OrComposeDecodeHookFunc( - hooks.StringToUnixTimeHookFunc(), - mapstructure.StringToTimeHookFunc(time.RFC3339), - ), - ), - Metadata: metadata, + decoderConfig := models.GetEventDecoderConfig() + decoderConfig.Result = &generalEvent + decoder, _ := mapstructure.NewDecoder(&decoderConfig) + + err := decoder.Decode(rawEvent) + if err != nil { + log.Info("โš“๏ธโŒ decoding incoming %s event failed: %s", style.Bold(itemEventType), err) + + return } - // sw.Prf("โš“๏ธ received %s event: %+v", itemEventType, rawEvent) + contractAddress := generalEvent.ContractAddress() + collectionStyle, _ := style.GenerateAddressStyles(contractAddress) + collectionStyle = collectionStyle.Bold(true) + fmtItemName := collectionStyle.Render(generalEvent.ItemName()) - switch osmodels.EventType(itemEventType) { + // sw.Prf("โš“๏ธ mapstructure generalEvent %s event: %+v", generalEvent.EventType, collectionStyle.Render(contractAddress.Hex())) + + switch osmodels.EventType(generalEvent.EventType) { // item listed case osmodels.ItemListed: - var event *models.ItemListed + var itemListed *models.ItemListed - decoderConfig.Result = &event - decoder, _ := mapstructure.NewDecoder(decoderConfig) + decoderConfig.Result = &itemListed + decoder, _ := mapstructure.NewDecoder(&decoderConfig) err := decoder.Decode(rawEvent) if err != nil { @@ -263,17 +240,13 @@ func (sw *SeaWatcher) eventHandler(response any) { } // push to eventHub for further processing - sw.gb.In.ItemListed <- event - - // sw.Prf("๐Ÿ“ข %s #%s listed for %sฮž", style.BoldAlmostWhite(event.Payload.Item.Name), style.BoldAlmostWhite(event.Payload.Item.NftID.TokenID().String()), event.Payload.EventPayload.GetPrice()) - - // sw.gb.GloomHub.Publish(channel.ItemListed, event) + sw.gb.In.ItemListed <- itemListed case osmodels.ItemReceivedBid: - var event *models.ItemReceivedBid + var itemReceivedBid *models.ItemReceivedBid - decoderConfig.Result = &event - decoder, _ := mapstructure.NewDecoder(decoderConfig) + decoderConfig.Result = &itemReceivedBid + decoder, _ := mapstructure.NewDecoder(&decoderConfig) err := decoder.Decode(rawEvent) if err != nil { @@ -283,15 +256,13 @@ func (sw *SeaWatcher) eventHandler(response any) { } // push to eventHub for further processing - sw.gb.In.ItemReceivedBid <- event - - // sw.Prf("๐Ÿ’ฆ %s #%s received bid: %sฮž", style.BoldAlmostWhite(event.Payload.Item.Metadata.Name), style.BoldAlmostWhite(event.Payload.Item.NftID.TokenID().String()), style.BoldAlmostWhite(fmt.Sprint(event.Payload.EventPayload.GetPrice()))) + sw.gb.In.ItemReceivedBid <- itemReceivedBid case osmodels.CollectionOffer: - var event *models.CollectionOffer + var collectionOffer *models.CollectionOffer - decoderConfig.Result = &event - decoder, _ := mapstructure.NewDecoder(decoderConfig) + decoderConfig.Result = &collectionOffer + decoder, _ := mapstructure.NewDecoder(&decoderConfig) err := decoder.Decode(rawEvent) if err != nil { @@ -301,34 +272,15 @@ func (sw *SeaWatcher) eventHandler(response any) { } // push to eventHub for further processing - sw.gb.In.CollectionOffer <- event - - // name := event.Payload.CollectionCriteria.Slug - // if collection := sw.gb.CollectionDB.GetCollectionForSlug(event.Payload.CollectionCriteria.Slug); collection != nil { - // name = collection.Name - // } + sw.gb.In.CollectionOffer <- collectionOffer - // // parse tokenPrice - // var tokenPrice *price.Price - // if event.Payload.BasePrice != nil { - // tokenPrice = price.NewPrice(event.Payload.BasePrice) - // } else { - // tokenPrice = price.NewPrice(big.NewInt(0)) - - // gbl.Log.Warnf("๐Ÿคทโ€โ™€๏ธ error parsing tokenPrice: %+v", event.Payload.BasePrice) - // } - - // sw.Prf("๐Ÿฆ• %s collection offer: %sฮž", style.BoldAlmostWhite(name), style.BoldAlmostWhite(fmt.Sprintf("%5.3f", tokenPrice.Ether()))) + // pretty.Println(collectionOffer) case osmodels.ItemMetadataUpdated: - var event *models.ItemMetadataUpdated + var itemMetadataUpdated *models.ItemMetadataUpdated - decoderConfig.Result = &event - decoder, _ := mapstructure.NewDecoder(decoderConfig) - - // gbl.Log.Info("\n\n") - // gbl.Log.Infof(fmt.Sprintf("raw: %# v", pretty.Formatter(rawEvent))) - // gbl.Log.Info("") + decoderConfig.Result = &itemMetadataUpdated + decoder, _ := mapstructure.NewDecoder(&decoderConfig) err := decoder.Decode(rawEvent) if err != nil { @@ -337,12 +289,17 @@ func (sw *SeaWatcher) eventHandler(response any) { return } - // gbl.Log.Infof(fmt.Sprintf("event: %# v", pretty.Formatter(event))) - // gbl.Log.Info("\n\n") - // push to eventHub for further processing - sw.gb.In.ItemMetadataUpdated <- event + sw.gb.In.ItemMetadataUpdated <- itemMetadataUpdated } + + if viper.GetBool("pubsub.server.enabled") { + publishChannel := internal.PubSubSeaWatcher + "/" + generalEvent.EventType + "/" + contractAddress.Hex() + pusu.Publish(sw.gb, publishChannel, rawEvent) + } + + // ๐Ÿ’„ styled log + go logEvent(sw, degendb.GetEventType(generalEvent.EventType), contractAddress, generalEvent.BasePrice(), fmtItemName, &generalEvent.Payload.Maker.Address) } func (sw *SeaWatcher) DecodeItemReceivedBidEvent(itemEvent map[string]interface{}) (osmodels.ItemReceivedBidEvent, error) { @@ -389,86 +346,85 @@ func (sw *SeaWatcher) DecodeCollectionOfferEvent(itemEvent map[string]interface{ return collectionOfferEvent, err } -func (sw *SeaWatcher) SubscribeForSlug(slug string, eventTypes []gbgrpc.EventType) uint64 { - return sw.SubscribeForSlugs([]string{slug}, eventTypes) -} - -func (sw *SeaWatcher) SubscribeForSlugs(slugs []string, eventTypes []gbgrpc.EventType) uint64 { - if viper.GetBool("grpc.client.enabled") { - // connect - grpcClient := remote.NewClient() - - if grpcClient == nil { - log.Errorf("fail to connect to gRPC to subscribe for %+v", style.BoldAlmostWhite(strings.Join(slugs, ", "))) - - return 0 - } - - sw.Prf("subscribing to %s...", strings.Join(slugs, ", ")) +func (sw *SeaWatcher) SubscribeForSlug(slug string, eventTypes []degendb.EventType) uint64 { + eventTypesSet := mapset.NewSet[degendb.EventType]() + for _, eventType := range eventTypes { + eventTypesSet.Add(eventType) + } - subsriptionRequest := &gbgrpc.SubscriptionRequest{EventTypes: []gbgrpc.EventType{gbgrpc.EventType_ITEM_LISTED}, Collections: slugs} //nolint:nosnakecase - _, err := grpcClient.Subscribe(context.Background(), subsriptionRequest, grpc.WaitForReady(true)) - if err != nil { - log.Errorf("getting stream failed: %v, retrying", err) - } + return sw.SubscribeForSlugs([]string{slug}, eventTypesSet) +} - // sw.Prf("subscribing to %s...", strings.Join(slugs, ", ")) +func (sw *SeaWatcher) SubscribeForSlugs(slugs []string, eventTypes mapset.Set[degendb.EventType]) uint64 { + if !viper.GetBool("pubsub.server.enabled") && !viper.GetBool("seawatcher.pubsub") && !viper.GetBool("seawatcher.local") { + gbl.Log.Infof("โš“๏ธ subscribeing to: %+v", style.BoldAlmostWhite(strings.Join(slugs, ", "))) + sw.gb.PublishCollectionsSlugs(slugs) return uint64(len(slugs)) - } else if !viper.GetBool("seawatcher.pubsub") && !viper.GetBool("seawatcher.local") { - gbl.Log.Warn("โš“๏ธ subscribe discarded - no local OpenSea clients") - gbl.Log.Warn("โš“๏ธ TODO implement subscribe via grpc (and maybe pubsub)") - - return 0 } newEventSubscriptions := uint64(0) for _, slug := range slugs { - if sw.IsSubscribed(slug) { + if sw.IsSubscribedToAllEvents(slug) { log.Debugf("โš“๏ธ โ˜•๏ธ already subscribed to OpenSea events for %s", slug) return 0 } - sw.Prf("subscribing to %s...", slug) + slugSubscriptions := make(map[degendb.EventType]func()) + slugEventSubscriptions := mapset.NewSet[string]() - sw.mu.Lock() + for _, eventType := range eventTypes.ToSlice() { + if slugSubscriptions[eventType] != nil { + log.Debugf("โš“๏ธ โ˜•๏ธ already subscribed to %s events for %s", eventType, slug) + + continue + } - sw.subscriptions[slug] = make(map[gbgrpc.EventType]func()) + slugSubscriptions[eventType] = sw.on(eventType, slug, sw.eventHandler) - for _, eventType := range eventTypes { - gloomberg.PrModf("seawa", "subscribing to %s events for %s", eventType, slug) - sw.subscriptions[slug][eventType] = sw.on(eventType, slug, sw.eventHandler) + newEventSubscriptions++ + slugEventSubscriptions.Add(eventType.String()) time.Sleep(time.Millisecond * 37) } + sw.mu.Lock() + sw.subscriptions[slug] = slugSubscriptions sw.mu.Unlock() - newEventSubscriptions++ + fmtSlug := style.BoldAlmostWhite(slug) + fmtDivider := style.GrayStyle.Render("|") + // no collection db -> no addresses -> no colors ๐Ÿ˜ข fix me! if collection := sw.gb.CollectionDB.GetCollectionForSlug(slug); collection != nil { log.Debugf("โฎ๏ธ resetting stats for %s", slug) collection.ResetStats() + + // use collection colors + fmtSlug = collection.Render(slug) + fmtDivider = collection.Style().Copy().Faint(true).Render("|") } + sw.Prf("%s: %s", fmtSlug, strings.Join(slugEventSubscriptions.ToSlice(), fmtDivider)) + time.Sleep(time.Millisecond * 137) } return newEventSubscriptions } -func (sw *SeaWatcher) UnubscribeForSlug(slug string, _ []gbgrpc.EventType) uint64 { +func (sw *SeaWatcher) UnubscribeForSlug(slug string, _ mapset.Set[degendb.EventType]) uint64 { return sw.UnubscribeForSlugs([]string{slug}, nil) } -func (sw *SeaWatcher) UnubscribeForSlugs(slugs []string, _ []gbgrpc.EventType) uint64 { +func (sw *SeaWatcher) UnubscribeForSlugs(slugs []string, _ mapset.Set[degendb.EventType]) uint64 { numUnsubscribed := uint64(0) for _, slug := range slugs { - if sw.IsSubscribed(slug) { + if sw.IsSubscribedToAllEvents(slug) { log.Debugf("โš“๏ธ โ˜•๏ธ not subscribed to events for %s", slug) return 0 @@ -488,8 +444,6 @@ func (sw *SeaWatcher) UnubscribeForSlugs(slugs []string, _ []gbgrpc.EventType) u sw.mu.Lock() sw.subscriptions[slug] = nil sw.mu.Unlock() - - // return true } numUnsubscribed++ @@ -500,16 +454,29 @@ func (sw *SeaWatcher) UnubscribeForSlugs(slugs []string, _ []gbgrpc.EventType) u return numUnsubscribed } -func (sw *SeaWatcher) IsSubscribed(slug string) bool { +func (sw *SeaWatcher) IsSubscribedToAllEvents(slug string) bool { sw.mu.Lock() - alreadySubscribed, ok := sw.subscriptions[slug] + slugSubscriptions, ok := sw.subscriptions[slug] sw.mu.Unlock() - if ok && alreadySubscribed != nil { - return true + if !ok || len(slugSubscriptions) < availableEventTypes.Cardinality() { + return false + } + + for _, eventType := range availableEventTypes.ToSlice() { + cancelSubscriptionFunc, ok := slugSubscriptions[eventType] + + if !ok { + log.Errorf("โš“๏ธโŒ error while checking existing eventtype subscriptions for %s / %+v", slug, eventType) + return false + } + + if cancelSubscriptionFunc == nil { + return false + } } - return false + return true } func (sw *SeaWatcher) createChannel(topic string) *phx.Channel { @@ -544,24 +511,24 @@ func (sw *SeaWatcher) getChannel(topic string) *phx.Channel { return channel } -func (sw *SeaWatcher) on(eventType gbgrpc.EventType, collectionSlug string, eventHandler func(response any)) func() { +func (sw *SeaWatcher) on(eventType degendb.EventType, collectionSlug string, eventHandler func(response any)) func() { topic := fmt.Sprintf("collection:%s", collectionSlug) - evType := strings.ToLower(eventType.String()) + openseaEvent := strings.ToLower(eventType.OpenseaEventName()) log.Debugf("Fetching channel %s", topic) channel := sw.getChannel(topic) - log.Debugf("subscribing to %s events on %s", evType, topic) - channel.On(evType, eventHandler) + log.Debugf("subscribing to %s events on %s", openseaEvent, topic) + channel.On(openseaEvent, eventHandler) channel.OnClose(func(payload any) { sw.Prf("โš ๏ธ Channel %s closed: %s", topic, payload) }) - log.Debugf("โš subscribed to %s for %s", evType, collectionSlug) + log.Debugf("โš subscribed to %s for %s", openseaEvent, collectionSlug) return func() { - sw.Prf("Unsubscribing from %s events on %s", evType, topic) + sw.Prf("Unsubscribing from %s events on %s", openseaEvent, topic) leave, err := channel.Leave() if err != nil { @@ -570,7 +537,7 @@ func (sw *SeaWatcher) on(eventType gbgrpc.EventType, collectionSlug string, even leave.Receive("ok", func(_ any) { delete(sw.channels, collectionSlug) - sw.Prf("Successfully left channel %s listening for %s", topic, evType) + sw.Prf("Successfully left channel %s listening for %s", topic, openseaEvent) }) } } @@ -589,10 +556,10 @@ func (sw *SeaWatcher) WorkerMgmtChannel() { // SubscribeToPubsubMgmt starts the seawatcher by subscribing to the mgmt channel and listening for new slugs to subscribe to. func (sw *SeaWatcher) SubscribeToPubsubMgmt() { - sw.Prf("subscribing to mgmt channel %s", style.AlmostWhiteStyle.Render(internal.PubSubSeaWatcherMgmt)) + sw.Prf("๐Ÿ‘” subscribing to mgmt channel %s", style.AlmostWhiteStyle.Render(internal.PubSubSeaWatcherMgmt)) err := sw.rdb.Receive(context.Background(), sw.rdb.B().Subscribe().Channel(internal.PubSubSeaWatcherMgmt).Build(), func(msg rueidis.PubSubMessage) { - log.Infof("โš“๏ธ received msg on channel %s: %s", msg.Channel, msg.Message) + log.Infof("๐Ÿ‘” received msg on channel %s: %s", msg.Channel, msg.Message) var mgmtEvent *models.MgmtEvent @@ -610,7 +577,7 @@ func (sw *SeaWatcher) SubscribeToPubsubMgmt() { return } - sw.Prf("๐Ÿ“ฃ subscribed to %s", style.AlmostWhiteStyle.Render(internal.PubSubSeaWatcherMgmt)) + sw.Prf("๐Ÿ‘” subscribed to %s", style.AlmostWhiteStyle.Render(internal.PubSubSeaWatcherMgmt)) } func (sw *SeaWatcher) handleMgmtEvent(mgmtEvent *models.MgmtEvent) { @@ -639,7 +606,7 @@ func (sw *SeaWatcher) handleMgmtEvent(mgmtEvent *models.MgmtEvent) { return } - var action func(slug []string, eventTypes []gbgrpc.EventType) uint64 + var action func(slug []string, eventTypes mapset.Set[degendb.EventType]) uint64 switch mgmtEvent.Action { case models.Subscribe: @@ -651,13 +618,13 @@ func (sw *SeaWatcher) handleMgmtEvent(mgmtEvent *models.MgmtEvent) { newEventSubscriptions := action(mgmtEvent.Slugs, availableEventTypes) sw.Prf( - "successfully subscribed to %s new collections/slugs | total subscribed collections: %s", + "๐Ÿ‘” successfully subscribed to %s new collections/slugs | total subscribed collections: %s", style.AlmostWhiteStyle.Render(fmt.Sprint(newEventSubscriptions)), style.AlmostWhiteStyle.Render(fmt.Sprint(len(sw.ActiveSubscriptions()))), ) default: - sw.Prf("โš“๏ธ ๐Ÿ‘€ received unknown mgmt event: %s", mgmtEvent.Action.String()) + sw.Prf("๐Ÿ‘” ๐Ÿ‘€ received unknown mgmt event: %s", mgmtEvent.Action.String()) return } @@ -680,10 +647,24 @@ func (sw *SeaWatcher) PublishSendSlugs() { if sw.rdb.Do(context.Background(), sw.rdb.B().Publish().Channel(internal.PubSubSeaWatcherMgmt).Message(string(jsonMgmtEvent)).Build()).Error() != nil { log.Errorf("โš“๏ธโŒ error publishing %s to redis: %s", sendSlugsEvent.Action.String(), err.Error()) } else { - sw.Prf("๐Ÿ“ฃ published %s event to %s", style.AlmostWhiteStyle.Render(sendSlugsEvent.Action.String()), style.AlmostWhiteStyle.Render(internal.PubSubSeaWatcherMgmt)) + sw.Prf("๐Ÿ‘” published %s event to %s", style.AlmostWhiteStyle.Render(sendSlugsEvent.Action.String()), style.AlmostWhiteStyle.Render(internal.PubSubSeaWatcherMgmt)) } } -func (sw *SeaWatcher) Subscribe(context.Context, *gbgrpc.SubscriptionRequest) (*emptypb.Empty, error) { - return &emptypb.Empty{}, nil +func logEvent(sw *SeaWatcher, eventType *degendb.GBEventType, address *common.Address, price *price.Price, fmtItem string, from *common.Address) { + primaryStyle, _ := style.GenerateAddressStyles(address) + + fmtCurrencySymbol := primaryStyle.Bold(false).Render("ฮž") + fmtPrice := style.BoldAlmostWhite(fmt.Sprintf("%7.4f", price.Ether())) + fmtCurrencySymbol + + fmtFrom := style.FormatAddress(from) + + out := strings.Builder{} + out.WriteString(eventType.Icon()) + out.WriteString(" " + fmtPrice) + out.WriteString(" " + fmtItem) + out.WriteString(" " + style.DividerArrowLeft.String()) + out.WriteString(fmtFrom) + + sw.Pr(out.String()) } diff --git a/internal/style/style.go b/internal/style/style.go index 59d043a..776ed2e 100644 --- a/internal/style/style.go +++ b/internal/style/style.go @@ -290,6 +290,23 @@ func ShortenedTokenIDStyled(tokenID *big.Int, primaryStyle lipgloss.Style, secon return prefix + id } +func FormatAddress(address *common.Address) string { + style := lipgloss.NewStyle().Foreground(GenerateColorWithSeed(address.Big().Int64())) + + return ShortenAddressStyled(address, style) +} + +// GenerateColors generates two colors based on contract address of the collection. +func GenerateAddressColors(address *common.Address) (lipgloss.Color, lipgloss.Color) { + return GenerateColorWithSeed(address.Hash().Big().Int64()), GenerateColorWithSeed(address.Big().Int64() ^ 2) +} + +func GenerateAddressStyles(address *common.Address) (lipgloss.Style, lipgloss.Style) { + primaryColor, secondaryColor := GenerateAddressColors(address) + + return lipgloss.NewStyle().Foreground(primaryColor), lipgloss.NewStyle().Foreground(secondaryColor) +} + // ShortenAddressStyled returns a shortened address styled with colors. func ShortenAddressStyled(address *common.Address, style lipgloss.Style) string { // gray out zero address diff --git a/internal/trapri/collection_offer.go b/internal/trapri/collection_offer.go index 270791c..2d5a33b 100644 --- a/internal/trapri/collection_offer.go +++ b/internal/trapri/collection_offer.go @@ -2,6 +2,8 @@ package trapri import ( "math/big" + "sync" + "time" "github.com/benleb/gloomberg/internal/degendb" "github.com/benleb/gloomberg/internal/gbl" @@ -11,10 +13,14 @@ import ( "github.com/benleb/gloomberg/internal/nemo/token" "github.com/benleb/gloomberg/internal/nemo/totra" "github.com/benleb/gloomberg/internal/seawa/models" - "github.com/charmbracelet/log" "github.com/ethereum/go-ethereum/common" ) +var ( + collectionOffers = map[common.Address]*models.CollectionOffer{} + collectionOffersMutex = &sync.Mutex{} +) + func HandleCollectionOffer(gb *gloomberg.Gloomberg, event *models.CollectionOffer) { contractAddress := common.HexToAddress(event.Payload.ContractCriteria.Address.Hex()) @@ -24,46 +30,81 @@ func HandleCollectionOffer(gb *gloomberg.Gloomberg, event *models.CollectionOffe // parse tokenPrice var tokenPrice *price.Price if event.Payload.BasePrice != nil { - tokenPrice = price.NewPrice(event.Payload.BasePrice) + basePrice := event.Payload.BasePrice + if event.Payload.Quantity > 1 { + basePrice = new(big.Int).Div(event.Payload.BasePrice, big.NewInt(int64(event.Payload.Quantity))) + } + + tokenPrice = price.NewPrice(basePrice) } else { tokenPrice = price.NewPrice(big.NewInt(0)) - gbl.Log.Warnf("๐Ÿคทโ€โ™€๏ธ error parsing tokenPrice: %+v", event.Payload.BasePrice) + gbl.Log.Warnf("๐Ÿคทโ€โ™€๏ธ error parsing tokenPrice: %+v", event.Payload) } - collectionTokens := gb.OwnWallets.GetCollectionTokens(contractAddress) - - // create a ttxCollectionOffer for each token of the collection...^^ - for _, collectionToken := range collectionTokens { - // - // create a TokenTransaction - ttxCollectionOffer := &totra.TokenTransaction{ - Tx: nil, - TxReceipt: nil, - From: sellerAddress, - AmountPaid: tokenPrice.Wei(), - TotalTokens: int64(event.Payload.Quantity), - Marketplace: &marketplace.OpenSea, - Action: degendb.CollectionOffer, - ReceivedAt: event.Payload.EventTimestamp, - DoNotPrint: false, - Transfers: []*totra.TokenTransfer{ - { - From: marketplace.OpenSea.ContractAddress(), - To: sellerAddress, - AmountTokens: big.NewInt(int64(event.Payload.Quantity)), - Token: &token.Token{ - Address: contractAddress, - ID: big.NewInt(collectionToken.ID.Int64()), - Name: event.Payload.CollectionCriteria.Slug, - }, - }, - }, + // if it should be a new top bid, we highlight it when printing + // highlight := false + + // check if we already have a top bid for this token and if not, add it + collectionOffersMutex.Lock() + currentTopOffer := collectionOffers[contractAddress] + collectionOffersMutex.Unlock() + + switch { + // no or expired top offer - setting new top offer + case currentTopOffer == nil: + gbl.Log.Debugf("๐Ÿญ no current top offer, new top offer: %+v", event.Payload.GetPrice().Wei()) + + case currentTopOffer.Payload.ExpirationDate.Before(time.Now()): + gbl.Log.Debugf("๐Ÿญ top offer expired, new top offer: %+v", event.Payload.GetPrice().Wei()) + + // new offer is higher than current top offer + case currentTopOffer != nil: + // we add a small amount (still researching how much :D) of ether/wei to the current top offer before comparing + // to prevent printing a lot of backrunned (=doubled) offers all the time + // amountToAdd := big.NewInt(13370000000000001) // โ‰ˆ0.01337....ฮž + // amountToAdd := big.NewInt(10370000000000001) // โ‰ˆ0.01037....ฮž + amountToAdd := big.NewInt(7370000000000001) // โ‰ˆ0.00737....ฮž + + currentTopOfferWithBuffer := big.NewInt(0).Add(currentTopOffer.Payload.GetPrice().Wei(), amountToAdd) + + if event.Payload.GetPrice().Wei().Cmp(currentTopOfferWithBuffer) < 0 { + gbl.Log.Debugf("๐Ÿญ current top offer (+buffer) higher than incoming bid: %+v > %+v", currentTopOfferWithBuffer, event.Payload.GetPrice().Wei()) + + return } + } - // format and print - gb.In.TokenTransactions <- ttxCollectionOffer + // set the new top offer + collectionOffersMutex.Lock() + collectionOffers[contractAddress] = event + collectionOffersMutex.Unlock() - log.Printf(" ๐Ÿฆ„ collection offer: %s | %s | %s", contractAddress.String(), sellerAddress.String(), tokenPrice.String()) + // create a TokenTransaction + ttxCollectionOffer := &totra.TokenTransaction{ + Tx: nil, + TxReceipt: nil, + From: sellerAddress, + AmountPaid: tokenPrice.Wei(), + TotalTokens: int64(event.Payload.Quantity), + Marketplace: &marketplace.OpenSea, + Action: degendb.CollectionOffer, + ReceivedAt: event.Payload.EventTimestamp, + DoNotPrint: false, + Transfers: []*totra.TokenTransfer{ + { + From: marketplace.OpenSea.ContractAddress(), + To: sellerAddress, + AmountTokens: big.NewInt(int64(event.Payload.Quantity)), + Token: &token.Token{ + Address: contractAddress, + ID: big.NewInt(0), + Name: event.Payload.CollectionCriteria.Slug, + }, + }, + }, } + + // format and print + gb.In.TokenTransactions <- ttxCollectionOffer } diff --git a/internal/trapri/eventHandler.go b/internal/trapri/eventHandler.go index 664fc43..fcea06a 100644 --- a/internal/trapri/eventHandler.go +++ b/internal/trapri/eventHandler.go @@ -1,24 +1,18 @@ package trapri import ( - "fmt" "strings" - "github.com/benleb/gloomberg/internal" - "github.com/benleb/gloomberg/internal/degendb" "github.com/benleb/gloomberg/internal/gbl" "github.com/benleb/gloomberg/internal/nemo/gloomberg" - "github.com/benleb/gloomberg/internal/nemo/price" - "github.com/benleb/gloomberg/internal/pusu" "github.com/benleb/gloomberg/internal/style" - "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/log" "github.com/ethereum/go-ethereum/common" "github.com/kr/pretty" "github.com/spf13/viper" ) -// SeaWatcherEventsHandler handles all incoming & decoded events from OpenSea and dispatches them to the appropriate handler. +// SeaWatcherEventsHandler handles events coming from the SeaWatcher and processes func SeaWatcherEventsHandler(gb *gloomberg.Gloomberg) { chanItemListed := gb.SubscribeItemListed() chanItemReceivedBid := gb.SubscribeItemReceivedBid() @@ -34,43 +28,25 @@ func SeaWatcherEventsHandler(gb *gloomberg.Gloomberg) { for { select { case event := <-chanItemListed: - gbl.Log.Debugf(" ๐Ÿ“ข item listed: %+v", pretty.Sprintf("%#v", event)) + gbl.Log.Debugf(" ๐Ÿ“ข item listed: %+v", event) - // gloomberg.Prf("โš“๏ธ handling %s event...", event.EventType) go HandleItemListed(gb, event) - if viper.GetBool("pubsub.server.enabled") || viper.GetBool("grpc.server.enabled") { - // publish via pubsub - publishChannel := internal.PubSubSeaWatcherListings + "/" + event.Payload.Item.NftID.ContractAddress().Hex() + "/" + event.Payload.Item.NftID.TokenID().String() - pusu.Publish(gb, publishChannel, event) - gloomberg.PrModf("seawa", "โš“๏ธ published %s event: %+v", event.EventType, event.Payload.Item.Name) - - // output to terminal - collectionPrimaryStyle := lipgloss.NewStyle().Foreground(style.GenerateColorWithSeed(event.Payload.Item.NftID.ContractAddress().Hash().Big().Int64())) - price := price.NewPrice(event.Payload.BasePrice) - fmtCurrencySymbol := collectionPrimaryStyle.Bold(false).Render("ฮž") - fmtPrice := style.BoldAlmostWhite(fmt.Sprintf("%5.2f", price.Ether())) + fmtCurrencySymbol - fmtItemName := collectionPrimaryStyle.Bold(true).Render(event.Payload.Item.Name) - fmtItemLink := style.TerminalLink(event.Payload.Item.Permalink, fmtItemName) - eventType := degendb.EventType(degendb.GetEventType(event.EventType)) - - gloomberg.Prf("%s %s %s", eventType.Icon(), fmtPrice, fmtItemLink) - } - case event := <-chanItemReceivedBid: - gbl.Log.Debugf(" ๐Ÿ’ฆ item received bid: %+v", pretty.Sprintf("%#v", event)) - - // log.Print(" ๐ŸŽญ ๐Ÿ’ฆ item received bid") - // pretty.Println(event) + gbl.Log.Debugf(" ๐Ÿ’ฆ item received bid: %+v", event) go HandleItemReceivedBid(gb, event) case event := <-chanCollectionOffer: - gbl.Log.Debugf(" ๐Ÿฆ• collection offer: %+v", pretty.Sprintf("%#v", event)) + gbl.Log.Debugf(" ๐Ÿฆ• collection offer: %+v", event) - // go HandleCollectionOffer(gb, event) + go HandleCollectionOffer(gb, event) case event := <-chanMetadataUpdated: + gbl.Log.Infof(" ๐ŸŽญ item metadata updated: %+v", event) + gbl.Log.Info(pretty.Sprint(event)) + log.Print(" ๐ŸŽญ ๐Ÿ’ฆ item metadata updated") + // filter lawless cloaknet transponders due to spam if event.Payload.ContractAddress() == common.HexToAddress("0xd3a0b315023243632a15fd623d6f33314193df4e") { continue @@ -86,7 +62,7 @@ func SeaWatcherEventsHandler(gb *gloomberg.Gloomberg) { log.Printf(" ๐ŸŽญ โ†’ %v", strings.Join(fmtTraits, style.DarkGrayStyle.Render(" | "))) gbl.Log.Info(" ๐ŸŽญ metadata updated:") - gbl.Log.Info(pretty.Sprint(event)) + // gbl.Log.Info(pretty.Sprint(event)) } // go HandleMetadataUpdated(gb, event) diff --git a/internal/trapri/item_listed.go b/internal/trapri/item_listed.go index 82efa77..754786f 100644 --- a/internal/trapri/item_listed.go +++ b/internal/trapri/item_listed.go @@ -16,6 +16,8 @@ import ( func HandleItemListed(gb *gloomberg.Gloomberg, event *models.ItemListed) { nftID := event.Payload.Item.NftID + contractAddress := nftID.ContractAddress() + // seller address sellerAddress := event.Payload.Maker.Address @@ -44,7 +46,7 @@ func HandleItemListed(gb *gloomberg.Gloomberg, event *models.ItemListed) { To: sellerAddress, AmountTokens: big.NewInt(int64(event.Payload.Quantity)), Token: &token.Token{ - Address: nftID.ContractAddress(), + Address: contractAddress, ID: big.NewInt(nftID.TokenID().Int64()), Name: itemName, }, @@ -52,13 +54,20 @@ func HandleItemListed(gb *gloomberg.Gloomberg, event *models.ItemListed) { }, } - // format and print + // format and print in stream gb.In.TokenTransactions <- ttxListing + // // ๐Ÿ’„ style + // primaryColor, _ := style.GenerateAddressColors(&contractAddress) + // collectionStyle := lipgloss.NewStyle().Foreground(primaryColor) + // fmtItemName := collectionStyle.Bold(true).Render(event.Payload.Item.Name) + // LogOpenSeaEvent(degendb.GetEventType(event.EventType), &contractAddress, price.NewPrice(event.Payload.BasePrice), fmtItemName, &event.Payload.Maker.Address) + // collection - if gb.ProviderPool != nil { - collection := tokencollections.GetCollection(gb, nftID.ContractAddress(), nftID.TokenID().Int64()) + if collection := tokencollections.GetCollection(gb, contractAddress, nftID.TokenID().Int64()); collection != nil { // counting for salira and more... collection.AddListing(uint64(event.Payload.Quantity)) + + return } } diff --git a/internal/trapri/item_metadata_updated.go b/internal/trapri/item_metadata_updated.go index 383f465..fb7eb72 100644 --- a/internal/trapri/item_metadata_updated.go +++ b/internal/trapri/item_metadata_updated.go @@ -1,62 +1,64 @@ package trapri -// import ( -// "math/big" -// "strings" - -// "github.com/benleb/gloomberg/internal/degendb" -// "github.com/benleb/gloomberg/internal/nemo/gloomberg" -// "github.com/benleb/gloomberg/internal/nemo/marketplace" -// "github.com/benleb/gloomberg/internal/nemo/token" -// "github.com/benleb/gloomberg/internal/nemo/tokencollections" -// "github.com/benleb/gloomberg/internal/nemo/totra" -// "github.com/benleb/gloomberg/internal/seawa/models" -// ) - -// func HandleItemMetdadataUpdated(gb *gloomberg.Gloomberg, event *models.ItemMetadataUpdated) { -// nftID := event.Payload.Item.NftID - -// // seller address -// sellerAddress := event.Payload.Maker.Address - -// // token name without id -// var itemName string -// if name := strings.Split(event.Payload.Item.Metadata.Name, " #")[0]; name != "" { -// itemName = name -// } else { -// itemName = event.Payload.Item.Metadata.Name -// } - -// // create a TokenTransaction -// ttxListing := &totra.TokenTransaction{ -// Tx: nil, -// TxReceipt: nil, -// From: sellerAddress, -// AmountPaid: big.NewInt(0), -// TotalTokens: int64(event.Payload.Quantity), -// Marketplace: &marketplace.OpenSea, -// Action: degendb.MetadataUpdate, -// ReceivedAt: event.Payload.CreatedDate, -// DoNotPrint: false, -// Transfers: []*totra.TokenTransfer{ -// { -// From: marketplace.OpenSea.ContractAddress(), -// To: sellerAddress, -// AmountTokens: big.NewInt(int64(event.Payload.Quantity)), -// Token: &token.Token{ -// Address: nftID.ContractAddress(), -// ID: big.NewInt(nftID.TokenID().Int64()), -// Name: itemName, -// }, -// }, -// }, -// } - -// // format and print -// gb.In.TokenTransactions <- ttxListing - -// // collection -// collkection := tokencollecthions.GetCollection(gb, nftID.ContractAddress(), nftID.TokenID().Int64()) -// // counting for salira and more... -// collection.AddListing(uint64(event.Payload.Quantity)) -// } +import ( + "math/big" + "strings" + + "github.com/benleb/gloomberg/internal" + "github.com/benleb/gloomberg/internal/degendb" + "github.com/benleb/gloomberg/internal/nemo/gloomberg" + "github.com/benleb/gloomberg/internal/nemo/marketplace" + "github.com/benleb/gloomberg/internal/nemo/token" + "github.com/benleb/gloomberg/internal/nemo/totra" + "github.com/benleb/gloomberg/internal/seawa/models" +) + +func HandleItemMetdadataUpdated(gb *gloomberg.Gloomberg, event *models.ItemMetadataUpdated) { + nftID := event.Payload.Item.NftID + contractAddress := nftID.ContractAddress() + + // seller address + sellerAddress := internal.ZeroAddress + + // token name without id + var itemName string + if name := strings.Split(event.Payload.Item.Metadata.Name, " #")[0]; name != "" { + itemName = name + } else { + itemName = event.Payload.Item.Metadata.Name + } + + // create a TokenTransaction + ttxMetadataUpdated := &totra.TokenTransaction{ + Tx: nil, + TxReceipt: nil, + From: sellerAddress, + AmountPaid: big.NewInt(0), + TotalTokens: int64(1), + Marketplace: &marketplace.OpenSea, + Action: degendb.MetadataUpdated, + ReceivedAt: event.Payload.CreatedDate, + DoNotPrint: false, + Transfers: []*totra.TokenTransfer{ + { + From: marketplace.OpenSea.ContractAddress(), + To: sellerAddress, + AmountTokens: big.NewInt(int64(1)), + Token: &token.Token{ + Address: contractAddress, + ID: big.NewInt(nftID.TokenID().Int64()), + Name: itemName, + }, + }, + }, + } + + // format and print + gb.In.TokenTransactions <- ttxMetadataUpdated + + // // ๐Ÿ’„ style + // primaryColor, _ := style.GenerateAddressColors(&contractAddress) + // collectionStyle := lipgloss.NewStyle().Foreground(primaryColor) + // fmtItemName := collectionStyle.Bold(true).Render(event.Payload.Item.Name) + // LogOpenSeaEvent(degendb.GetEventType(event.Event), &contractAddress, price.NewPrice(big.NewInt(0)), fmtItemName, &sellerAddress) +} diff --git a/internal/trapri/item_received_bid.go b/internal/trapri/item_received_bid.go index b8a1364..b285980 100644 --- a/internal/trapri/item_received_bid.go +++ b/internal/trapri/item_received_bid.go @@ -11,13 +11,9 @@ import ( "github.com/benleb/gloomberg/internal/nemo/gloomberg" "github.com/benleb/gloomberg/internal/nemo/marketplace" "github.com/benleb/gloomberg/internal/nemo/token" - "github.com/benleb/gloomberg/internal/nemo/tokencollections" "github.com/benleb/gloomberg/internal/nemo/totra" - "github.com/benleb/gloomberg/internal/notify" "github.com/benleb/gloomberg/internal/seawa/models" - "github.com/charmbracelet/log" "github.com/ethereum/go-ethereum/common" - "github.com/kr/pretty" ) var ( @@ -26,19 +22,14 @@ var ( ) func HandleItemReceivedBid(gb *gloomberg.Gloomberg, event *models.ItemReceivedBid) { - nftID := event.Payload.Item.NftID + nftID := event.Payload.NftID + + contractAddress := nftID.ContractAddress() // our token? - isOwnToken := gb.OwnWallets.ContainsToken(nftID.ContractAddress(), nftID.TokenID().String()) + isOwnToken := gb.OwnWallets.ContainsToken(contractAddress, nftID.TokenID().String()) // did someone from us make a bid? - isWatchUsersWallet := gb.Watcher.Contains(event.Payload.Maker.Address) - - collection := tokencollections.GetCollection(gb, nftID.ContractAddress(), nftID.TokenID().Int64()) - - collectionName := "unknown" - if collection != nil { - collectionName = collection.Name - } + isWatchUsersWallet := gb.Watcher != nil && gb.Watcher.Contains(event.Payload.Maker.Address) // check if we hold the token/got a bid if !isOwnToken && !isWatchUsersWallet { @@ -47,55 +38,49 @@ func HandleItemReceivedBid(gb *gloomberg.Gloomberg, event *models.ItemReceivedBi return } - // check if we already have a top bid for this token - // and if so, if the new bid is higher - // if it should be a new top bid, we highlight it when printing highlightBid := false // check if we already have a top bid for this token and if not, add it tokenTopBidsMutex.Lock() - if topBid, ok := tokenTopBids[nftID.TID()]; !ok || topBid == nil { - tokenTopBids[nftID.TID()] = event - } + currentTopBid := tokenTopBids[nftID.TID()] tokenTopBidsMutex.Unlock() - // get the current top bid for this token - topBid := tokenTopBids[nftID.TID()] - - log.Debugf("2 %s | own: %+v | watchUser: %+v", collectionName, isOwnToken, isWatchUsersWallet) - switch { // no or expired top bid - setting new top bid - case topBid == nil || topBid.Payload.ExpirationDate.Before(time.Now()): - tokenTopBids[nftID.TID()] = event + case currentTopBid == nil: + gbl.Log.Debugf("๐Ÿญ no top bid, new top bid: %+v", event.Payload.GetPrice().Wei()) + + case currentTopBid.Payload.ExpirationDate.Before(time.Now()): + gbl.Log.Debugf("๐Ÿญ top bid expired, new top bid: %+v", event.Payload.GetPrice().Wei()) // new bid is higher than current top bid - case topBid != nil: - // we add a small amount of ether/wei to the current top bid before comparing + case currentTopBid != nil: + // we add a small amount (still researching how much :D) of ether/wei to the current top bid before comparing // to prevent printing a lot of backrunned (=doubled) bids all the time - amountToAdd := big.NewInt(13370000000000001) // โ‰ˆ0.01337....ฮž - topBidPriceWithBuffer := big.NewInt(0).Add(topBid.Payload.GetPrice().Wei(), amountToAdd) + // amountToAdd := big.NewInt(13370000000000001) // โ‰ˆ0.01337....ฮž + amountToAdd := big.NewInt(7370000000000001) // โ‰ˆ0.00737....ฮž + + currentTopBidWithBuffer := big.NewInt(0).Add(currentTopBid.Payload.GetPrice().Wei(), amountToAdd) - if event.Payload.GetPrice().Wei().Cmp(topBidPriceWithBuffer) < 0 { - log.Debugf("๐Ÿญ bid lower than current topX:\n%+v\n>\n%+v", topBid, event) + if event.Payload.GetPrice().Wei().Cmp(currentTopBidWithBuffer) < 0 { + gbl.Log.Debugf("๐Ÿญ current top bid (+buffer) higher than incoming bid: %+v > %+v", currentTopBidWithBuffer, event.Payload.GetPrice().Wei()) return } - tokenTopBids[nftID.TID()] = event - highlightBid = true - - log.Debugf("๐Ÿญ new topX: %+v", tokenTopBids[nftID.TID()]) } + tokenTopBidsMutex.Lock() + tokenTopBids[nftID.TID()] = event + tokenTopBidsMutex.Unlock() + // seller address sellerAddress := common.HexToAddress(event.Payload.Maker.Address.Hex()) itemName := strings.Split(event.Payload.Item.Metadata.Name, " #")[0] - log.Debugf("3 %s | own: %+v | watchUser: %+v", collectionName, isOwnToken, isWatchUsersWallet) // // create a TokenTransaction ttxBid := &totra.TokenTransaction{ @@ -115,7 +100,7 @@ func HandleItemReceivedBid(gb *gloomberg.Gloomberg, event *models.ItemReceivedBi To: sellerAddress, AmountTokens: big.NewInt(int64(event.Payload.Quantity)), Token: &token.Token{ - Address: nftID.ContractAddress(), + Address: contractAddress, ID: nftID.TokenID(), Name: itemName, }, @@ -123,21 +108,14 @@ func HandleItemReceivedBid(gb *gloomberg.Gloomberg, event *models.ItemReceivedBi }, } - if isOwnToken { - // format and print - gb.In.TokenTransactions <- ttxBid - } - - log.Debugf("4 %s | own: %+v | watchUser: %+v", collectionName, isOwnToken, isWatchUsersWallet) + // format and print + gb.In.TokenTransactions <- ttxBid + // send notification if isWatchUsersWallet { - gbl.Log.Infof("๐Ÿงฑ sending telegram notification ItemReceivedBid ๐Ÿงš | isOwnToken: %+v | isWatchUsersWallet: %+v", isOwnToken, isWatchUsersWallet) - ttxBid.Action = degendb.OwnBid - log.Printf(" ๐ŸŠ ttxBid: %+v", pretty.Sprintf("%#v", event)) - // send notification - go notify.SendNotification(gb, ttxBid) + // go notify.SendNotification(gb, ttxBid) } } diff --git a/internal/trapri/listings.go b/internal/trapri/listings.go index ec97d62..879611d 100644 --- a/internal/trapri/listings.go +++ b/internal/trapri/listings.go @@ -1,132 +1,132 @@ package trapri -import ( - "fmt" - "math/big" - "strconv" - "strings" - "time" - - "github.com/benleb/gloomberg/internal/degendb" - "github.com/benleb/gloomberg/internal/gbl" - "github.com/benleb/gloomberg/internal/nemo/gloomberg" - "github.com/benleb/gloomberg/internal/nemo/marketplace" - "github.com/benleb/gloomberg/internal/nemo/osmodels" - "github.com/benleb/gloomberg/internal/nemo/price" - "github.com/benleb/gloomberg/internal/nemo/token" - "github.com/benleb/gloomberg/internal/nemo/tokencollections" - "github.com/benleb/gloomberg/internal/nemo/totra" - "github.com/benleb/gloomberg/internal/notify" - "github.com/benleb/gloomberg/internal/style" - "github.com/charmbracelet/log" - "github.com/ethereum/go-ethereum/common" - "github.com/spf13/viper" -) - -func FormatListing(gb *gloomberg.Gloomberg, event *osmodels.ItemListedEvent) { - // nftID is a string in the format // - nftID := strings.Split(event.Payload.Item.NftID, "/") - if len(nftID) != 3 { - gbl.Log.Infof("๐Ÿคทโ€โ™€๏ธ error parsing nftID: %s", event.Payload.Item.NftID) - } - - contractAddress := common.HexToAddress(nftID[1]) - - // get tokenID from nftID - tokenID, _ := strconv.ParseInt(nftID[2], 10, 64) - - // seller address - sellerAddress := common.HexToAddress(event.Payload.Maker.Address) - - // parse amountPaid - priceWeiRaw, _, err := big.ParseFloat(event.Payload.BasePrice.String(), 10, 64, big.ToNearestEven) - if err != nil { - gbl.Log.Errorf("โŒ error parsing amountPaid: %s", err.Error()) - - return - } - - priceWei, _ := priceWeiRaw.Int(nil) - amountPaid := price.NewPrice(priceWei) - - // collection - collection := tokencollections.GetCollection(gb, contractAddress, tokenID) - - // counting for salira and more... - collection.AddListing(uint64(event.Payload.Quantity)) - - var receivedAt time.Time - if sentAt, err := time.Parse(time.RFC3339, event.Payload.EventTimestamp); err == nil { - receivedAt = sentAt - } else { - gbl.Log.Debugf("โŒ failed to parse sentAt: %s | %+v | %+v", err, event.Payload.EventTimestamp, event.Payload.ListingDate) - receivedAt = time.Now() - } - - itemName := strings.Split(event.Payload.Item.Metadata.Name, " #")[0] - - // - // create a TokenTransaction - ttxListing := &totra.TokenTransaction{ - Tx: nil, - TxReceipt: nil, - From: sellerAddress, - AmountPaid: amountPaid.Wei(), - TotalTokens: int64(event.Payload.Quantity), - Marketplace: &marketplace.OpenSea, - Action: degendb.Listing, - ReceivedAt: receivedAt, - DoNotPrint: false, - Transfers: []*totra.TokenTransfer{ - { - From: marketplace.OpenSea.ContractAddress(), - To: sellerAddress, - AmountTokens: big.NewInt(int64(event.Payload.Quantity)), - Token: &token.Token{ - Address: contractAddress, - ID: big.NewInt(tokenID), - Name: itemName, - }, - }, - }, - } - - gbl.Log.Debugf("%s: %+v | %+v", event.StreamEvent, ttxListing, event) - - // format and print - gb.In.TokenTransactions <- ttxListing - - // highlight "rare" lawless listings - if contractAddress == common.HexToAddress("0xb119ec7ee48928a94789ed0842309faf34f0c790") { - tokenName := event.Payload.Item.Metadata.Name - - switch { - case strings.Contains(tokenName, "-qf"): - tokenName = strings.Replace(tokenName, "-qf", style.PinkBoldStyle.Render("-qf * * * "), 1) - case strings.Contains(tokenName, "-rq"): - tokenName = strings.Replace(tokenName, "-rq", style.PinkBoldStyle.Render("-rq"), 1) - case strings.Contains(tokenName, "-pq"): - tokenName = strings.Replace(tokenName, "-pq", style.Bold("-pq"), 1) - case strings.Contains(tokenName, "-qp"): - tokenName = strings.Replace(tokenName, "-qp", style.Bold("-qp"), 1) - case strings.Contains(tokenName, "-qr"): - tokenName = strings.Replace(tokenName, "-qr", style.Bold("-qr"), 1) - - default: - log.Debugf("lawless listing but common token: %s", tokenName) - - return - } - - osLink := style.TerminalLink(event.Payload.Item.Permalink, event.Payload.Item.Permalink) - - go notify.SendMessageViaTelegram(fmt.Sprintf("lawless listing: %s \n amountPaid: %s url: %s", tokenName, fmt.Sprintf("%5.3f", amountPaid.Ether()), osLink), viper.GetInt64("notifications.manifold.dakma"), "", 0, nil) - - highlightMessage := strings.Builder{} - highlightMessage.WriteString("\n") - highlightMessage.WriteString(fmt.Sprintf(" lawless %s | %5.3fฮž | %s\n", tokenName, amountPaid.Ether(), osLink)) - highlightMessage.WriteString("\n") - - fmt.Println(highlightMessage.String()) //nolint:forbidigo - } -} +// import ( +// "fmt" +// "math/big" +// "strconv" +// "strings" +// "time" + +// "github.com/benleb/gloomberg/internal/degendb" +// "github.com/benleb/gloomberg/internal/gbl" +// "github.com/benleb/gloomberg/internal/nemo/gloomberg" +// "github.com/benleb/gloomberg/internal/nemo/marketplace" +// "github.com/benleb/gloomberg/internal/nemo/osmodels" +// "github.com/benleb/gloomberg/internal/nemo/price" +// "github.com/benleb/gloomberg/internal/nemo/token" +// "github.com/benleb/gloomberg/internal/nemo/tokencollections" +// "github.com/benleb/gloomberg/internal/nemo/totra" +// "github.com/benleb/gloomberg/internal/notify" +// "github.com/benleb/gloomberg/internal/style" +// "github.com/charmbracelet/log" +// "github.com/ethereum/go-ethereum/common" +// "github.com/spf13/viper" +// ) + +// func FormatListing(gb *gloomberg.Gloomberg, event *osmodels.ItemListedEvent) { +// // nftID is a string in the format // +// nftID := strings.Split(event.Payload.Item.NftID, "/") +// if len(nftID) != 3 { +// gbl.Log.Infof("๐Ÿคทโ€โ™€๏ธ error parsing nftID: %s", event.Payload.Item.NftID) +// } + +// contractAddress := common.HexToAddress(nftID[1]) + +// // get tokenID from nftID +// tokenID, _ := strconv.ParseInt(nftID[2], 10, 64) + +// // seller address +// sellerAddress := common.HexToAddress(event.Payload.Maker.Address) + +// // parse amountPaid +// priceWeiRaw, _, err := big.ParseFloat(event.Payload.BasePrice.String(), 10, 64, big.ToNearestEven) +// if err != nil { +// gbl.Log.Errorf("โŒ error parsing amountPaid: %s", err.Error()) + +// return +// } + +// priceWei, _ := priceWeiRaw.Int(nil) +// amountPaid := price.NewPrice(priceWei) + +// // collection +// collection := tokencollections.GetCollection(gb, contractAddress, tokenID) + +// // counting for salira and more... +// collection.AddListing(uint64(event.Payload.Quantity)) + +// var receivedAt time.Time +// if sentAt, err := time.Parse(time.RFC3339, event.Payload.EventTimestamp); err == nil { +// receivedAt = sentAt +// } else { +// gbl.Log.Debugf("โŒ failed to parse sentAt: %s | %+v | %+v", err, event.Payload.EventTimestamp, event.Payload.ListingDate) +// receivedAt = time.Now() +// } + +// itemName := strings.Split(event.Payload.Item.Metadata.Name, " #")[0] + +// // +// // create a TokenTransaction +// ttxListing := &totra.TokenTransaction{ +// Tx: nil, +// TxReceipt: nil, +// From: sellerAddress, +// AmountPaid: amountPaid.Wei(), +// TotalTokens: int64(event.Payload.Quantity), +// Marketplace: &marketplace.OpenSea, +// Action: degendb.Listing, +// ReceivedAt: receivedAt, +// DoNotPrint: false, +// Transfers: []*totra.TokenTransfer{ +// { +// From: marketplace.OpenSea.ContractAddress(), +// To: sellerAddress, +// AmountTokens: big.NewInt(int64(event.Payload.Quantity)), +// Token: &token.Token{ +// Address: contractAddress, +// ID: big.NewInt(tokenID), +// Name: itemName, +// }, +// }, +// }, +// } + +// gbl.Log.Debugf("%s: %+v | %+v", event.StreamEvent, ttxListing, event) + +// // format and print +// gb.In.TokenTransactions <- ttxListing + +// // highlight "rare" lawless listings +// if contractAddress == common.HexToAddress("0xb119ec7ee48928a94789ed0842309faf34f0c790") { +// tokenName := event.Payload.Item.Metadata.Name + +// switch { +// case strings.Contains(tokenName, "-qf"): +// tokenName = strings.Replace(tokenName, "-qf", style.PinkBoldStyle.Render("-qf * * * "), 1) +// case strings.Contains(tokenName, "-rq"): +// tokenName = strings.Replace(tokenName, "-rq", style.PinkBoldStyle.Render("-rq"), 1) +// case strings.Contains(tokenName, "-pq"): +// tokenName = strings.Replace(tokenName, "-pq", style.Bold("-pq"), 1) +// case strings.Contains(tokenName, "-qp"): +// tokenName = strings.Replace(tokenName, "-qp", style.Bold("-qp"), 1) +// case strings.Contains(tokenName, "-qr"): +// tokenName = strings.Replace(tokenName, "-qr", style.Bold("-qr"), 1) + +// default: +// log.Debugf("lawless listing but common token: %s", tokenName) + +// return +// } + +// osLink := style.TerminalLink(event.Payload.Item.Permalink, event.Payload.Item.Permalink) + +// go notify.SendMessageViaTelegram(fmt.Sprintf("lawless listing: %s \n amountPaid: %s url: %s", tokenName, fmt.Sprintf("%5.3f", amountPaid.Ether()), osLink), viper.GetInt64("notifications.manifold.dakma"), "", 0, nil) + +// highlightMessage := strings.Builder{} +// highlightMessage.WriteString("\n") +// highlightMessage.WriteString(fmt.Sprintf(" lawless %s | %5.3fฮž | %s\n", tokenName, amountPaid.Ether(), osLink)) +// highlightMessage.WriteString("\n") + +// fmt.Println(highlightMessage.String()) //nolint:forbidigo +// } +// } diff --git a/internal/trapri/trapri.go b/internal/trapri/trapri.go index 6189d96..c613e87 100644 --- a/internal/trapri/trapri.go +++ b/internal/trapri/trapri.go @@ -15,14 +15,13 @@ import ( "github.com/benleb/gloomberg/internal/gbl" "github.com/benleb/gloomberg/internal/jobs" "github.com/benleb/gloomberg/internal/nemo/gloomberg" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc" - "github.com/benleb/gloomberg/internal/nemo/gloomberg/gbgrpc/gen" "github.com/benleb/gloomberg/internal/nemo/price" "github.com/benleb/gloomberg/internal/nemo/standard" "github.com/benleb/gloomberg/internal/nemo/tokencollections" "github.com/benleb/gloomberg/internal/nemo/totra" "github.com/benleb/gloomberg/internal/notify" "github.com/benleb/gloomberg/internal/opensea" + "github.com/benleb/gloomberg/internal/pusu" seawatcher "github.com/benleb/gloomberg/internal/seawa" "github.com/benleb/gloomberg/internal/style" "github.com/benleb/gloomberg/internal/ticker" @@ -30,11 +29,14 @@ import ( "github.com/benleb/gloomberg/internal/utils/wwatcher" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/log" + mapset "github.com/deckarep/golang-set/v2" "github.com/ethereum/go-ethereum/common" "github.com/kr/pretty" "github.com/spf13/viper" ) +var alreadySubscribed = mapset.NewSet[string]() + // func TokenTransactionFormatter(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatcher, queueWsOutTokenTransactions chan *totra.TokenTransaction, queueWsInTokenTransactions chan *totra.TokenTransaction) {. func TokenTransactionFormatter(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatcher) { gbl.Log.Debugf("๐Ÿงฑ starting ttx formatter worker") @@ -254,8 +256,29 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche transferredToken.Rank = rank transferredToken.RankSymbol = rankSymbol - // fmtRank := style.TrendLightGreenStyle.Copy().Bold(false).Render(fmt.Sprintf("%d%s", rank, rankSymbol)) - fmtRank := lipgloss.NewStyle().Foreground(style.OpenseaToneBlue).Render(fmt.Sprintf("%d%s", rank, rankSymbol)) + // get total supply of the collection + totalSupply := uint64(0) + if collection.Metadata.TotalSupply > 0.0 { + totalSupply = collection.Metadata.TotalSupply + } else if collection.Raw.Stats.TotalSupply > 0.0 { + totalSupply = uint64(collection.Raw.Stats.TotalSupply) + } + + // calculate relative ranking if total supply is available + relativeRanking := 1.0 + if totalSupply > 0 { + relativeRanking = float64(rank) / float64(totalSupply) + } + + // format rank information + var fmtRank string + if relativeRanking < 0.337 { + // show the rank (and optional symbol) if it's in the top 33.7% + fmtRank = lipgloss.NewStyle().Foreground(style.OpenseaToneBlue).Render(fmt.Sprintf("%d%s", rank, rankSymbol)) + } else { + // otherwise we do not show any rank information but just a symbol (to indicate that it's a ranked token) + fmtRank = lipgloss.NewStyle().Foreground(style.OpenseaToneBlue).Faint(true).Render("โŠ–") + } fmtTokenID.WriteString(fmtRank) } @@ -312,9 +335,7 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche var fmtTotalSupply string if transfer.Standard == standard.ERC1155 && (isOwnWallet || isOwnCollection || isWatchUsersWallet) { - // if supply, err := gb.Nodes.TotalSupplyERC1155(ctx, transfer.Token.Address, transfer.Token.ID); err == nil { if supply, err := gb.ProviderPool.ERC1155TotalSupply(ctx, transfer.Token.Address, transfer.Token.ID); err == nil { - // fmtTokenID.WriteString(style.DarkGrayStyle.Render(" /") + collection.StyleSecondary().Copy().Faint(true).Render(supply.String())) fmtTotalSupply = style.DarkGrayStyle.Render(" /") + collection.StyleSecondary().Copy().Faint(true).Render(supply.String()) } } @@ -343,21 +364,14 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche if numCollectionTokens > 1 { numberStyle, _ := getNumberStyles(int(numCollectionTokens)) - // - // check if this was sweep or someone just dumped a lot of tokens into (blur) bids - if tfFrom := ttx.GetNonZeroNFTSenders(); len(tfFrom) > 0 { - for sender, transfers := range tfFrom { - if numCollectionTokens == int64(len(transfers)) && ttx.From == sender { - // all tokens sold by the same address -> dumped into bids - // isBidDump = true - if numCollectionTokens > 5 || ttx.GetPrice().Ether() > 3.0 { - // if it's a significant amount of tokens or ether we use a reddish style - numberStyle = style.TrendRedStyle - } else { - // otherwise we use a light red style - numberStyle = style.TrendLightRedStyle - } - } + // real purchase or accepted offers/dump into bids? + if ttx.IsAcceptedOffer() { + if numCollectionTokens > 7 || ttx.GetPrice().Ether() > 3.37 { + // if it's a significant amount of tokens or ether we use a reddish style + numberStyle = style.TrendRedStyle + } else { + // otherwise we use a light red style + numberStyle = style.TrendLightRedStyle } } @@ -402,8 +416,12 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche // use a variant without a link for the history // needed due to a bug causing unnecessary line breaks - fmtEvent.WriteString(name + " " + strings.Join(fmtTokenIds[contractAddress][:idsShown], collection.StyleSecondary().Copy().Faint(true).Render(", "))) - fmtHistoryEvent.WriteString(name + " " + strings.Join(fmtHistoryTokenIds[contractAddress][:idsShown], collection.StyleSecondary().Copy().Faint(true).Render(", "))) + + fmtEvent.WriteString(name) + if !ttx.IsCollectionOffer() { + fmtEvent.WriteString(" " + strings.Join(fmtTokenIds[contractAddress][:idsShown], collection.StyleSecondary().Copy().Faint(true).Render(", "))) + fmtHistoryEvent.WriteString(name + " " + strings.Join(fmtHistoryTokenIds[contractAddress][:idsShown], collection.StyleSecondary().Copy().Faint(true).Render(", "))) + } if len(fmtTokenIds[contractAddress]) > maxShown && collection != nil { fmtEvent.WriteString(collection.StyleSecondary().Render("โ€ฆ")) @@ -471,20 +489,30 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche parsedEvent.Colors.Time = style.DarkGray - if ttx.IsListing() { + switch { + case ttx.IsListing(): timeNow = style.Gray7Style.Render(currentTime) parsedEvent.Colors.Time = style.Gray7 - } else if isOwnCollection { - timeNow = currentCollection.Style().Copy().Bold(true).Render(currentTime) + + case ttx.IsCollectionOffer() || ttx.IsItemBid(): + timeNow = currentCollection.Style().Copy().Faint(true).Render(currentTime) parsedEvent.Colors.Time = currentCollection.Colors.Primary - } - // highlight line if the seller or buyer is a wallet from the configured wallets - if isOwnWallet { + case isOwnWallet: timeNow = lipgloss.NewStyle().Foreground(style.Pink).Bold(true).Render(currentTime) parsedEvent.Colors.Time = lipgloss.Color(style.Pink.Dark) + + case isOwnCollection: + timeNow = currentCollection.Style().Copy().Bold(true).Render(currentTime) + parsedEvent.Colors.Time = currentCollection.Colors.Primary } + // // highlight line if the seller or buyer is a wallet from the configured wallets + // if isOwnWallet { + // timeNow = lipgloss.NewStyle().Foreground(style.Pink).Bold(true).Render(currentTime) + // parsedEvent.Colors.Time = lipgloss.Color(style.Pink.Dark) + // } + // is our own wallet or collection isOwn := isOwnWallet || isOwnCollection @@ -570,7 +598,11 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche // average price (makes no sense for multi-collections tx) averagePrice := ttx.GetPrice() if ttx.TotalTokens > 1 { - averagePrice = price.NewPrice(big.NewInt(0).Div(ttx.AmountPaid, big.NewInt(ttx.TotalTokens))) + if ttx.IsCollectionOffer() { + averagePrice = price.NewPrice(big.NewInt(0).Mul(ttx.AmountPaid, big.NewInt(ttx.TotalTokens))) + } else { + averagePrice = price.NewPrice(big.NewInt(0).Div(ttx.AmountPaid, big.NewInt(ttx.TotalTokens))) + } } priceWidth := "%6.3f" @@ -601,7 +633,7 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche } } - out.WriteString(ttx.GetPurchaseOrBidIndicator()) + out.WriteString(ttx.GetPAOI()) // average price per item pricePerItemStyle := style.DarkerGrayStyle @@ -613,23 +645,8 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche out.WriteString("" + pricePerItemStyle.Render(formattedAveragePriceEther)) out.WriteString(formattedFaintCurrencySymbol) - // // floor price TODO fix this - // var trendIndicatorStyle lipgloss.Style - // if currentCollection != nil { - // trendIndicatorStyle = style.CreateTrendIndicator(currentCollection.PreviousFloorPrice, currentFloorPrice) - // } else { - // trendIndicatorStyle = style.CreateTrendIndicator(0.0, currentFloorPrice) - // } - currentFloorPriceStyle := style.DarkerGrayStyle - // if currentFloorPrice > 0.0 { - // currentFloorPriceStyle = style.GrayStyle.Copy().Faint(true) - // } - - // trendIndicatorFaintStyle := trendIndicatorStyle.Copy().Faint(true) - // out.WriteString(" " + currentFloorPriceStyle.Render(fmt.Sprintf("%6.3f", currentFloorPrice)) + trendIndicatorFaintStyle.Render("ฮž")) - // print sales for collection if viper.GetBool("show.sales") { numLastSales, _ := currentCollection.GetSaLiCount() @@ -780,7 +797,7 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche } arrow := style.DividerArrowRight - if ttx.IsListing() || ttx.IsBurn() { + if ttx.IsListing() || ttx.IsItemBid() || ttx.IsCollectionOffer() || ttx.IsBurn() { arrow = style.DividerArrowLeft } @@ -856,22 +873,11 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche currentCollection.OpenseaSlug = opensea.GetCollectionSlug(currentCollection.ContractAddress) } - if !seawa.IsSubscribed(currentCollection.OpenseaSlug) { - // if seawa.SubscribeForSlug(currentCollection.OpenseaSlug) { - // gprcClient := gbgrpc.NewClient("") - - if gbgrpc.GRPCClient != nil { - subsriptionRequest := &gen.SubscriptionRequest{EventTypes: []gen.EventType{gen.EventType_ITEM_LISTED}, Collections: []string{currentCollection.OpenseaSlug}} //nolint:nosnakecase - - _, err := gbgrpc.GRPCClient.Subscribe(context.Background(), subsriptionRequest, nil) - if err != nil { - log.Errorf("failed to subscribe to events for %s: %s", currentCollection.OpenseaSlug, err) - } - - log.Printf("subsubsubbbyyyyyyyyyyyyyyy: %+v", subsriptionRequest) - } + if !alreadySubscribed.Contains(currentCollection.OpenseaSlug) && !seawa.IsSubscribedToAllEvents(currentCollection.OpenseaSlug) { + if seawa.SubscribeForSlug(currentCollection.OpenseaSlug, []degendb.EventType{degendb.Listing, degendb.CollectionOffer, degendb.Bid}) > 0 { //nolint:nosnakecase + alreadySubscribed.Add(currentCollection.OpenseaSlug) + pusu.SubscribeToListingsViaRedis(gb) - if seawa.SubscribeForSlug(currentCollection.OpenseaSlug, []gen.EventType{gen.EventType_ITEM_LISTED, gen.EventType_ITEM_RECEIVED_BID, gen.EventType_COLLECTION_OFFER}) > 0 { //nolint:nosnakecase seawa.Pr(fmt.Sprintf("auto-subscribed to events for %s (after %d sales) | stats resetted", style.AlmostWhiteStyle.Render(currentCollection.OpenseaSlug), autoSubscribeAfterSales)) } } @@ -999,7 +1005,7 @@ func formatTokenTransaction(gb *gloomberg.Gloomberg, seawa *seawatcher.SeaWatche parsedEvent.IsOwnWallet = isOwnWallet parsedEvent.IsOwnCollection = isOwnCollection parsedEvent.IsWatchUsersWallet = isWatchUsersWallet - parsedEvent.PurchaseOrBidIndicator = ttx.GetPurchaseOrBidIndicator() + parsedEvent.PAOI = ttx.GetPAOI() // ...and actually use this!! gb.RecentOwnEvents.Add(&parsedEvent)