diff --git a/nft.go b/nft.go index 68c2100..d855944 100644 --- a/nft.go +++ b/nft.go @@ -26,14 +26,15 @@ type AskOrder struct { } type Nft struct { - Id string `json:"id"` - TokenID int64 `json:"tokenId,string"` - OtherId int64 `json:"otherId,string"` - Collection Collection `json:"collection"` - MetadataUrl string `json:"metadataUrl"` - UpdatedAt int64 `json:"updatedAt,string"` - CurrentAskPrice float64 `json:"currentAskPrice,string"` - CurrentSeller string `json:"currentSeller"` + Id string `json:"id"` + TokenID int64 `json:"tokenId,string"` + OtherId int64 `json:"otherId,string"` + Collection Collection `json:"collection"` + MetadataUrl string `json:"metadataUrl"` + UpdatedAt int64 `json:"updatedAt,string"` + CurrentAskPrice float64 `json:"currentAskPrice,string"` + CurrentSeller string `json:"currentSeller"` + CurrentSellerUsername string LatestTradedPriceInBNB float64 `json:"latestTradedPriceInBNB,string"` TradeVolumeBNB float64 `json:"tradeVolumeBNB,string"` TotalTrades int64 `json:"totalTrades,string"` @@ -51,10 +52,11 @@ type Nft struct { } type MarketService struct { - collection string - graphClient *graphql.Client - squadservice *SquadService - floorService *FloorService + collection string + graphClient *graphql.Client + squadservice *SquadService + floorService *FloorService + profileService *ProfileService bscaddress string @@ -63,12 +65,13 @@ type MarketService struct { nftslock *sync.RWMutex } -func NewMarketService(collection string, graphClient *graphql.Client, squadservice *SquadService, floorService *FloorService, bscadress string) *MarketService { +func NewMarketService(collection string, graphClient *graphql.Client, squadservice *SquadService, floorService *FloorService, profileService *ProfileService, bscadress string) *MarketService { return &MarketService{ - collection: collection, - graphClient: graphClient, - squadservice: squadservice, - floorService: floorService, + collection: collection, + graphClient: graphClient, + squadservice: squadservice, + floorService: floorService, + profileService: profileService, bscaddress: bscadress, @@ -205,6 +208,12 @@ func (s *MarketService) GetNft(ctx context.Context, collection string, tokenId i res.IsVeryRecent = time.Unix(res.UpdatedAt, 0).After(time.Now().Add(-5 * time.Minute)) res.IsNotMine = res.CurrentSeller != s.bscaddress + if n, err := s.profileService.GetUsername(res.CurrentSeller); err != nil { + res.CurrentSellerUsername = res.CurrentSeller + } else { + res.CurrentSellerUsername = n + } + return res, nil } @@ -265,6 +274,11 @@ func (s *MarketService) getPage(ctx context.Context, collection string, pagenum floor = nft.CurrentAskPrice } nft.IsNotMine = nft.CurrentSeller != s.bscaddress + if n, err := s.profileService.GetUsername(nft.CurrentSeller); err != nil { + nft.CurrentSellerUsername = nft.CurrentSeller + } else { + nft.CurrentSellerUsername = n + } res = append(res, nft) } diff --git a/profile.go b/profile.go new file mode 100644 index 0000000..fdca980 --- /dev/null +++ b/profile.go @@ -0,0 +1,84 @@ +package pancakeswapnft + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/pkg/errors" + "io" + "net/http" + "sync" + "time" +) + +type Profile struct { + Address string `json:"address"` + Username string `json:"username"` + Leaderboard struct { + Global string `json:"global"` + Team string `json:"team"` + Volume int `json:"volume"` + NextRank string `json:"next_rank"` + } `json:"leaderboard"` + LeaderboardFantoken struct { + Global string `json:"global"` + Team string `json:"team"` + Volume int `json:"volume"` + NextRank string `json:"next_rank"` + } `json:"leaderboard_fantoken"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +func getUsernameFromAddress(address string) (string, error) { + resp, err := http.Get(fmt.Sprintf("https://profile.pancakeswap.com/api/users/%s", address)) + if err != nil { + return "", err + } + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + raw, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + if resp.StatusCode != http.StatusOK { + return "", errors.Errorf("non 200 http status : %d / raw : %s", resp.StatusCode, raw) + } + var profile Profile + err = json.NewDecoder(bytes.NewReader(raw)).Decode(&profile) + if err != nil { + return "", err + } + + return profile.Username, nil +} + +type ProfileService struct { + cache map[string]string + lock *sync.Mutex +} + +func NewProfileService() *ProfileService { + return &ProfileService{ + cache: make(map[string]string), + lock: &sync.Mutex{}, + } +} + +func (s *ProfileService) GetUsername(address string) (string, error) { + s.lock.Lock() + defer s.lock.Unlock() + if name, ok := s.cache[address]; ok { + return name, nil + } + + name, err := getUsernameFromAddress(address) + if err != nil { + return "", err + } + s.cache[address] = name + + return name, nil +} diff --git a/transaction.go b/transaction.go index 0c6d627..7a43c5f 100644 --- a/transaction.go +++ b/transaction.go @@ -22,8 +22,10 @@ type Transaction struct { AskPrice float64 `json:"askPrice,string"` NetPrice float64 `json:"netPrice,string"` Buyer User `json:"buyer"` - Seller User `json:"seller"` - WithBNB bool `json:"withBNB"` + BuyerUsername string + Seller User `json:"seller"` + SellerUsername string + WithBNB bool `json:"withBNB"` Time time.Time Squad Squad Score float64 @@ -33,19 +35,21 @@ type Transaction struct { } type TransactionService struct { - graphClient *graphql.Client - squadservice *SquadService - floorService *FloorService + graphClient *graphql.Client + squadservice *SquadService + floorService *FloorService + profileService *ProfileService bscaddress string } -func NewTransactionService(graphClient *graphql.Client, squadservice *SquadService, floorService *FloorService, bscaddress string) *TransactionService { +func NewTransactionService(graphClient *graphql.Client, squadservice *SquadService, floorService *FloorService, profileService *ProfileService, bscaddress string) *TransactionService { return &TransactionService{ - graphClient: graphClient, - squadservice: squadservice, - floorService: floorService, - bscaddress: bscaddress, + graphClient: graphClient, + squadservice: squadservice, + floorService: floorService, + profileService: profileService, + bscaddress: bscaddress, } } @@ -106,6 +110,16 @@ func (s *TransactionService) GetLightPageByLimit(ctx context.Context, pagenumber return transaction.Nft.TransactionHistory[i].Timestamp > transaction.Nft.TransactionHistory[j].Timestamp }) transaction.IsMine = transaction.Buyer.Address == s.bscaddress || transaction.Seller.Address == s.bscaddress + 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 + } } var res []Transaction @@ -153,18 +167,28 @@ func (s *TransactionService) GetPageByLimit(ctx context.Context, pagenumber int, } for _, transaction := range respData.Transactions { - s, err := s.squadservice.GetSquad(transaction.Nft.TokenID) + 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 = s + transaction.Squad = squad - transaction.Score = 10 / ((float64(s.Rank) / float64(10000)) * transaction.AskPrice) + transaction.Score = 10 / ((float64(squad.Rank) / float64(10000)) * 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) + 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 + } } var res []Transaction