Skip to content

Commit 5fb7cf3

Browse files
committed
Merge branch 'develop'
2 parents 3abd619 + 7c5a315 commit 5fb7cf3

File tree

11 files changed

+208
-79
lines changed

11 files changed

+208
-79
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
-- +migrate Up
3+
4+
DROP TABLE IF EXISTS temp_associated_accounts;
5+
6+
CREATE TYPE "public"."profile_type" AS ENUM ('user', 'application', 'vault', 'application_vault');
7+
8+
CREATE TABLE "public"."profiles" (
9+
"id" text NOT NULL PRIMARY KEY,
10+
"created_at" timestamp NOT NULL DEFAULT now(),
11+
"updated_at" timestamp NOT NULL DEFAULT now(),
12+
"profile_name" text,
13+
"avatar" text,
14+
"type" "public"."profile_type" DEFAULT 'user'::profile_type,
15+
"application_id" int4,
16+
"vault_id" int4,
17+
"active_score" int4 NOT NULL DEFAULT 0,
18+
"did_onboarding_telegram" bool NOT NULL DEFAULT false
19+
);
20+
21+
CREATE TABLE "public"."associated_accounts" (
22+
"id" int8 NOT NULL,
23+
"profile_id" text NOT NULL,
24+
"platform" varchar(255) NOT NULL,
25+
"platform_identifier" varchar(255) NOT NULL,
26+
"created_at" timestamp NOT NULL DEFAULT now(),
27+
"updated_at" timestamp NOT NULL DEFAULT now(),
28+
"platform_metadata" json,
29+
CONSTRAINT "fk_profile_id" FOREIGN KEY ("profile_id") REFERENCES "public"."profiles"("id") ON DELETE CASCADE
30+
);
31+
32+
CREATE UNIQUE INDEX aa_platform_platform_identifier_unique ON public.associated_accounts USING btree (platform, platform_identifier);
33+
CREATE INDEX idx_associated_accounts ON public.associated_accounts USING btree (profile_id);
34+
CREATE INDEX idx_associated_accounts_metadata_username ON public.associated_accounts USING gin (to_tsvector('english'::regconfig, (platform_metadata ->> 'username'::text)));
35+
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);
36+
37+
-- +migrate Down
38+
DROP TABLE IF EXISTS associated_accounts;
39+
DROP TABLE IF EXISTS profiles;
40+
41+
DROP TYPE IF EXISTS profile_type;

pkg/entities/nfts.go

Lines changed: 69 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121

2222
"github.com/defipod/mochi/pkg/config"
2323
"github.com/defipod/mochi/pkg/consts"
24-
"github.com/defipod/mochi/pkg/contract/neko"
2524
"github.com/defipod/mochi/pkg/contract/nekostaking"
2625
"github.com/defipod/mochi/pkg/contracts/erc721"
2726
"github.com/defipod/mochi/pkg/logger"
@@ -1349,13 +1348,12 @@ func (e *Entity) GetProfileNftBalance(req request.GetProfileNFTsRequest) (*respo
13491348

13501349
}
13511350

1352-
go e.fetchNftBalance(req.ProfileID, collection)
1351+
// go e.fetchNftBalance(req.ProfileID, collection)
13531352

13541353
return &data, nil
13551354
}
13561355

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

14991497
// TODO: remove after airdrop
1500-
func (e *Entity) GetNekoHolders(col model.NFTCollection) ([]model.UserNFTBalance, error) {
1498+
func (e *Entity) FetchHolders(col model.NFTCollection) ([]model.UserNFTBalance, error) {
15011499
chainID, err := strconv.Atoi(col.ChainID)
15021500
if err != nil {
1503-
e.log.Errorf(err, "[GetNekoHolders] strconv.Atoi() failed")
1501+
e.log.Errorf(err, "[FetchHolders] strconv.Atoi() failed")
15041502
return nil, err
15051503
}
15061504

15071505
chain, err := e.repo.Chain.GetByID(chainID)
15081506
if err != nil {
1509-
e.log.Errorf(err, "[GetNekoHolders] repo.Chain.GetByID() failed")
1507+
e.log.Errorf(err, "[FetchHolders] repo.Chain.GetByID() failed")
15101508
return nil, err
15111509
}
15121510

15131511
client, err := ethclient.Dial(chain.RPC)
15141512
if err != nil {
1515-
e.log.Errorf(err, "[GetNekoHolders] ethclient.Dial() failed")
1513+
e.log.Errorf(err, "[FetchHolders] ethclient.Dial() failed")
15161514
return nil, err
15171515
}
15181516

1519-
nekoInstance, err := neko.NewNeko(common.HexToAddress(col.Address), client)
1517+
tokenTracker, err := erc721.NewErc721(common.HexToAddress(col.Address), client)
15201518
if err != nil {
1521-
e.log.Errorf(err, "[GetNekoHolders] neko.NewNeko() failed")
1519+
e.log.Errorf(err, "[FetchHolders] erc721.NewErc721() failed")
15221520
return nil, err
15231521
}
15241522

1525-
stakingAddr := consts.NekoStakingContractAddress
1526-
stakingInstance, err := nekostaking.NewNekostaking(common.HexToAddress(stakingAddr), client)
1527-
if err != nil {
1528-
e.log.Errorf(err, "[GetNekoHolders] nekostaking.NewNekoStaking() failed")
1529-
return nil, err
1530-
}
1531-
1532-
stakings, err := nekoInstance.TokensOfOwner(nil, common.HexToAddress(stakingAddr))
1533-
if err != nil {
1534-
e.log.Errorf(err, "[GetNekoHolders] nekoInstance.TokensOfOwner() failed")
1535-
return nil, err
1536-
}
1537-
stakingIds := sliceutils.Map(stakings, func(t *big.Int) int64 {
1538-
return t.Int64()
1539-
})
1523+
// tokenTracker, err := neko.NewNeko(common.HexToAddress(col.Address), client)
1524+
// if err != nil {
1525+
// e.log.Errorf(err, "[FetchHolders] neko.NewNeko() failed")
1526+
// return nil, err
1527+
// }
1528+
1529+
// stakingAddr := consts.NekoStakingContractAddress
1530+
// stakingInstance, err := nekostaking.NewNekostaking(common.HexToAddress(stakingAddr), client)
1531+
// if err != nil {
1532+
// e.log.Errorf(err, "[FetchHolders] nekostaking.NewNekoStaking() failed")
1533+
// return nil, err
1534+
// }
1535+
1536+
// stakings, err := nekoInstance.TokensOfOwner(nil, common.HexToAddress(stakingAddr))
1537+
// if err != nil {
1538+
// e.log.Errorf(err, "[FetchHolders] nekoInstance.TokensOfOwner() failed")
1539+
// return nil, err
1540+
// }
1541+
// stakingIds := sliceutils.Map(stakings, func(t *big.Int) int64 {
1542+
// return t.Int64()
1543+
// })
15401544

15411545
var unstakings []*big.Int
15421546
for tokenId := 0; tokenId < col.TotalSupply; tokenId++ {
1543-
if sliceutils.Contains(stakingIds, int64(tokenId)) {
1544-
continue
1545-
}
1547+
// if sliceutils.Contains(stakingIds, int64(tokenId)) {
1548+
// continue
1549+
// }
15461550
unstakings = append(unstakings, big.NewInt(int64(tokenId)))
15471551
}
15481552

1549-
var wg sync.WaitGroup
1550-
wg.Add(2)
1553+
// var wg sync.WaitGroup
1554+
// wg.Add(2)
15511555

15521556
holders := make(map[string]int)
15531557
tokensByHolder := make(map[string][]int64)
1554-
go func(tokenIds []*big.Int) {
1555-
for _, tokenId := range tokenIds {
1556-
owner, err := nekoInstance.OwnerOf(&bind.CallOpts{}, tokenId)
1557-
if err != nil {
1558-
e.log.Errorf(err, "[GetNekoHolders] failed to get balance of %d in chain %s", tokenId.Int64(), col.ChainID)
1559-
continue
1560-
}
1561-
1562-
k := strings.ToLower(owner.Hex())
1563-
holders[k]++
1564-
tokensByHolder[k] = append(tokensByHolder[k], tokenId.Int64())
1558+
// go func(tokenIds []*big.Int) {
1559+
tokenIds := unstakings
1560+
for _, tokenId := range tokenIds {
1561+
owner, err := tokenTracker.OwnerOf(&bind.CallOpts{}, tokenId)
1562+
if err != nil {
1563+
e.log.Errorf(err, "[FetchHolders] failed to get balance of %d in chain %s", tokenId.Int64(), col.ChainID)
1564+
continue
15651565
}
1566-
wg.Done()
1567-
}(unstakings)
1566+
e.log.Infof("Token %d owner: %s", tokenId.Int64(), owner.Hex())
1567+
1568+
k := strings.ToLower(owner.Hex())
1569+
holders[k]++
1570+
tokensByHolder[k] = append(tokensByHolder[k], tokenId.Int64())
1571+
}
1572+
// wg.Done()
1573+
// }(unstakings)
15681574

15691575
stakers := make(map[string]int)
15701576
tokensByStaker := make(map[string][]int64)
1571-
go func(tokenIds []*big.Int) {
1572-
for _, tokenId := range tokenIds {
1573-
staker, err := stakingInstance.Owners(&bind.CallOpts{}, tokenId)
1574-
if err != nil {
1575-
e.log.Errorf(err, "[GetNekoHolders] stakingInstance.Owners() failed")
1576-
continue
1577-
}
1578-
1579-
k := strings.ToLower(staker.Hex())
1580-
stakers[k]++
1581-
tokensByStaker[k] = append(tokensByStaker[k], tokenId.Int64())
1582-
}
1583-
wg.Done()
1584-
}(stakings)
1585-
1586-
wg.Wait()
1577+
// go func(tokenIds []*big.Int) {
1578+
// for _, tokenId := range tokenIds {
1579+
// staker, err := stakingInstance.Owners(&bind.CallOpts{}, tokenId)
1580+
// if err != nil {
1581+
// e.log.Errorf(err, "[FetchHolders] stakingInstance.Owners() failed")
1582+
// continue
1583+
// }
1584+
1585+
// k := strings.ToLower(staker.Hex())
1586+
// stakers[k]++
1587+
// tokensByStaker[k] = append(tokensByStaker[k], tokenId.Int64())
1588+
// }
1589+
// wg.Done()
1590+
// }(stakings)
1591+
1592+
// wg.Wait()
15871593

15881594
evmAccs, err := e.ListAllWalletAddresses()
15891595
if err != nil {
1590-
e.log.Error(err, "[GetNekoHolders] ListAllWalletAddresses() failed")
1596+
e.log.Error(err, "[FetchHolders] ListAllWalletAddresses() failed")
15911597
return nil, err
15921598
}
15931599

@@ -1617,3 +1623,7 @@ func (e *Entity) ExistedNekoHolder(colId, addr string) bool {
16171623
existed, _ := e.repo.UserNFTBalance.IsExists(colId, addr)
16181624
return existed
16191625
}
1626+
1627+
func (e *Entity) GetPodTownNFTBalances(collectionAddresses []string) ([]model.PodTownUserNFTBalance, error) {
1628+
return e.repo.UserNFTBalance.GetPodTownUserNFTBalances(collectionAddresses)
1629+
}

pkg/handler/nft/interface.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,5 @@ type IHandler interface {
3131
GetNftTokenTickers(c *gin.Context)
3232
GetNftSalesHandler(c *gin.Context)
3333
GetProfileNFTBalances(c *gin.Context)
34+
GetPodTownNFTBalances(c *gin.Context)
3435
}

pkg/handler/nft/nft.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,3 +644,21 @@ func (h *Handler) GetProfileNFTBalances(c *gin.Context) {
644644

645645
c.JSON(http.StatusOK, response.CreateResponse(data, nil, nil, nil))
646646
}
647+
648+
func (h *Handler) GetPodTownNFTBalances(c *gin.Context) {
649+
req := &request.GetPodTownNFTBalancesRequest{}
650+
if err := req.Bind(c); err != nil {
651+
h.log.Info("[handler.GetPodTownNFTBalances] Bind() failed")
652+
c.JSON(http.StatusBadRequest, response.CreateResponse[any](nil, nil, err, nil))
653+
return
654+
}
655+
656+
data, err := h.entities.GetPodTownNFTBalances(req.CollectionAddresses)
657+
if err != nil {
658+
h.log.Error(err, "[handler.GetPodTownNFTBalances] entity.GetPodTownNFTBalances() failed")
659+
c.JSON(http.StatusInternalServerError, response.CreateResponse[any](nil, nil, err, nil))
660+
return
661+
}
662+
663+
c.JSON(http.StatusOK, response.CreateResponse(data, nil, nil, nil))
664+
}

pkg/job/update_user_nft_balance.go

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package job
22

33
import (
4-
"strings"
5-
64
"github.com/defipod/mochi/pkg/entities"
75
"github.com/defipod/mochi/pkg/logger"
8-
"github.com/defipod/mochi/pkg/model"
96
sliceutils "github.com/defipod/mochi/pkg/util/slice"
107
)
118

@@ -84,27 +81,35 @@ func (c *updateUserNFTBalances) Run() error {
8481
return err
8582
}
8683

87-
nekoAddr := "0x7aCeE5D0acC520faB33b3Ea25D4FEEF1FfebDE73"
88-
nekoCol := sliceutils.Find(collections, func(c model.NFTCollection) bool {
89-
return strings.EqualFold(nekoAddr, c.Address)
90-
})
84+
// collectionAddresses := ["0x7aCeE5D0acC520faB33b3Ea25D4FEEF1FfebDE73", "0x7aCeE5D0acC520faB33b3Ea25D4FEEF1FfebDE73", ]
85+
// nekoAddr :=
86+
// nekoCol := sliceutils.Find(collections, func(c model.NFTCollection) bool {
87+
// return strings.EqualFold(nekoAddr, c.Address)
88+
// })
9189

92-
if nekoCol == nil {
93-
c.log.Info("Neko Collection not found")
94-
return nil
95-
}
90+
// if nekoCol == nil {
91+
// c.log.Info("Neko Collection not found")
92+
// return nil
93+
// }
9694

97-
data, err := c.entity.GetNekoHolders(*nekoCol)
98-
if err != nil {
99-
c.log.Errorf(err, "entity.GetNekoHolders() failed")
100-
return err
101-
}
95+
for _, col := range collections {
96+
fetched := []string{"0x7aCeE5D0acC520faB33b3Ea25D4FEEF1FfebDE73"}
97+
if sliceutils.Contains(fetched, col.Address) {
98+
continue
99+
}
102100

103-
for _, bal := range data {
104-
err = c.entity.NewUserNFTBalance(bal)
101+
data, err := c.entity.FetchHolders(col)
105102
if err != nil {
106-
c.log.Errorf(err, "NewUserNFTBalance() failed")
107-
continue
103+
c.log.Errorf(err, "entity.FetchHolders() failed")
104+
return err
105+
}
106+
107+
for _, bal := range data {
108+
err = c.entity.NewUserNFTBalance(bal)
109+
if err != nil {
110+
c.log.Errorf(err, "NewUserNFTBalance() failed")
111+
continue
112+
}
108113
}
109114
}
110115

pkg/model/user_nft_balance.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,13 @@ type UserNFTBalanceIdentify struct {
5757
UserDiscordId string `json:"user_discord_id"`
5858
NftCollectionId string `json:"nft_collection_id"`
5959
}
60+
61+
type PodTownUserNFTBalance struct {
62+
ProfileId string `json:"profile_id"`
63+
DiscordId string `json:"discord_id"`
64+
UserAddress string `json:"user_address"`
65+
Neko int `json:"neko"`
66+
Rabby int `json:"rabby"`
67+
Fukuro int `json:"fukuro"`
68+
Gm int `json:"gm"`
69+
}

pkg/repo/user_nft_balance/pg.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"gorm.io/gorm"
55
"gorm.io/gorm/clause"
66

7+
"github.com/defipod/mochi/pkg/consts"
78
"github.com/defipod/mochi/pkg/model"
89
)
910

@@ -119,3 +120,24 @@ func (pg *pg) IsExists(collectionID, userAddress string) (bool, error) {
119120
Count(&count).Error
120121
return count > 0, err
121122
}
123+
124+
func (pg *pg) GetPodTownUserNFTBalances(collectionAddresses []string) ([]model.PodTownUserNFTBalance, error) {
125+
podTownGuildId := "882287783169896468"
126+
var data []model.PodTownUserNFTBalance
127+
return data, pg.db.Table("user_nft_balances b").
128+
Select(`
129+
b.user_address,
130+
b.profile_id,
131+
sum(b.balance + staking_nekos) filter (where c.symbol ilike 'NEKO') as neko,
132+
sum(b.balance) filter (where c.symbol ilike 'RABBY') as rabby,
133+
sum(b.balance) filter (where c.symbol ilike 'FUKURO') as fukuro,
134+
max(aa.platform_identifier) discord_id,
135+
max(ugs.total_count) as gm
136+
`).
137+
Joins("LEFT JOIN nft_collections c ON b.nft_collection_id = c.id").
138+
Joins("LEFT JOIN associated_accounts aa ON b.profile_id = aa.profile_id and aa.platform = ?", consts.PlatformDiscord).
139+
Joins("LEFT JOIN discord_user_gm_streaks ugs ON aa.platform_identifier = ugs.discord_id AND ugs.guild_id = ?", podTownGuildId).
140+
Where("c.address IN ?", collectionAddresses).
141+
Group("b.user_address, b.profile_id").
142+
Find(&data).Error
143+
}

pkg/repo/user_nft_balance/store.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ type Store interface {
88
List(ListQuery) ([]model.UserNFTBalance, error)
99
TotalBalance(collectionID string) (int, error)
1010
IsExists(collectionID, userAddress string) (bool, error)
11+
GetPodTownUserNFTBalances(collectionAddresses []string) ([]model.PodTownUserNFTBalance, error)
1112
}

0 commit comments

Comments
 (0)