From dac7a0daf906f2963bb57bbe79f57671ba90f12e Mon Sep 17 00:00:00 2001 From: Cuteivist Date: Fri, 22 Dec 2023 10:43:19 +0100 Subject: [PATCH] feat: Fetch community data for tokens (#4497) --- protocol/messenger.go | 2 +- services/wallet/community/manager.go | 57 ++++++++++++++++++- services/wallet/reader.go | 17 +++++- services/wallet/service.go | 4 +- services/wallet/token/token.go | 15 ++--- services/wallet/token/token_test.go | 3 +- .../transfer/commands_sequential_test.go | 10 ++-- 7 files changed, 84 insertions(+), 24 deletions(-) diff --git a/protocol/messenger.go b/protocol/messenger.go index ef8791fda..ab0d25957 100644 --- a/protocol/messenger.go +++ b/protocol/messenger.go @@ -455,7 +455,7 @@ func NewMessenger( if c.tokenManager != nil { managerOptions = append(managerOptions, communities.WithTokenManager(c.tokenManager)) } else if c.rpcClient != nil { - tokenManager := token.NewTokenManager(c.walletDb, c.rpcClient, community.NewManager(database, c.httpServer), c.rpcClient.NetworkManager, database, c.httpServer) + tokenManager := token.NewTokenManager(c.walletDb, c.rpcClient, community.NewManager(database, c.httpServer, nil), c.rpcClient.NetworkManager, database, c.httpServer) managerOptions = append(managerOptions, communities.WithTokenManager(communities.NewDefaultTokenManager(tokenManager))) } diff --git a/services/wallet/community/manager.go b/services/wallet/community/manager.go index b7147768d..9444955b8 100644 --- a/services/wallet/community/manager.go +++ b/services/wallet/community/manager.go @@ -2,22 +2,39 @@ package community import ( "database/sql" + "encoding/json" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/log" "github.com/status-im/status-go/server" "github.com/status-im/status-go/services/wallet/thirdparty" + "github.com/status-im/status-go/services/wallet/walletevent" +) + +// These events are used to notify the UI of state changes +const ( + EventCommmunityDataUpdated walletevent.EventType = "wallet-community-data-updated" ) type Manager struct { db *DataDB communityInfoProvider thirdparty.CommunityInfoProvider mediaServer *server.MediaServer + feed *event.Feed } -func NewManager(db *sql.DB, mediaServer *server.MediaServer) *Manager { +type Data struct { + ID string `json:"id"` + Name string `json:"name"` + Color string `json:"color"` + Image string `json:"image,omitempty"` +} + +func NewManager(db *sql.DB, mediaServer *server.MediaServer, feed *event.Feed) *Manager { return &Manager{ db: NewDataDB(db), mediaServer: mediaServer, + feed: feed, } } @@ -58,7 +75,43 @@ func (cm *Manager) FetchCommunityInfo(communityID string) (*thirdparty.Community } return nil, err } - err = cm.setCommunityInfo(communityID, communityInfo) return communityInfo, err } + +func (cm *Manager) FetchCommunityMetadataAsync(communityID string) { + go func() { + communityInfo, err := cm.FetchCommunityInfo(communityID) + if err != nil { + log.Error("FetchCommunityInfo failed", "communityID", communityID, "err", err) + return + } + + cm.signalUpdatedCommunityMetadata(communityID, communityInfo) + }() +} + +func (cm *Manager) signalUpdatedCommunityMetadata(communityID string, communityInfo *thirdparty.CommunityInfo) { + if communityInfo == nil { + return + } + data := Data{ + ID: communityID, + Name: communityInfo.CommunityName, + Color: communityInfo.CommunityColor, + Image: communityInfo.CommunityImage, // TODO make media server url after merging community token media server changes + } + + payload, err := json.Marshal(data) + if err != nil { + log.Error("Error marshaling response: %v", err) + return + } + + event := walletevent.Event{ + Type: EventCommmunityDataUpdated, + Message: string(payload), + } + + cm.feed.Send(event) +} diff --git a/services/wallet/reader.go b/services/wallet/reader.go index 2233504b8..d4cdea348 100644 --- a/services/wallet/reader.go +++ b/services/wallet/reader.go @@ -14,6 +14,7 @@ import ( "github.com/status-im/status-go/params" "github.com/status-im/status-go/rpc" "github.com/status-im/status-go/services/wallet/async" + "github.com/status-im/status-go/services/wallet/community" "github.com/status-im/status-go/services/wallet/market" "github.com/status-im/status-go/services/wallet/thirdparty" "github.com/status-im/status-go/services/wallet/transfer" @@ -46,11 +47,12 @@ func belongsToMandatoryTokens(symbol string) bool { return false } -func NewReader(rpcClient *rpc.Client, tokenManager *token.Manager, marketManager *market.Manager, accountsDB *accounts.Database, persistence *Persistence, walletFeed *event.Feed) *Reader { +func NewReader(rpcClient *rpc.Client, tokenManager *token.Manager, marketManager *market.Manager, communityManager *community.Manager, accountsDB *accounts.Database, persistence *Persistence, walletFeed *event.Feed) *Reader { return &Reader{ rpcClient: rpcClient, tokenManager: tokenManager, marketManager: marketManager, + communityManager: communityManager, accountsDB: accountsDB, persistence: persistence, walletFeed: walletFeed, @@ -62,6 +64,7 @@ type Reader struct { rpcClient *rpc.Client tokenManager *token.Manager marketManager *market.Manager + communityManager *community.Manager accountsDB *accounts.Database persistence *Persistence walletFeed *event.Feed @@ -103,7 +106,7 @@ type Token struct { PegSymbol string `json:"pegSymbol"` Verified bool `json:"verified"` Image string `json:"image,omitempty"` - CommunityData *token.CommunityData `json:"community_data,omitempty"` + CommunityData *community.Data `json:"community_data,omitempty"` } func splitVerifiedTokens(tokens []*token.Token) ([]*token.Token, []*token.Token) { @@ -389,6 +392,8 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address) return nil, err } + communities := make(map[string]bool) + for address, tokens := range result { for index, token := range tokens { marketValuesPerCurrency := make(map[string]TokenMarketValues) @@ -409,6 +414,10 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address) } } + if token.CommunityData != nil { + communities[token.CommunityData.ID] = true + } + if _, ok := tokenDetails[token.Symbol]; !ok { continue } @@ -422,6 +431,10 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address) r.lastWalletTokenUpdateTimestamp.Store(time.Now().Unix()) + for communityID := range communities { + r.communityManager.FetchCommunityMetadataAsync(communityID) + } + return result, r.persistence.SaveTokens(result) } diff --git a/services/wallet/service.go b/services/wallet/service.go index 03ad06bbc..582e964dc 100644 --- a/services/wallet/service.go +++ b/services/wallet/service.go @@ -99,7 +99,7 @@ func NewService( }) }) - communityManager := community.NewManager(db, mediaServer) + communityManager := community.NewManager(db, mediaServer, feed) balanceCacher := balance.NewCacherWithTTL(5 * time.Minute) tokenManager := token.NewTokenManager(db, rpcClient, communityManager, rpcClient.NetworkManager, appDB, mediaServer) savedAddressesManager := &SavedAddressesManager{db: db} @@ -110,7 +110,7 @@ func NewService( cryptoCompare := cryptocompare.NewClient() coingecko := coingecko.NewClient() marketManager := market.NewManager(cryptoCompare, coingecko, feed) - reader := NewReader(rpcClient, tokenManager, marketManager, accountsDB, NewPersistence(db), feed) + reader := NewReader(rpcClient, tokenManager, marketManager, communityManager, accountsDB, NewPersistence(db), feed) history := history.NewService(db, accountsDB, feed, rpcClient, tokenManager, marketManager, balanceCacher.Cache()) currency := currency.NewService(db, feed, tokenManager, marketManager) blockChainState := NewBlockChainState(rpcClient, accountsDB) diff --git a/services/wallet/token/token.go b/services/wallet/token/token.go index 1d31c23d5..b325ca769 100644 --- a/services/wallet/token/token.go +++ b/services/wallet/token/token.go @@ -49,16 +49,9 @@ type Token struct { PegSymbol string `json:"pegSymbol"` Image string `json:"image,omitempty"` - CommunityData *CommunityData `json:"community_data,omitempty"` - Verified bool `json:"verified"` - TokenListID string `json:"tokenListId"` -} - -type CommunityData struct { - ID string `json:"id"` - Name string `json:"name"` - Color string `json:"color"` - Image string `json:"image,omitempty"` + CommunityData *community.Data `json:"community_data,omitempty"` + Verified bool `json:"verified"` + TokenListID string `json:"tokenListId"` } func (t *Token) IsNative() bool { @@ -551,7 +544,7 @@ func (tm *Manager) getTokensFromDB(query string, args ...any) ([]*Token, error) break } - token.CommunityData = &CommunityData{ + token.CommunityData = &community.Data{ ID: communityID, } } diff --git a/services/wallet/token/token_test.go b/services/wallet/token/token_test.go index 70f348242..668a815e3 100644 --- a/services/wallet/token/token_test.go +++ b/services/wallet/token/token_test.go @@ -8,6 +8,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/status-im/status-go/params" + "github.com/status-im/status-go/services/wallet/community" "github.com/status-im/status-go/t/helpers" "github.com/status-im/status-go/walletdatabase" ) @@ -97,7 +98,7 @@ func TestCommunityTokens(t *testing.T) { Symbol: "COM", Decimals: 12, ChainID: 777, - CommunityData: &CommunityData{ + CommunityData: &community.Data{ ID: "random_community_id", }, } diff --git a/services/wallet/transfer/commands_sequential_test.go b/services/wallet/transfer/commands_sequential_test.go index d2520dd8c..ac808b5fe 100644 --- a/services/wallet/transfer/commands_sequential_test.go +++ b/services/wallet/transfer/commands_sequential_test.go @@ -937,7 +937,8 @@ func TestFindBlocksCommand(t *testing.T) { } client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db) client.SetClient(tc.NetworkID(), tc) - tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil), network.NewManager(appdb), appdb, mediaServer) + tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil, nil), network.NewManager(appdb), appdb, mediaServer) + tokenManager.SetTokens([]*token.Token{ { Address: tokenTXXAddress, @@ -1062,7 +1063,7 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) { client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db) client.SetClient(tc.NetworkID(), tc) - tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil), network.NewManager(appdb), appdb, mediaServer) + tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil, nil), network.NewManager(appdb), appdb, mediaServer) tokenManager.SetTokens([]*token.Token{ { @@ -1182,7 +1183,7 @@ func TestFetchNewBlocksCommand_findBlocksWithEthTransfers(t *testing.T) { client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db) client.SetClient(tc.NetworkID(), tc) - tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil), network.NewManager(appdb), appdb, mediaServer) + tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil, nil), network.NewManager(appdb), appdb, mediaServer) tokenManager.SetTokens([]*token.Token{ { @@ -1259,8 +1260,7 @@ func TestFetchNewBlocksCommand(t *testing.T) { client, _ := statusRpc.NewClient(nil, 1, params.UpstreamRPCConfig{Enabled: false, URL: ""}, []params.Network{}, db) client.SetClient(tc.NetworkID(), tc) - - tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil), network.NewManager(appdb), appdb, mediaServer) + tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil, nil), network.NewManager(appdb), appdb, mediaServer) tokenManager.SetTokens([]*token.Token{ {