pancakeswapnft/floor.go

119 lines
2.5 KiB
Go

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
}