Skip to content

Commit a38b34a

Browse files
committed
feat: cache community metadata in wallet
Fixes #12521
1 parent dca38d1 commit a38b34a

16 files changed

+577
-255
lines changed

node/status_node_services.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (b *StatusNode) initServices(config *params.NodeConfig, mediaServer *server
106106
// Community collectibles.
107107
// Messenger needs the CollectiblesManager to get the list of collectibles owned
108108
// by a certain account and check community entry permissions.
109-
// We handle circular dependency between the two by delaying ininitalization of the CollectibleMetadataProvider
109+
// We handle circular dependency between the two by delaying ininitalization of the CommunityCollectibleInfoProvider
110110
// in the CollectiblesManager.
111111
if config.WakuConfig.Enabled {
112112
wakuService, err := b.wakuService(&config.WakuConfig, &config.ClusterConfig)
@@ -125,7 +125,6 @@ func (b *StatusNode) initServices(config *params.NodeConfig, mediaServer *server
125125

126126
services = append(services, wakuext)
127127

128-
b.SetWalletCollectibleMetadataProvider(wakuext)
129128
b.SetWalletCollectibleCommunityInfoProvider(wakuext)
130129
}
131130

@@ -153,7 +152,6 @@ func (b *StatusNode) initServices(config *params.NodeConfig, mediaServer *server
153152

154153
services = append(services, wakuext)
155154

156-
b.SetWalletCollectibleMetadataProvider(wakuext)
157155
b.SetWalletCollectibleCommunityInfoProvider(wakuext)
158156
}
159157

@@ -514,12 +512,6 @@ func (b *StatusNode) WalletService() *wallet.Service {
514512
return b.walletSrvc
515513
}
516514

517-
func (b *StatusNode) SetWalletCollectibleMetadataProvider(provider thirdparty.CollectibleMetadataProvider) {
518-
if b.walletSrvc != nil {
519-
b.walletSrvc.SetCollectibleMetadataProvider(provider)
520-
}
521-
}
522-
523515
func (b *StatusNode) SetWalletCollectibleCommunityInfoProvider(provider thirdparty.CollectibleCommunityInfoProvider) {
524516
if b.walletSrvc != nil {
525517
b.walletSrvc.SetCollectibleCommunityInfoProvider(provider)

services/ext/service.go

Lines changed: 50 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -555,64 +555,80 @@ func tokenURIToCommunityID(tokenURI string) string {
555555
return communityID
556556
}
557557

558-
func (s *Service) CanProvideCollectibleMetadata(id thirdparty.CollectibleUniqueID, tokenURI string) (bool, error) {
559-
ret := tokenURI != "" && tokenURIToCommunityID(tokenURI) != ""
560-
return ret, nil
558+
func (s *Service) GetCommunityID(tokenURI string) string {
559+
if tokenURI != "" {
560+
return tokenURIToCommunityID(tokenURI)
561+
}
562+
return ""
561563
}
562564

563-
func (s *Service) FetchCollectibleMetadata(id thirdparty.CollectibleUniqueID, tokenURI string) (*thirdparty.FullCollectibleData, error) {
565+
func (s *Service) FillCollectibleMetadata(collectible *thirdparty.FullCollectibleData) error {
564566
if s.messenger == nil {
565-
return nil, fmt.Errorf("messenger not ready")
567+
return fmt.Errorf("messenger not ready")
566568
}
567569

568-
communityID := tokenURIToCommunityID(tokenURI)
570+
if collectible == nil {
571+
return fmt.Errorf("empty collectible")
572+
}
573+
574+
id := collectible.CollectibleData.ID
575+
communityID := collectible.CollectibleData.CommunityID
569576

570577
if communityID == "" {
571-
return nil, fmt.Errorf("invalid tokenURI")
578+
return fmt.Errorf("invalid communityID")
572579
}
573580

574581
community, err := s.fetchCommunity(communityID)
575582

576583
if err != nil {
577-
return nil, err
584+
return err
578585
}
579586

580587
if community == nil {
581-
return nil, nil
588+
return nil
582589
}
583590

584591
tokenMetadata, err := s.fetchCommunityCollectibleMetadata(community, id.ContractID)
585592

586593
if err != nil {
587-
return nil, err
594+
return err
588595
}
589596

590597
if tokenMetadata == nil {
591-
return nil, nil
598+
return nil
592599
}
593600

594-
token, err := s.fetchCommunityToken(communityID, id.ContractID)
601+
communityToken, err := s.fetchCommunityToken(communityID, id.ContractID)
595602
if err != nil {
596-
return nil, err
603+
return err
597604
}
598605

599-
return &thirdparty.FullCollectibleData{
600-
CollectibleData: thirdparty.CollectibleData{
601-
ID: id,
602-
CommunityID: communityID,
603-
Name: tokenMetadata.GetName(),
604-
Description: tokenMetadata.GetDescription(),
605-
ImageURL: tokenMetadata.GetImage(),
606-
TokenURI: tokenURI,
607-
Traits: getCollectibleCommunityTraits(token),
608-
},
609-
CollectionData: &thirdparty.CollectionData{
606+
permission := fetchCommunityCollectiblePermission(community, id)
607+
608+
privilegesLevel := token.CommunityLevel
609+
if permission != nil {
610+
privilegesLevel = permissionTypeToPrivilegesLevel(permission.GetType())
611+
}
612+
613+
collectible.CollectibleData.Name = tokenMetadata.GetName()
614+
collectible.CollectibleData.Description = tokenMetadata.GetDescription()
615+
collectible.CollectibleData.ImageURL = tokenMetadata.GetImage()
616+
collectible.CollectibleData.Traits = getCollectibleCommunityTraits(communityToken)
617+
618+
if collectible.CollectionData == nil {
619+
collectible.CollectionData = &thirdparty.CollectionData{
610620
ID: id.ContractID,
611621
CommunityID: communityID,
612-
Name: tokenMetadata.GetName(),
613-
ImageURL: tokenMetadata.GetImage(),
614-
},
615-
}, nil
622+
}
623+
}
624+
collectible.CollectionData.Name = tokenMetadata.GetName()
625+
collectible.CollectionData.ImageURL = tokenMetadata.GetImage()
626+
627+
collectible.CommunityInfo = &thirdparty.CollectibleCommunityInfo{
628+
PrivilegesLevel: privilegesLevel,
629+
}
630+
631+
return nil
616632
}
617633

618634
func permissionTypeToPrivilegesLevel(permissionType protobuf.CommunityTokenPermission_Type) token.PrivilegesLevel {
@@ -626,7 +642,7 @@ func permissionTypeToPrivilegesLevel(permissionType protobuf.CommunityTokenPermi
626642
}
627643
}
628644

629-
func (s *Service) FetchCollectibleCommunityInfo(communityID string, id thirdparty.CollectibleUniqueID) (*thirdparty.CollectiblesCommunityInfo, error) {
645+
func (s *Service) FetchCommunityInfo(communityID string) (*thirdparty.CommunityInfo, error) {
630646
community, err := s.fetchCommunity(communityID)
631647
if err != nil {
632648
return nil, err
@@ -635,37 +651,13 @@ func (s *Service) FetchCollectibleCommunityInfo(communityID string, id thirdpart
635651
return nil, nil
636652
}
637653

638-
metadata, err := s.fetchCommunityCollectibleMetadata(community, id.ContractID)
639-
if err != nil {
640-
return nil, err
641-
}
642-
if metadata == nil {
643-
return nil, nil
644-
}
645-
646-
permission := fetchCommunityCollectiblePermission(community, id)
647-
648-
privilegesLevel := token.CommunityLevel
649-
if permission != nil {
650-
privilegesLevel = permissionTypeToPrivilegesLevel(permission.GetType())
651-
}
652-
653-
return &thirdparty.CollectiblesCommunityInfo{
654-
CommunityID: communityID,
655-
CommunityName: community.Name(),
656-
CommunityColor: community.Color(),
657-
CommunityImage: fetchCommunityImage(community),
658-
PrivilegesLevel: privilegesLevel,
659-
}, nil
660-
}
661-
662-
func (s *Service) FetchCollectibleCommunityTraits(communityID string, id thirdparty.CollectibleUniqueID) ([]thirdparty.CollectibleTrait, error) {
663-
token, err := s.fetchCommunityToken(communityID, id.ContractID)
664-
if err != nil {
665-
return nil, err
654+
communityInfo := &thirdparty.CommunityInfo{
655+
CommunityName: community.Name(),
656+
CommunityColor: community.Color(),
657+
CommunityImage: fetchCommunityImage(community),
666658
}
667659

668-
return getCollectibleCommunityTraits(token), nil
660+
return communityInfo, nil
669661
}
670662

671663
func (s *Service) fetchCommunity(communityID string) (*communities.Community, error) {

services/wallet/collectibles/collectible_data_db.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"math/big"
77

8+
"github.com/status-im/status-go/protocol/communities/token"
89
"github.com/status-im/status-go/services/wallet/bigint"
910
"github.com/status-im/status-go/services/wallet/thirdparty"
1011
"github.com/status-im/status-go/sqlite"
@@ -21,6 +22,7 @@ func NewCollectibleDataDB(sqlDb *sql.DB) *CollectibleDataDB {
2122
}
2223

2324
const collectibleDataColumns = "chain_id, contract_address, token_id, provider, name, description, permalink, image_url, animation_url, animation_media_type, background_color, token_uri, community_id"
25+
const collectibleCommunityDataColumns = "community_privileges_level"
2426
const collectibleTraitsColumns = "chain_id, contract_address, token_id, trait_type, trait_value, display_type, max_value"
2527
const selectCollectibleTraitsColumns = "trait_type, trait_value, display_type, max_value"
2628

@@ -252,3 +254,70 @@ func (o *CollectibleDataDB) GetData(ids []thirdparty.CollectibleUniqueID) (map[s
252254
}
253255
return ret, nil
254256
}
257+
258+
func (o *CollectibleDataDB) SetCommunityInfo(id thirdparty.CollectibleUniqueID, communityInfo thirdparty.CollectibleCommunityInfo) (err error) {
259+
tx, err := o.db.Begin()
260+
if err != nil {
261+
return err
262+
}
263+
defer func() {
264+
if err == nil {
265+
err = tx.Commit()
266+
return
267+
}
268+
_ = tx.Rollback()
269+
}()
270+
271+
update, err := tx.Prepare(`UPDATE collectible_data_cache
272+
SET community_privileges_level=?
273+
WHERE chain_id=? AND contract_address=? AND token_id=?`)
274+
if err != nil {
275+
return err
276+
}
277+
278+
_, err = update.Exec(
279+
communityInfo.PrivilegesLevel,
280+
id.ContractID.ChainID,
281+
id.ContractID.Address,
282+
(*bigint.SQLBigIntBytes)(id.TokenID.Int),
283+
)
284+
285+
return err
286+
}
287+
288+
func (o *CollectibleDataDB) GetCommunityInfo(id thirdparty.CollectibleUniqueID) (*thirdparty.CollectibleCommunityInfo, error) {
289+
ret := thirdparty.CollectibleCommunityInfo{
290+
PrivilegesLevel: token.CommunityLevel,
291+
}
292+
293+
getData, err := o.db.Prepare(fmt.Sprintf(`SELECT %s
294+
FROM collectible_data_cache
295+
WHERE chain_id=? AND contract_address=? AND token_id=?`, collectibleCommunityDataColumns))
296+
if err != nil {
297+
return nil, err
298+
}
299+
300+
row := getData.QueryRow(
301+
id.ContractID.ChainID,
302+
id.ContractID.Address,
303+
(*bigint.SQLBigIntBytes)(id.TokenID.Int),
304+
)
305+
306+
var dbPrivilegesLevel sql.NullByte
307+
308+
err = row.Scan(
309+
&dbPrivilegesLevel,
310+
)
311+
312+
if err == sql.ErrNoRows {
313+
return nil, nil
314+
} else if err != nil {
315+
return nil, err
316+
}
317+
318+
if dbPrivilegesLevel.Valid {
319+
ret.PrivilegesLevel = token.PrivilegesLevel(dbPrivilegesLevel.Byte)
320+
}
321+
322+
return &ret, nil
323+
}

services/wallet/collectibles/collectible_data_db_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/ethereum/go-ethereum/common"
99

10+
"github.com/status-im/status-go/protocol/communities/token"
1011
"github.com/status-im/status-go/services/wallet/bigint"
1112
w_common "github.com/status-im/status-go/services/wallet/common"
1213
"github.com/status-im/status-go/services/wallet/thirdparty"
@@ -72,6 +73,17 @@ func generateTestCollectiblesData(count int) (result []thirdparty.CollectibleDat
7273
return result
7374
}
7475

76+
func generateTestCommunityData(count int) []thirdparty.CollectibleCommunityInfo {
77+
result := make([]thirdparty.CollectibleCommunityInfo, 0, count)
78+
for i := 0; i < count; i++ {
79+
newCommunityInfo := thirdparty.CollectibleCommunityInfo{
80+
PrivilegesLevel: token.PrivilegesLevel(i),
81+
}
82+
result = append(result, newCommunityInfo)
83+
}
84+
return result
85+
}
86+
7587
func TestUpdateCollectiblesData(t *testing.T) {
7688
db, cleanDB := setupCollectibleDataDBTest(t)
7789
defer cleanDB()
@@ -146,3 +158,28 @@ func TestUpdateCollectiblesData(t *testing.T) {
146158
require.Equal(t, c0, loadedMap[c0.ID.HashKey()])
147159
require.Equal(t, c1, loadedMap[c1.ID.HashKey()])
148160
}
161+
162+
func TestUpdateCommunityData(t *testing.T) {
163+
db, cleanDB := setupCollectibleDataDBTest(t)
164+
defer cleanDB()
165+
166+
const nData = 50
167+
data := generateTestCollectiblesData(nData)
168+
communityData := generateTestCommunityData(nData)
169+
170+
var err error
171+
172+
err = db.SetData(data)
173+
require.NoError(t, err)
174+
175+
for i := 0; i < nData; i++ {
176+
err = db.SetCommunityInfo(data[i].ID, communityData[i])
177+
require.NoError(t, err)
178+
}
179+
180+
for i := 0; i < nData; i++ {
181+
loadedCommunityData, err := db.GetCommunityInfo(data[i].ID)
182+
require.NoError(t, err)
183+
require.Equal(t, communityData[i], *loadedCommunityData)
184+
}
185+
}

services/wallet/collectibles/commands.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ func (c *loadOwnedCollectiblesCommand) Run(parent context.Context) (err error) {
214214
break
215215
}
216216

217-
log.Debug("partial loadOwnedCollectiblesCommand", "chain", c.chainID, "account", c.account, "page", pageNr, "in", time.Since(pageStart), "found", len(partialOwnership.Items), "collectibles")
217+
log.Debug("partial loadOwnedCollectiblesCommand", "chain", c.chainID, "account", c.account, "page", pageNr, "in", time.Since(pageStart), "found", len(partialOwnership.Items))
218218

219219
c.partialOwnership = append(c.partialOwnership, partialOwnership.Items...)
220220

services/wallet/collectibles/controller.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/status-im/status-go/services/accounts/settingsevent"
1919
"github.com/status-im/status-go/services/wallet/async"
2020
walletCommon "github.com/status-im/status-go/services/wallet/common"
21+
"github.com/status-im/status-go/services/wallet/community"
2122
"github.com/status-im/status-go/services/wallet/transfer"
2223
"github.com/status-im/status-go/services/wallet/walletevent"
2324
)
@@ -35,6 +36,7 @@ type timerPerAddressAndChainID = map[common.Address]timerPerChainID
3536
type Controller struct {
3637
manager *Manager
3738
ownershipDB *OwnershipDB
39+
communityDB *community.DataDB
3840
walletFeed *event.Feed
3941
accountsDB *accounts.Database
4042
accountsFeed *event.Feed
@@ -64,6 +66,7 @@ func NewController(
6466
return &Controller{
6567
manager: manager,
6668
ownershipDB: NewOwnershipDB(db),
69+
communityDB: community.NewDataDB(db),
6770
walletFeed: walletFeed,
6871
accountsDB: accountsDB,
6972
accountsFeed: accountsFeed,
@@ -412,7 +415,8 @@ func (c *Controller) notifyCommunityCollectiblesReceived(ownedCollectibles Owned
412415
continue
413416
}
414417

415-
communityInfo, err := c.manager.FetchCollectibleCommunityInfo(communityID, collectibleID)
418+
communityInfo, err := c.communityDB.GetCommunityInfo(communityID)
419+
416420
if err != nil {
417421
log.Error("Error fetching community info", "error", err)
418422
continue
@@ -421,7 +425,7 @@ func (c *Controller) notifyCommunityCollectiblesReceived(ownedCollectibles Owned
421425
header := CommunityCollectibleHeader{
422426
ID: collectibleID,
423427
Name: collectibleData.CollectibleData.Name,
424-
CommunityHeader: communityInfoToHeader(*communityInfo),
428+
CommunityHeader: communityInfoToHeader(communityID, communityInfo, collectibleData.CommunityInfo),
425429
}
426430

427431
communityCollectibles = append(communityCollectibles, header)

0 commit comments

Comments
 (0)