Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
anhnh12 committed Jul 10, 2024
2 parents 3abd619 + 7c5a315 commit 5fb7cf3
Show file tree
Hide file tree
Showing 11 changed files with 208 additions and 79 deletions.
41 changes: 41 additions & 0 deletions migrations/schemas/20240710114601-init_temp_accounts_table.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@

-- +migrate Up

DROP TABLE IF EXISTS temp_associated_accounts;

CREATE TYPE "public"."profile_type" AS ENUM ('user', 'application', 'vault', 'application_vault');

CREATE TABLE "public"."profiles" (
"id" text NOT NULL PRIMARY KEY,
"created_at" timestamp NOT NULL DEFAULT now(),
"updated_at" timestamp NOT NULL DEFAULT now(),
"profile_name" text,
"avatar" text,
"type" "public"."profile_type" DEFAULT 'user'::profile_type,
"application_id" int4,
"vault_id" int4,
"active_score" int4 NOT NULL DEFAULT 0,
"did_onboarding_telegram" bool NOT NULL DEFAULT false
);

CREATE TABLE "public"."associated_accounts" (
"id" int8 NOT NULL,
"profile_id" text NOT NULL,
"platform" varchar(255) NOT NULL,
"platform_identifier" varchar(255) NOT NULL,
"created_at" timestamp NOT NULL DEFAULT now(),
"updated_at" timestamp NOT NULL DEFAULT now(),
"platform_metadata" json,
CONSTRAINT "fk_profile_id" FOREIGN KEY ("profile_id") REFERENCES "public"."profiles"("id") ON DELETE CASCADE
);

CREATE UNIQUE INDEX aa_platform_platform_identifier_unique ON public.associated_accounts USING btree (platform, platform_identifier);
CREATE INDEX idx_associated_accounts ON public.associated_accounts USING btree (profile_id);
CREATE INDEX idx_associated_accounts_metadata_username ON public.associated_accounts USING gin (to_tsvector('english'::regconfig, (platform_metadata ->> 'username'::text)));
CREATE INDEX idx_onlyy_associated_accounts_profile_id ON public.associated_accounts USING btree (profile_id) INCLUDE (id, platform, platform_identifier, platform_metadata, created_at, updated_at);

-- +migrate Down
DROP TABLE IF EXISTS associated_accounts;
DROP TABLE IF EXISTS profiles;

DROP TYPE IF EXISTS profile_type;
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)
}
Loading

0 comments on commit 5fb7cf3

Please sign in to comment.