119 lines
2.5 KiB
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
|
||
|
}
|