2021-11-25 07:04:16 +00:00
|
|
|
package pancakeswapnft
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"github.com/dustin/go-humanize"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/machinebox/graphql"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Transaction struct {
|
|
|
|
Id string `json:"id"`
|
|
|
|
Block string `json:"block"`
|
|
|
|
Timestamp int64 `json:"timestamp,string"`
|
|
|
|
Collection Collection `json:"collection"`
|
|
|
|
Nft Nft `json:"nft"`
|
|
|
|
AskPrice float64 `json:"askPrice,string"`
|
|
|
|
NetPrice float64 `json:"netPrice,string"`
|
|
|
|
Buyer User `json:"buyer"`
|
2021-12-22 06:38:57 +00:00
|
|
|
BuyerUsername string
|
|
|
|
Seller User `json:"seller"`
|
|
|
|
SellerUsername string
|
|
|
|
WithBNB bool `json:"withBNB"`
|
2021-11-25 07:04:16 +00:00
|
|
|
Time time.Time
|
|
|
|
Squad Squad
|
|
|
|
Score float64
|
|
|
|
Url string
|
|
|
|
TimeDescription string
|
2021-12-06 09:49:50 +00:00
|
|
|
IsMine bool
|
2021-11-25 07:04:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type TransactionService struct {
|
2021-12-22 06:38:57 +00:00
|
|
|
graphClient *graphql.Client
|
|
|
|
squadservice *SquadService
|
|
|
|
floorService *FloorService
|
|
|
|
profileService *ProfileService
|
2021-12-06 09:49:50 +00:00
|
|
|
|
|
|
|
bscaddress string
|
2021-11-25 07:04:16 +00:00
|
|
|
}
|
|
|
|
|
2021-12-22 06:38:57 +00:00
|
|
|
func NewTransactionService(graphClient *graphql.Client, squadservice *SquadService, floorService *FloorService, profileService *ProfileService, bscaddress string) *TransactionService {
|
2021-11-25 07:04:16 +00:00
|
|
|
return &TransactionService{
|
2021-12-22 06:38:57 +00:00
|
|
|
graphClient: graphClient,
|
|
|
|
squadservice: squadservice,
|
|
|
|
floorService: floorService,
|
|
|
|
profileService: profileService,
|
|
|
|
bscaddress: bscaddress,
|
2021-11-25 07:04:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type WhereOptions struct {
|
|
|
|
Collection string `json:"collection,omitempty"`
|
|
|
|
TimestampLte string `json:"timestamp_lte,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *TransactionService) GetLightPageByLimit(ctx context.Context, pagenumber int, limit int, opts WhereOptions) ([]Transaction, error) {
|
|
|
|
req := graphql.NewRequest(`
|
|
|
|
query getNftsMarketData($first: Int, $skip: Int!, $where: Transaction_filter, $orderBy: Transaction_orderBy, $orderDirection: OrderDirection) {
|
|
|
|
transactions(where: $where, first: $first, orderBy: $orderBy, orderDirection: $orderDirection, skip: $skip) {
|
|
|
|
timestamp
|
|
|
|
askPrice
|
2021-12-06 09:23:51 +00:00
|
|
|
netPrice
|
2021-11-25 07:04:16 +00:00
|
|
|
nft {
|
|
|
|
tokenId
|
2021-12-06 09:17:31 +00:00
|
|
|
transactionHistory {
|
|
|
|
timestamp
|
|
|
|
askPrice
|
|
|
|
}
|
2021-11-25 07:04:16 +00:00
|
|
|
}
|
|
|
|
buyer {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
seller {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}`)
|
|
|
|
req.Var("first", limit)
|
|
|
|
req.Var("orderBy", "timestamp")
|
|
|
|
req.Var("orderDirection", "desc")
|
|
|
|
req.Var("skip", (pagenumber-1)*1000)
|
|
|
|
req.Var("where", opts)
|
|
|
|
|
|
|
|
var respData struct {
|
|
|
|
Transactions []*Transaction `json:"transactions"`
|
|
|
|
}
|
|
|
|
if err := s.graphClient.Run(ctx, req, &respData); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "requesting graphql")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, transaction := range respData.Transactions {
|
|
|
|
squad, err := s.squadservice.GetSquad(transaction.Nft.TokenID)
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("getting squad for tokenid %d", transaction.Nft.TokenID)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
transaction.Squad = squad
|
|
|
|
|
|
|
|
transaction.Score = transaction.Squad.GetScoreByPrice(s.floorService, transaction.AskPrice)
|
|
|
|
transaction.Url = fmt.Sprintf("https://pancakeswap.finance/nfts/collections/%s/%d", opts.Collection, transaction.Nft.TokenID)
|
|
|
|
|
|
|
|
transaction.Time = time.Unix(transaction.Timestamp, 0)
|
|
|
|
transaction.TimeDescription = humanize.Time(transaction.Time)
|
2021-12-06 09:38:44 +00:00
|
|
|
sort.Slice(transaction.Nft.TransactionHistory, func(i, j int) bool {
|
|
|
|
return transaction.Nft.TransactionHistory[i].Timestamp > transaction.Nft.TransactionHistory[j].Timestamp
|
|
|
|
})
|
2021-12-06 09:49:50 +00:00
|
|
|
transaction.IsMine = transaction.Buyer.Address == s.bscaddress || transaction.Seller.Address == s.bscaddress
|
2021-12-22 06:38:57 +00:00
|
|
|
if n, err := s.profileService.GetUsername(transaction.Seller.Address); err != nil {
|
|
|
|
transaction.SellerUsername = transaction.Seller.Address
|
|
|
|
} else {
|
|
|
|
transaction.SellerUsername = n
|
|
|
|
}
|
|
|
|
if n, err := s.profileService.GetUsername(transaction.Buyer.Address); err != nil {
|
|
|
|
transaction.BuyerUsername = transaction.Buyer.Address
|
|
|
|
} else {
|
|
|
|
transaction.BuyerUsername = n
|
|
|
|
}
|
2021-11-25 07:04:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var res []Transaction
|
|
|
|
for _, transaction := range respData.Transactions {
|
|
|
|
res = append(res, *transaction)
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *TransactionService) GetPageByLimit(ctx context.Context, pagenumber int, limit int, opts WhereOptions) ([]Transaction, error) {
|
|
|
|
req := graphql.NewRequest(`
|
|
|
|
query getNftsMarketData($first: Int, $skip: Int!, $where: Transaction_filter, $orderBy: Transaction_orderBy, $orderDirection: OrderDirection) {
|
|
|
|
transactions(where: $where, first: $first, orderBy: $orderBy, orderDirection: $orderDirection, skip: $skip) {
|
|
|
|
id
|
|
|
|
block
|
|
|
|
timestamp
|
|
|
|
askPrice
|
|
|
|
netPrice
|
|
|
|
nft {
|
|
|
|
tokenId
|
|
|
|
}
|
|
|
|
collection {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
buyer {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
seller {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
withBNB
|
|
|
|
}
|
|
|
|
}`)
|
|
|
|
req.Var("first", limit)
|
|
|
|
req.Var("orderBy", "timestamp")
|
|
|
|
req.Var("orderDirection", "desc")
|
|
|
|
req.Var("skip", (pagenumber-1)*1000)
|
|
|
|
req.Var("where", opts)
|
|
|
|
|
|
|
|
var respData struct {
|
|
|
|
Transactions []*Transaction `json:"transactions"`
|
|
|
|
}
|
|
|
|
if err := s.graphClient.Run(ctx, req, &respData); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "requesting graphql")
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, transaction := range respData.Transactions {
|
2021-12-22 06:38:57 +00:00
|
|
|
squad, err := s.squadservice.GetSquad(transaction.Nft.TokenID)
|
2021-11-25 07:04:16 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msgf("getting squad for tokenid %d", transaction.Nft.TokenID)
|
|
|
|
continue
|
|
|
|
}
|
2021-12-22 06:38:57 +00:00
|
|
|
transaction.Squad = squad
|
2021-11-25 07:04:16 +00:00
|
|
|
|
2021-12-22 06:38:57 +00:00
|
|
|
transaction.Score = 10 / ((float64(squad.Rank) / float64(10000)) * transaction.AskPrice)
|
2021-11-25 07:04:16 +00:00
|
|
|
transaction.Url = fmt.Sprintf("https://pancakeswap.finance/nfts/collections/%s/%d", opts.Collection, transaction.Nft.TokenID)
|
|
|
|
|
|
|
|
transaction.Time = time.Unix(transaction.Timestamp, 0)
|
|
|
|
transaction.TimeDescription = humanize.Time(transaction.Time)
|
2021-12-22 06:38:57 +00:00
|
|
|
if n, err := s.profileService.GetUsername(transaction.Seller.Address); err != nil {
|
|
|
|
transaction.SellerUsername = transaction.Seller.Address
|
|
|
|
} else {
|
|
|
|
transaction.SellerUsername = n
|
|
|
|
}
|
|
|
|
if n, err := s.profileService.GetUsername(transaction.Buyer.Address); err != nil {
|
|
|
|
transaction.BuyerUsername = transaction.Buyer.Address
|
|
|
|
} else {
|
|
|
|
transaction.BuyerUsername = n
|
|
|
|
}
|
2021-11-25 07:04:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var res []Transaction
|
|
|
|
for _, transaction := range respData.Transactions {
|
|
|
|
res = append(res, *transaction)
|
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
func (s *TransactionService) GetAll(ctx context.Context, collection string) ([]Transaction, error) {
|
|
|
|
var res []Transaction
|
|
|
|
currentpage := 1
|
|
|
|
whereoptions := WhereOptions{
|
|
|
|
Collection: collection,
|
|
|
|
TimestampLte: "",
|
|
|
|
}
|
|
|
|
now := time.Now()
|
|
|
|
tmpmap := make(map[string]Transaction)
|
|
|
|
loop:
|
|
|
|
for {
|
|
|
|
log.Debug().Msgf("timestamp : %s", whereoptions.TimestampLte)
|
|
|
|
page, err := s.GetPageByLimit(ctx, currentpage, 1000, whereoptions)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "getting page %d", currentpage)
|
|
|
|
}
|
|
|
|
if len(page) == 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
for _, transaction := range page {
|
|
|
|
if transaction.Time.Before(now.Add(-7 * 24 * time.Hour)) {
|
|
|
|
break loop
|
|
|
|
}
|
|
|
|
tmpmap[transaction.Id] = transaction
|
|
|
|
}
|
|
|
|
|
|
|
|
currentpage++
|
|
|
|
whereoptions.TimestampLte = strconv.Itoa(int(page[len(page)-1].Timestamp))
|
|
|
|
}
|
|
|
|
for _, transaction := range tmpmap {
|
|
|
|
res = append(res, transaction)
|
|
|
|
}
|
|
|
|
sort.Slice(res, func(i, j int) bool {
|
|
|
|
return res[i].Timestamp > res[j].Timestamp
|
|
|
|
})
|
|
|
|
return res, nil
|
|
|
|
}
|