package pancakeswapnft import ( "context" "sync" "time" "github.com/machinebox/graphql" "github.com/pkg/errors" "github.com/rs/zerolog/log" ) type FloorService struct { Done chan struct{} collection string graphClient *graphql.Client floors map[time.Time]float64 lock *sync.RWMutex cacheDuration time.Duration } func NewFloorService(ctx context.Context, collection string, graphClient *graphql.Client, cacheDuration time.Duration) *FloorService { s := &FloorService{ Done: make(chan struct{}), collection: collection, graphClient: graphClient, floors: make(map[time.Time]float64), lock: &sync.RWMutex{}, cacheDuration: cacheDuration, } s.start(ctx) return s } func (s *FloorService) start(ctx context.Context) { t := time.NewTicker(1 * time.Minute) err := s.watchfloor(ctx) if err != nil { log.Error().Err(err).Msg("watching floor") } go func() { defer close(s.Done) for { select { case <-ctx.Done(): return case <-t.C: err := s.watchfloor(ctx) if err != nil { log.Error().Err(err).Msg("watching floor") } } } }() } func (s *FloorService) watchfloor(ctx context.Context) error { req := graphql.NewRequest(` query ($first: Int, $skip: Int!, $where: NFT_filter, $orderBy: NFT_orderBy, $orderDirection: OrderDirection) { nfts(where: $where, first: $first, orderBy: $orderBy, orderDirection: $orderDirection, skip: $skip) { currentAskPrice } }`) req.Var("first", 1) req.Var("orderBy", "currentAskPrice") req.Var("orderDirection", "asc") req.Var("skip", 0) req.Var("where", struct { Collection string `json:"collection"` IsTradable bool `json:"isTradable"` }{ Collection: s.collection, IsTradable: true, }) var respData struct { Nfts []Nft `json:"nfts"` } if err := s.graphClient.Run(ctx, req, &respData); err != nil { return errors.Wrap(err, "querying graphql") } if len(respData.Nfts) <= 0 { return errors.Errorf("no tradable nft found") } floor := respData.Nfts[0].CurrentAskPrice s.lock.Lock() newfloors := make(map[time.Time]float64) for t, f := range s.floors { if t.After(time.Now().Add(-s.cacheDuration)) { newfloors[t] = f } } newfloors[time.Now()] = floor s.floors = newfloors s.lock.Unlock() return nil } func (s *FloorService) GetFloor() float64 { s.lock.RLock() defer s.lock.RUnlock() var tot float64 for _, f := range s.floors { tot += f } res := tot / float64(len(s.floors)) if res < 0.001 { res = 3 } return res }