diff --git a/protocol/communities/community.go b/protocol/communities/community.go index d14e7f039..5a63ac978 100644 --- a/protocol/communities/community.go +++ b/protocol/communities/community.go @@ -865,6 +865,24 @@ func (o *Community) AddCommunityTokensMetadata(token *protobuf.CommunityTokenMet return o.config.CommunityDescription, nil } +func containsToken(tokens []*protobuf.CommunityTokenMetadata, symbol string) bool { + for _, token := range tokens { + if token.Symbol == symbol { + return true + } + } + return false +} + +func (o *Community) UpsertCommunityTokensMetadata(token *protobuf.CommunityTokenMetadata) (bool, error) { + if containsToken(o.config.CommunityDescription.CommunityTokensMetadata, token.Symbol) { + return false, nil + } + + _, err := o.AddCommunityTokensMetadata(token) + return true, err +} + func (o *Community) UnbanUserFromCommunity(pk *ecdsa.PublicKey) (*protobuf.CommunityDescription, error) { o.mutex.Lock() defer o.mutex.Unlock() diff --git a/protocol/communities/manager.go b/protocol/communities/manager.go index 1c3191f45..feb5343fa 100644 --- a/protocol/communities/manager.go +++ b/protocol/communities/manager.go @@ -481,6 +481,11 @@ func (m *Manager) Start() error { if m.ownerVerifier != nil { m.runOwnerVerificationLoop() } + + go func() { + _ = m.fillMissingCommunityTokens() + }() + return nil } @@ -513,6 +518,61 @@ func (m *Manager) runENSVerificationLoop() { }() } +// This function is mostly a way to fix any community that is missing tokens in its description +func (m *Manager) fillMissingCommunityTokens() error { + controlledCommunities, err := m.Controlled() + if err != nil { + m.logger.Error("failed to retrieve orgs", zap.Error(err)) + return err + } + + unlock := func() { + for _, c := range controlledCommunities { + m.communityLock.Unlock(c.ID()) + } + } + for _, c := range controlledCommunities { + m.communityLock.Lock(c.ID()) + } + defer unlock() + + for _, community := range controlledCommunities { + tokens, err := m.GetCommunityTokens(community.IDString()) + if err != nil { + m.logger.Error("failed to retrieve community tokens", zap.Error(err)) + return err + } + + for _, token := range tokens { + if token.DeployState != community_token.Deployed { + continue + } + tokenMetadata := &protobuf.CommunityTokenMetadata{ + ContractAddresses: map[uint64]string{uint64(token.ChainID): token.Address}, + Description: token.Description, + Image: token.Base64Image, + Symbol: token.Symbol, + TokenType: token.TokenType, + Name: token.Name, + Decimals: uint32(token.Decimals), + } + modified, err := community.UpsertCommunityTokensMetadata(tokenMetadata) + if err != nil { + m.logger.Error("failed to add token metadata to the description", zap.Error(err)) + return err + } + if modified { + err = m.saveAndPublish(community) + if err != nil { + m.logger.Error("failed to save the new community", zap.Error(err)) + return err + } + } + } + } + return nil +} + // Only for testing func (m *Manager) CommunitiesToValidate() (map[string][]communityToValidate, error) { // nolint: golint return m.persistence.getCommunitiesToValidate() diff --git a/protocol/communities/manager_test.go b/protocol/communities/manager_test.go index 18879381c..efbd5669a 100644 --- a/protocol/communities/manager_test.go +++ b/protocol/communities/manager_test.go @@ -19,6 +19,7 @@ import ( userimages "github.com/status-im/status-go/images" "github.com/status-im/status-go/params" "github.com/status-im/status-go/protocol/common" + community_token "github.com/status-im/status-go/protocol/communities/token" "github.com/status-im/status-go/protocol/requests" "github.com/status-im/status-go/protocol/transport" v1 "github.com/status-im/status-go/protocol/v1" @@ -2003,3 +2004,48 @@ func (s *ManagerSuite) TestCommunityQueueMultipleDifferentSignersIgnoreIfNotRetu s.Require().NoError(err) s.Require().Equal(clock1, fetchedCommunity.config.CommunityDescription.Clock) } + +func (s *ManagerSuite) TestFillMissingCommunityTokens() { + // Create community + request := &requests.CreateCommunity{ + Name: "status", + Description: "token membership description", + Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, + } + + community, err := s.manager.CreateCommunity(request, true) + s.Require().NoError(err) + s.Require().NotNil(community) + s.Require().Len(community.CommunityTokensMetadata(), 0) + + // Create community token but without adding to the description + token := community_token.CommunityToken{ + TokenType: protobuf.CommunityTokenType_ERC721, + CommunityID: community.IDString(), + Address: "0x001", + Name: "TestTok", + Symbol: "TST", + Description: "Desc", + Supply: &bigint.BigInt{Int: big.NewInt(0)}, + InfiniteSupply: true, + Transferable: true, + RemoteSelfDestruct: true, + ChainID: 1, + DeployState: community_token.Deployed, + Base64Image: "", + Decimals: 18, + Deployer: "0x0002", + PrivilegesLevel: community_token.CommunityLevel, + } + + err = s.manager.persistence.AddCommunityToken(&token) + s.Require().NoError(err) + + // Fill community with missing token + err = s.manager.fillMissingCommunityTokens() + s.Require().NoError(err) + + community, err = s.manager.GetByID(community.ID()) + s.Require().NoError(err) + s.Require().Len(community.CommunityTokensMetadata(), 1) +}