diff --git a/protocol/messenger.go b/protocol/messenger.go index a2df64085..ef8791fda 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) + tokenManager := token.NewTokenManager(c.walletDb, c.rpcClient, community.NewManager(database, c.httpServer), c.rpcClient.NetworkManager, database, c.httpServer) managerOptions = append(managerOptions, communities.WithTokenManager(communities.NewDefaultTokenManager(tokenManager))) } diff --git a/server/handlers.go b/server/handlers.go index 3a8e4e1ab..190ad3e50 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -35,6 +35,7 @@ const ( discordAttachmentsPath = basePath + "/discord/attachments" LinkPreviewThumbnailPath = "/link-preview/thumbnail" StatusLinkPreviewThumbnailPath = "/status-link-preview/thumbnail" + communityTokenImagesPath = "/communityTokenImages" walletBasePath = "/wallet" walletCommunityImagesPath = walletBasePath + "/communityImages" @@ -930,6 +931,63 @@ func handleQRCodeGeneration(multiaccountsDB *multiaccounts.Database, logger *zap } } +func handleCommunityTokenImages(db *sql.DB, logger *zap.Logger) http.HandlerFunc { + if db == nil { + return handleRequestDBMissing(logger) + } + + return func(w http.ResponseWriter, r *http.Request) { + params := r.URL.Query() + + if len(params["communityID"]) == 0 { + logger.Error("no communityID") + return + } + if len(params["chainID"]) == 0 { + logger.Error("no chainID") + return + } + if len(params["symbol"]) == 0 { + logger.Error("no symbol") + return + } + + chainID, err := strconv.ParseUint(params["chainID"][0], 10, 64) + if err != nil { + logger.Error("invalid chainID in community token image", zap.Error(err)) + return + } + + var base64Image string + err = db.QueryRow("SELECT image_base64 FROM community_tokens WHERE community_id = ? AND chain_id = ? AND symbol = ?", params["communityID"][0], chainID, params["symbol"][0]).Scan(&base64Image) + if err != nil { + logger.Error("failed to find community token image", zap.Error(err)) + return + } + if len(base64Image) == 0 { + logger.Error("empty community token image") + return + } + imagePayload, err := images.GetPayloadFromURI(base64Image) + if err != nil { + logger.Error("failed to get community token image payload", zap.Error(err)) + return + } + mime, err := images.GetProtobufImageMime(imagePayload) + if err != nil { + logger.Error("failed to get community token image mime", zap.Error(err)) + } + + w.Header().Set("Content-Type", mime) + w.Header().Set("Cache-Control", "no-store") + + _, err = w.Write(imagePayload) + if err != nil { + logger.Error("failed to write community token image", zap.Error(err)) + } + } +} + func handleWalletCommunityImages(db *sql.DB, logger *zap.Logger) http.HandlerFunc { if db == nil { return handleRequestDBMissing(logger) diff --git a/server/server_media.go b/server/server_media.go index 44456ccd5..7570ea9bd 100644 --- a/server/server_media.go +++ b/server/server_media.go @@ -3,6 +3,7 @@ package server import ( "database/sql" "net/url" + "strconv" "github.com/status-im/status-go/ipfs" "github.com/status-im/status-go/logutils" @@ -52,6 +53,7 @@ func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader, multiaccountsDB *mu ipfsPath: handleIPFS(s.downloader, s.logger), LinkPreviewThumbnailPath: handleLinkPreviewThumbnail(s.db, s.logger), StatusLinkPreviewThumbnailPath: handleStatusLinkPreviewThumbnail(s.db, s.logger), + communityTokenImagesPath: handleCommunityTokenImages(s.db, s.logger), walletCommunityImagesPath: handleWalletCommunityImages(s.walletDB, s.logger), walletCollectionImagesPath: handleWalletCollectionImages(s.walletDB, s.logger), walletCollectibleImagesPath: handleWalletCollectibleImages(s.walletDB, s.logger), @@ -146,6 +148,18 @@ func (s *MediaServer) MakeContactImageURL(publicKey string, imageType string) st return u.String() } +func (s *MediaServer) MakeCommunityTokenImagesURL(communityID string, chainID uint64, symbol string) string { + u := s.MakeBaseURL() + u.Path = communityTokenImagesPath + u.RawQuery = url.Values{ + "communityID": {communityID}, + "chainID": {strconv.FormatUint(chainID, 10)}, + "symbol": {symbol}, + }.Encode() + + return u.String() +} + func (s *MediaServer) MakeWalletCommunityImagesURL(communityID string) string { u := s.MakeBaseURL() u.Path = walletCommunityImagesPath diff --git a/services/communitytokens/database.go b/services/communitytokens/database.go index 2492f6b46..72771f9e1 100644 --- a/services/communitytokens/database.go +++ b/services/communitytokens/database.go @@ -47,7 +47,7 @@ func (db *Database) GetTokenPrivilegesLevel(chainID uint64, contractAddress stri } func (db *Database) GetCommunityERC20Metadata() ([]*token.CommunityToken, error) { - rows, err := db.db.Query(`SELECT community_id, address, name, symbol, chain_id, image_base64 FROM community_tokens WHERE type = ?`, protobuf.CommunityTokenType_ERC20) + rows, err := db.db.Query(`SELECT community_id, address, name, symbol, chain_id FROM community_tokens WHERE type = ?`, protobuf.CommunityTokenType_ERC20) if err != nil { return nil, err } @@ -56,7 +56,7 @@ func (db *Database) GetCommunityERC20Metadata() ([]*token.CommunityToken, error) var result []*token.CommunityToken for rows.Next() { token := token.CommunityToken{} - err := rows.Scan(&token.CommunityID, &token.Address, &token.Name, &token.Symbol, &token.ChainID, &token.Base64Image) + err := rows.Scan(&token.CommunityID, &token.Address, &token.Name, &token.Symbol, &token.ChainID) if err != nil { return nil, err } diff --git a/services/wallet/service.go b/services/wallet/service.go index 826d56413..03ad06bbc 100644 --- a/services/wallet/service.go +++ b/services/wallet/service.go @@ -101,7 +101,7 @@ func NewService( communityManager := community.NewManager(db, mediaServer) balanceCacher := balance.NewCacherWithTTL(5 * time.Minute) - tokenManager := token.NewTokenManager(db, rpcClient, communityManager, rpcClient.NetworkManager, appDB) + tokenManager := token.NewTokenManager(db, rpcClient, communityManager, rpcClient.NetworkManager, appDB, mediaServer) savedAddressesManager := &SavedAddressesManager{db: db} transactionManager := transfer.NewTransactionManager(db, gethManager, transactor, config, accountsDB, pendingTxManager, feed) transferController := transfer.NewTransferController(db, accountsDB, rpcClient, accountFeed, feed, transactionManager, pendingTxManager, diff --git a/services/wallet/token/token.go b/services/wallet/token/token.go index ebc1d445b..1d31c23d5 100644 --- a/services/wallet/token/token.go +++ b/services/wallet/token/token.go @@ -24,6 +24,7 @@ import ( "github.com/status-im/status-go/rpc" "github.com/status-im/status-go/rpc/chain" "github.com/status-im/status-go/rpc/network" + "github.com/status-im/status-go/server" "github.com/status-im/status-go/services/communitytokens" "github.com/status-im/status-go/services/utils" "github.com/status-im/status-go/services/wallet/async" @@ -89,6 +90,7 @@ type Manager struct { stores []store // Set on init, not changed afterwards communityTokensDB *communitytokens.Database communityManager *community.Manager + mediaServer *server.MediaServer tokens []*Token @@ -116,6 +118,7 @@ func NewTokenManager( communityManager *community.Manager, networkManager *network.Manager, appDB *sql.DB, + mediaServer *server.MediaServer, ) *Manager { maker, _ := contracts.NewContractMaker(RPCClient) stores := []store{newUniswapStore(), newDefaultStore()} @@ -151,6 +154,7 @@ func NewTokenManager( stores: stores, communityTokensDB: communitytokens.NewCommunityTokensDatabase(appDB), tokens: tokens, + mediaServer: mediaServer, } } @@ -516,7 +520,6 @@ func (tm *Manager) DiscoverToken(ctx context.Context, chainID uint64, address co } func (tm *Manager) getTokensFromDB(query string, args ...any) ([]*Token, error) { - communityTokens := []*token.CommunityToken{} if tm.communityTokensDB != nil { // Error is skipped because it's only returning optional metadata @@ -544,7 +547,7 @@ func (tm *Manager) getTokensFromDB(query string, args ...any) ([]*Token, error) if communityToken.CommunityID != communityID || uint64(communityToken.ChainID) != token.ChainID || communityToken.Symbol != token.Symbol { continue } - token.Image = communityToken.Base64Image + token.Image = tm.mediaServer.MakeCommunityTokenImagesURL(communityID, token.ChainID, token.Symbol) break } diff --git a/services/wallet/transfer/commands_sequential_test.go b/services/wallet/transfer/commands_sequential_test.go index 0023eb22c..d2520dd8c 100644 --- a/services/wallet/transfer/commands_sequential_test.go +++ b/services/wallet/transfer/commands_sequential_test.go @@ -27,6 +27,7 @@ import ( "github.com/status-im/status-go/contracts/ethscan" "github.com/status-im/status-go/contracts/ierc20" "github.com/status-im/status-go/rpc/chain" + "github.com/status-im/status-go/server" "github.com/status-im/status-go/services/wallet/async" "github.com/status-im/status-go/services/wallet/balance" "github.com/status-im/status-go/services/wallet/community" @@ -912,6 +913,9 @@ func TestFindBlocksCommand(t *testing.T) { require.NoError(t, err) tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil} + mediaServer, err := server.NewMediaServer(appdb, nil, nil, db) + require.NoError(t, err) + wdb := NewDB(db) tc := &TestClient{ t: t, @@ -933,7 +937,7 @@ 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) + tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil), network.NewManager(appdb), appdb, mediaServer) tokenManager.SetTokens([]*token.Token{ { Address: tokenTXXAddress, @@ -1041,6 +1045,9 @@ func TestFetchTransfersForLoadedBlocks(t *testing.T) { require.NoError(t, err) tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil} + mediaServer, err := server.NewMediaServer(appdb, nil, nil, db) + require.NoError(t, err) + wdb := NewDB(db) blockChannel := make(chan []*DBHeader, 100) @@ -1055,7 +1062,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) + tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil), network.NewManager(appdb), appdb, mediaServer) tokenManager.SetTokens([]*token.Token{ { @@ -1152,6 +1159,9 @@ func TestFetchNewBlocksCommand_findBlocksWithEthTransfers(t *testing.T) { require.NoError(t, err) tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil} + mediaServer, err := server.NewMediaServer(appdb, nil, nil, db) + require.NoError(t, err) + wdb := NewDB(db) blockChannel := make(chan []*DBHeader, 10) @@ -1172,7 +1182,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) + tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil), network.NewManager(appdb), appdb, mediaServer) tokenManager.SetTokens([]*token.Token{ { @@ -1227,6 +1237,9 @@ func TestFetchNewBlocksCommand(t *testing.T) { require.NoError(t, err) tm := &TransactionManager{db, nil, nil, nil, nil, nil, nil, nil, nil, nil} + mediaServer, err := server.NewMediaServer(appdb, nil, nil, db) + require.NoError(t, err) + wdb := NewDB(db) blockChannel := make(chan []*DBHeader, 10) @@ -1246,7 +1259,8 @@ 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) + + tokenManager := token.NewTokenManager(db, client, community.NewManager(appdb, nil), network.NewManager(appdb), appdb, mediaServer) tokenManager.SetTokens([]*token.Token{ {