Skip to content

Commit

Permalink
Merge pull request #1405 from consolelabs/fix/update-job-nft-balances
Browse files Browse the repository at this point in the history
fix: update job - fetch nft balances
  • Loading branch information
anhnh12 authored Jul 9, 2024
2 parents 25edea7 + 4245015 commit 7cce79e
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 79 deletions.
128 changes: 69 additions & 59 deletions pkg/entities/nfts.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (

"github.com/defipod/mochi/pkg/config"
"github.com/defipod/mochi/pkg/consts"
"github.com/defipod/mochi/pkg/contract/neko"
"github.com/defipod/mochi/pkg/contract/nekostaking"
"github.com/defipod/mochi/pkg/contracts/erc721"
"github.com/defipod/mochi/pkg/logger"
Expand Down Expand Up @@ -1349,13 +1348,12 @@ func (e *Entity) GetProfileNftBalance(req request.GetProfileNFTsRequest) (*respo

}

go e.fetchNftBalance(req.ProfileID, collection)
// go e.fetchNftBalance(req.ProfileID, collection)

return &data, nil
}

func (e *Entity) fetchNftBalance(profileID string, collection *model.NFTCollection) {
fmt.Println(profileID == "", !strings.EqualFold(collection.ERCFormat, model.ErcFormat721) || !collection.IsVerified)
if profileID == "" {
return
}
Expand Down Expand Up @@ -1497,97 +1495,105 @@ func (e *Entity) GetNekoBalanceFunc(config model.NFTCollectionConfig) (func(addr
}

// TODO: remove after airdrop
func (e *Entity) GetNekoHolders(col model.NFTCollection) ([]model.UserNFTBalance, error) {
func (e *Entity) FetchHolders(col model.NFTCollection) ([]model.UserNFTBalance, error) {
chainID, err := strconv.Atoi(col.ChainID)
if err != nil {
e.log.Errorf(err, "[GetNekoHolders] strconv.Atoi() failed")
e.log.Errorf(err, "[FetchHolders] strconv.Atoi() failed")
return nil, err
}

chain, err := e.repo.Chain.GetByID(chainID)
if err != nil {
e.log.Errorf(err, "[GetNekoHolders] repo.Chain.GetByID() failed")
e.log.Errorf(err, "[FetchHolders] repo.Chain.GetByID() failed")
return nil, err
}

client, err := ethclient.Dial(chain.RPC)
if err != nil {
e.log.Errorf(err, "[GetNekoHolders] ethclient.Dial() failed")
e.log.Errorf(err, "[FetchHolders] ethclient.Dial() failed")
return nil, err
}

nekoInstance, err := neko.NewNeko(common.HexToAddress(col.Address), client)
tokenTracker, err := erc721.NewErc721(common.HexToAddress(col.Address), client)
if err != nil {
e.log.Errorf(err, "[GetNekoHolders] neko.NewNeko() failed")
e.log.Errorf(err, "[FetchHolders] erc721.NewErc721() failed")
return nil, err
}

stakingAddr := consts.NekoStakingContractAddress
stakingInstance, err := nekostaking.NewNekostaking(common.HexToAddress(stakingAddr), client)
if err != nil {
e.log.Errorf(err, "[GetNekoHolders] nekostaking.NewNekoStaking() failed")
return nil, err
}

stakings, err := nekoInstance.TokensOfOwner(nil, common.HexToAddress(stakingAddr))
if err != nil {
e.log.Errorf(err, "[GetNekoHolders] nekoInstance.TokensOfOwner() failed")
return nil, err
}
stakingIds := sliceutils.Map(stakings, func(t *big.Int) int64 {
return t.Int64()
})
// tokenTracker, err := neko.NewNeko(common.HexToAddress(col.Address), client)
// if err != nil {
// e.log.Errorf(err, "[FetchHolders] neko.NewNeko() failed")
// return nil, err
// }

// stakingAddr := consts.NekoStakingContractAddress
// stakingInstance, err := nekostaking.NewNekostaking(common.HexToAddress(stakingAddr), client)
// if err != nil {
// e.log.Errorf(err, "[FetchHolders] nekostaking.NewNekoStaking() failed")
// return nil, err
// }

// stakings, err := nekoInstance.TokensOfOwner(nil, common.HexToAddress(stakingAddr))
// if err != nil {
// e.log.Errorf(err, "[FetchHolders] nekoInstance.TokensOfOwner() failed")
// return nil, err
// }
// stakingIds := sliceutils.Map(stakings, func(t *big.Int) int64 {
// return t.Int64()
// })

var unstakings []*big.Int
for tokenId := 0; tokenId < col.TotalSupply; tokenId++ {
if sliceutils.Contains(stakingIds, int64(tokenId)) {
continue
}
// if sliceutils.Contains(stakingIds, int64(tokenId)) {
// continue
// }
unstakings = append(unstakings, big.NewInt(int64(tokenId)))
}

var wg sync.WaitGroup
wg.Add(2)
// var wg sync.WaitGroup
// wg.Add(2)

holders := make(map[string]int)
tokensByHolder := make(map[string][]int64)
go func(tokenIds []*big.Int) {
for _, tokenId := range tokenIds {
owner, err := nekoInstance.OwnerOf(&bind.CallOpts{}, tokenId)
if err != nil {
e.log.Errorf(err, "[GetNekoHolders] failed to get balance of %d in chain %s", tokenId.Int64(), col.ChainID)
continue
}

k := strings.ToLower(owner.Hex())
holders[k]++
tokensByHolder[k] = append(tokensByHolder[k], tokenId.Int64())
// go func(tokenIds []*big.Int) {
tokenIds := unstakings
for _, tokenId := range tokenIds {
owner, err := tokenTracker.OwnerOf(&bind.CallOpts{}, tokenId)
if err != nil {
e.log.Errorf(err, "[FetchHolders] failed to get balance of %d in chain %s", tokenId.Int64(), col.ChainID)
continue
}
wg.Done()
}(unstakings)
e.log.Infof("Token %d owner: %s", tokenId.Int64(), owner.Hex())

k := strings.ToLower(owner.Hex())
holders[k]++
tokensByHolder[k] = append(tokensByHolder[k], tokenId.Int64())
}
// wg.Done()
// }(unstakings)

stakers := make(map[string]int)
tokensByStaker := make(map[string][]int64)
go func(tokenIds []*big.Int) {
for _, tokenId := range tokenIds {
staker, err := stakingInstance.Owners(&bind.CallOpts{}, tokenId)
if err != nil {
e.log.Errorf(err, "[GetNekoHolders] stakingInstance.Owners() failed")
continue
}

k := strings.ToLower(staker.Hex())
stakers[k]++
tokensByStaker[k] = append(tokensByStaker[k], tokenId.Int64())
}
wg.Done()
}(stakings)

wg.Wait()
// go func(tokenIds []*big.Int) {
// for _, tokenId := range tokenIds {
// staker, err := stakingInstance.Owners(&bind.CallOpts{}, tokenId)
// if err != nil {
// e.log.Errorf(err, "[FetchHolders] stakingInstance.Owners() failed")
// continue
// }

// k := strings.ToLower(staker.Hex())
// stakers[k]++
// tokensByStaker[k] = append(tokensByStaker[k], tokenId.Int64())
// }
// wg.Done()
// }(stakings)

// wg.Wait()

evmAccs, err := e.ListAllWalletAddresses()
if err != nil {
e.log.Error(err, "[GetNekoHolders] ListAllWalletAddresses() failed")
e.log.Error(err, "[FetchHolders] ListAllWalletAddresses() failed")
return nil, err
}

Expand Down Expand Up @@ -1617,3 +1623,7 @@ func (e *Entity) ExistedNekoHolder(colId, addr string) bool {
existed, _ := e.repo.UserNFTBalance.IsExists(colId, addr)
return existed
}

func (e *Entity) GetPodTownNFTBalances(collectionAddresses []string) ([]model.PodTownUserNFTBalance, error) {
return e.repo.UserNFTBalance.GetPodTownUserNFTBalances(collectionAddresses)
}
1 change: 1 addition & 0 deletions pkg/handler/nft/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@ type IHandler interface {
GetNftTokenTickers(c *gin.Context)
GetNftSalesHandler(c *gin.Context)
GetProfileNFTBalances(c *gin.Context)
GetPodTownNFTBalances(c *gin.Context)
}
18 changes: 18 additions & 0 deletions pkg/handler/nft/nft.go
Original file line number Diff line number Diff line change
Expand Up @@ -644,3 +644,21 @@ func (h *Handler) GetProfileNFTBalances(c *gin.Context) {

c.JSON(http.StatusOK, response.CreateResponse(data, nil, nil, nil))
}

func (h *Handler) GetPodTownNFTBalances(c *gin.Context) {
req := &request.GetPodTownNFTBalancesRequest{}
if err := req.Bind(c); err != nil {
h.log.Info("[handler.GetPodTownNFTBalances] Bind() failed")
c.JSON(http.StatusBadRequest, response.CreateResponse[any](nil, nil, err, nil))
return
}

data, err := h.entities.GetPodTownNFTBalances(req.CollectionAddresses)
if err != nil {
h.log.Error(err, "[handler.GetPodTownNFTBalances] entity.GetPodTownNFTBalances() failed")
c.JSON(http.StatusInternalServerError, response.CreateResponse[any](nil, nil, err, nil))
return
}

c.JSON(http.StatusOK, response.CreateResponse(data, nil, nil, nil))
}
45 changes: 25 additions & 20 deletions pkg/job/update_user_nft_balance.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package job

import (
"strings"

"github.com/defipod/mochi/pkg/entities"
"github.com/defipod/mochi/pkg/logger"
"github.com/defipod/mochi/pkg/model"
sliceutils "github.com/defipod/mochi/pkg/util/slice"
)

Expand Down Expand Up @@ -84,27 +81,35 @@ func (c *updateUserNFTBalances) Run() error {
return err
}

nekoAddr := "0x7aCeE5D0acC520faB33b3Ea25D4FEEF1FfebDE73"
nekoCol := sliceutils.Find(collections, func(c model.NFTCollection) bool {
return strings.EqualFold(nekoAddr, c.Address)
})
// collectionAddresses := ["0x7aCeE5D0acC520faB33b3Ea25D4FEEF1FfebDE73", "0x7aCeE5D0acC520faB33b3Ea25D4FEEF1FfebDE73", ]
// nekoAddr :=
// nekoCol := sliceutils.Find(collections, func(c model.NFTCollection) bool {
// return strings.EqualFold(nekoAddr, c.Address)
// })

if nekoCol == nil {
c.log.Info("Neko Collection not found")
return nil
}
// if nekoCol == nil {
// c.log.Info("Neko Collection not found")
// return nil
// }

data, err := c.entity.GetNekoHolders(*nekoCol)
if err != nil {
c.log.Errorf(err, "entity.GetNekoHolders() failed")
return err
}
for _, col := range collections {
fetched := []string{"0x7aCeE5D0acC520faB33b3Ea25D4FEEF1FfebDE73"}
if sliceutils.Contains(fetched, col.Address) {
continue
}

for _, bal := range data {
err = c.entity.NewUserNFTBalance(bal)
data, err := c.entity.FetchHolders(col)
if err != nil {
c.log.Errorf(err, "NewUserNFTBalance() failed")
continue
c.log.Errorf(err, "entity.FetchHolders() failed")
return err
}

for _, bal := range data {
err = c.entity.NewUserNFTBalance(bal)
if err != nil {
c.log.Errorf(err, "NewUserNFTBalance() failed")
continue
}
}
}

Expand Down
10 changes: 10 additions & 0 deletions pkg/model/user_nft_balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,13 @@ type UserNFTBalanceIdentify struct {
UserDiscordId string `json:"user_discord_id"`
NftCollectionId string `json:"nft_collection_id"`
}

type PodTownUserNFTBalance struct {
ProfileId string `json:"profile_id"`
DiscordId string `json:"discord_id"`
UserAddress string `json:"user_address"`
Neko int `json:"neko"`
Rabby int `json:"rabby"`
Fukuro int `json:"fukuro"`
Gm int `json:"gm"`
}
22 changes: 22 additions & 0 deletions pkg/repo/user_nft_balance/pg.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"gorm.io/gorm"
"gorm.io/gorm/clause"

"github.com/defipod/mochi/pkg/consts"
"github.com/defipod/mochi/pkg/model"
)

Expand Down Expand Up @@ -119,3 +120,24 @@ func (pg *pg) IsExists(collectionID, userAddress string) (bool, error) {
Count(&count).Error
return count > 0, err
}

func (pg *pg) GetPodTownUserNFTBalances(collectionAddresses []string) ([]model.PodTownUserNFTBalance, error) {
podTownGuildId := "882287783169896468"
var data []model.PodTownUserNFTBalance
return data, pg.db.Table("user_nft_balances b").
Select(`
b.user_address,
b.profile_id,
sum(b.balance + staking_nekos) filter (where c.symbol ilike 'NEKO') as neko,
sum(b.balance) filter (where c.symbol ilike 'RABBY') as rabby,
sum(b.balance) filter (where c.symbol ilike 'FUKURO') as fukuro,
max(aa.platform_identifier) discord_id,
max(ugs.total_count) as gm
`).
Joins("LEFT JOIN nft_collections c ON b.nft_collection_id = c.id").
Joins("LEFT JOIN associated_accounts aa ON b.profile_id = aa.profile_id and aa.platform = ?", consts.PlatformDiscord).
Joins("LEFT JOIN discord_user_gm_streaks ugs ON aa.platform_identifier = ugs.discord_id AND ugs.guild_id = ?", podTownGuildId).
Where("c.address IN ?", collectionAddresses).
Group("b.user_address, b.profile_id").
Find(&data).Error
}
1 change: 1 addition & 0 deletions pkg/repo/user_nft_balance/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ type Store interface {
List(ListQuery) ([]model.UserNFTBalance, error)
TotalBalance(collectionID string) (int, error)
IsExists(collectionID, userAddress string) (bool, error)
GetPodTownUserNFTBalances(collectionAddresses []string) ([]model.PodTownUserNFTBalance, error)
}
20 changes: 20 additions & 0 deletions pkg/request/nft.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package request

import (
"strings"

"github.com/gin-gonic/gin"
)

type AddNftWatchlistRequest struct {
WatchlistBaseRequest
GuildID string `json:"guild_id"`
Expand Down Expand Up @@ -32,3 +38,17 @@ type GetProfileNFTsRequest struct {
ProfileID string `json:"profile_id" form:"profile_id"`
CollectionAddress string `json:"address" uri:"address" binding:"required"`
}

type GetPodTownNFTBalancesRequest struct {
Param string `json:"addresses" form:"addresses" binding:"required"`
CollectionAddresses []string `json:"-" form:"-"`
}

func (req *GetPodTownNFTBalancesRequest) Bind(c *gin.Context) error {
if err := c.BindQuery(&req); err != nil {
return err
}

req.CollectionAddresses = strings.Split(req.Param, ",")
return nil
}
1 change: 1 addition & 0 deletions pkg/routes/v1.go
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ func NewRoutes(r *gin.Engine, h *handler.Handler, cfg config.Config) {

// nft balances
collectionsGroup.GET("/address/:address/balances", h.Nft.GetProfileNFTBalances)
collectionsGroup.GET("/podtown/balances", h.Nft.GetPodTownNFTBalances) // TODO: remove after airdrop
}

defaultNftTickerGroup := nftsGroup.Group("/default-nft-ticker")
Expand Down

0 comments on commit 7cce79e

Please sign in to comment.