Feat: Profile showcase validate collectible ownership (#4737)

* feat: profile showcase checks then presenting collectibles

* chore: more obvious CollectiblesManager configuration
This commit is contained in:
Mikhail Rogachev 2024-02-22 11:08:58 +03:00 committed by GitHub
parent 01b3f8ace4
commit eb5bad4868
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 312 additions and 88 deletions

View File

@ -164,6 +164,7 @@ func (m *DefaultTokenManager) GetAllChainIDs() ([]uint64, error) {
type CollectiblesManager interface { type CollectiblesManager interface {
FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletcommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletcommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error)
GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error)
} }
func (m *DefaultTokenManager) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (BalancesByChain, error) { func (m *DefaultTokenManager) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (BalancesByChain, error) {
@ -5162,6 +5163,11 @@ func (m *Manager) encryptCommunityDescriptionChannel(community *Community, chann
return m.encryptCommunityDescriptionImpl([]byte(community.IDString()+channelID), d) return m.encryptCommunityDescriptionImpl([]byte(community.IDString()+channelID), d)
} }
// TODO: add collectiblesManager to messenger intance
func (m *Manager) GetCollectiblesManager() CollectiblesManager {
return m.collectiblesManager
}
type DecryptCommunityResponse struct { type DecryptCommunityResponse struct {
Decrypted bool Decrypted bool
Description *protobuf.CommunityDescription Description *protobuf.CommunityDescription

View File

@ -3,6 +3,7 @@ package communities
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"image" "image"
"image/png" "image/png"
"math" "math"
@ -114,6 +115,10 @@ func (m *testCollectiblesManager) FetchBalancesByOwnerAndContractAddress(ctx con
return m.response[uint64(chainID)][ownerAddress], nil return m.response[uint64(chainID)][ownerAddress], nil
} }
func (m *testCollectiblesManager) GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) {
return nil, errors.New("GetCollectibleOwnership is not implemented for testCollectiblesManager")
}
type testTokenManager struct { type testTokenManager struct {
response map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big response map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big
} }

View File

@ -3,8 +3,6 @@ package identity
import "errors" import "errors"
var ErrorNoAccountProvidedWithTokenOrCollectible = errors.New("no account provided with tokens or collectible") var ErrorNoAccountProvidedWithTokenOrCollectible = errors.New("no account provided with tokens or collectible")
var ErrorDublicateAccountAddress = errors.New("duplicate account address")
var ErrorAccountVisibilityLowerThanCollectible = errors.New("account visibility lower than collectible")
type ProfileShowcaseVisibility int type ProfileShowcaseVisibility int
@ -122,25 +120,5 @@ func Validate(preferences *ProfileShowcasePreferences) error {
return ErrorNoAccountProvidedWithTokenOrCollectible return ErrorNoAccountProvidedWithTokenOrCollectible
} }
accountsMap := make(map[string]*ProfileShowcaseAccountPreference)
for _, account := range preferences.Accounts {
if _, ok := accountsMap[account.Address]; ok {
return ErrorDublicateAccountAddress
}
accountsMap[account.Address] = account
}
for _, collectible := range preferences.Collectibles {
account, ok := accountsMap[collectible.AccountAddress]
if !ok {
return nil
// NOTE: with current wallet collectible implementation we don't know account on this stage
// return errorNoAccountAddressForCollectible
}
if account.ShowcaseVisibility < collectible.ShowcaseVisibility {
return ErrorAccountVisibilityLowerThanCollectible
}
}
return nil return nil
} }

View File

@ -451,17 +451,16 @@ func NewMessenger(
ensVerifier := ens.New(node, logger, transp, database, c.verifyENSURL, c.verifyENSContractAddress) ensVerifier := ens.New(node, logger, transp, database, c.verifyENSURL, c.verifyENSContractAddress)
var walletAPI *wallet.API
if c.walletService != nil {
walletAPI = wallet.NewAPI(c.walletService)
}
managerOptions := []communities.ManagerOption{ managerOptions := []communities.ManagerOption{
communities.WithAccountManager(c.accountsManager), communities.WithAccountManager(c.accountsManager),
} }
if walletAPI != nil { var walletAPI *wallet.API
if c.walletService != nil {
walletAPI = wallet.NewAPI(c.walletService)
managerOptions = append(managerOptions, communities.WithCollectiblesManager(walletAPI)) managerOptions = append(managerOptions, communities.WithCollectiblesManager(walletAPI))
} else if c.collectiblesManager != nil {
managerOptions = append(managerOptions, communities.WithCollectiblesManager(c.collectiblesManager))
} }
if c.tokenManager != nil { if c.tokenManager != nil {

View File

@ -159,7 +159,7 @@ func (s *MessengerBackupSuite) TestBackupProfile() {
}) })
s.Require().NoError(err) s.Require().NoError(err)
profileShowcasePreferences := DummyProfileShowcasePreferences() profileShowcasePreferences := DummyProfileShowcasePreferences(false)
err = bob1.SetProfileShowcasePreferences(profileShowcasePreferences, false) err = bob1.SetProfileShowcasePreferences(profileShowcasePreferences, false)
s.Require().NoError(err) s.Require().NoError(err)

View File

@ -92,6 +92,7 @@ type config struct {
httpServer *server.MediaServer httpServer *server.MediaServer
rpcClient *rpc.Client rpcClient *rpc.Client
tokenManager communities.TokenManager tokenManager communities.TokenManager
collectiblesManager communities.CollectiblesManager
accountsManager account.Manager accountsManager account.Manager
verifyTransactionClient EthClient verifyTransactionClient EthClient
@ -394,6 +395,13 @@ func WithTokenManager(tokenManager communities.TokenManager) Option {
} }
} }
func WithCollectiblesManager(collectiblesManager communities.CollectiblesManager) Option {
return func(c *config) error {
c.collectiblesManager = collectiblesManager
return nil
}
}
func WithAccountManager(accountManager account.Manager) Option { func WithAccountManager(accountManager account.Manager) Option {
return func(c *config) error { return func(c *config) error {
c.accountsManager = accountManager c.accountsManager = accountManager

View File

@ -6,21 +6,98 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
crand "crypto/rand" crand "crypto/rand"
"errors" "errors"
"math/big"
"reflect" "reflect"
"sort" "sort"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"go.uber.org/zap" "go.uber.org/zap"
eth_common "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/multiaccounts/accounts" "github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/protocol/common" "github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/communities" "github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/identity" "github.com/status-im/status-go/protocol/identity"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/services/wallet/bigint"
w_common "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/thirdparty"
) )
var errorDecryptingPayloadEncryptionKey = errors.New("decrypting the payload encryption key resulted in no error and a nil key") var errorDecryptingPayloadEncryptionKey = errors.New("decrypting the payload encryption key resulted in no error and a nil key")
var errorConvertCollectibleTokenIDToInt = errors.New("failed to convert collectible token id to bigint")
var errorNoAccountPresentedForCollectible = errors.New("account holding the collectible is not presented in the profile showcase")
var errorDublicateAccountAddress = errors.New("duplicate account address")
var errorAccountVisibilityLowerThanCollectible = errors.New("account visibility lower than collectible")
func toCollectibleUniqueID(contractAddress string, tokenID string, chainID uint64) (thirdparty.CollectibleUniqueID, error) {
tokenIDInt := new(big.Int)
tokenIDInt, isTokenIDOk := tokenIDInt.SetString(tokenID, 10)
if !isTokenIDOk {
return thirdparty.CollectibleUniqueID{}, errorConvertCollectibleTokenIDToInt
}
return thirdparty.CollectibleUniqueID{
ContractID: thirdparty.ContractID{
ChainID: w_common.ChainID(chainID),
Address: eth_common.HexToAddress(contractAddress),
},
TokenID: &bigint.BigInt{Int: tokenIDInt},
}, nil
}
func (m *Messenger) fetchCollectibleOwner(contractAddress string, tokenID string, chainID uint64) ([]thirdparty.AccountBalance, error) {
collectibleID, err := toCollectibleUniqueID(contractAddress, tokenID, chainID)
if err != nil {
return nil, err
}
balance, err := m.communitiesManager.GetCollectiblesManager().GetCollectibleOwnership(collectibleID)
if err != nil {
return nil, err
}
return balance, nil
}
func (m *Messenger) validateCollectiblesOwnership(accounts []*identity.ProfileShowcaseAccountPreference,
collectibles []*identity.ProfileShowcaseCollectiblePreference) error {
accountsMap := make(map[string]identity.ProfileShowcaseVisibility)
for _, accountProfile := range accounts {
if _, ok := accountsMap[accountProfile.Address]; ok {
return errorDublicateAccountAddress
}
accountsMap[accountProfile.Address] = accountProfile.ShowcaseVisibility
}
for _, collectibleProfile := range collectibles {
balances, err := m.fetchCollectibleOwner(collectibleProfile.ContractAddress, collectibleProfile.TokenID,
collectibleProfile.ChainID)
if err != nil {
return err
}
// NOTE: ERC721 tokens can have only a single holder
// but ERC1155 which can be supported later can have more than one holder and balances > 1
found := false
for _, balance := range balances {
if accountShowcaseVisibility, ok := accountsMap[balance.Address.String()]; ok {
if accountShowcaseVisibility < collectibleProfile.ShowcaseVisibility {
return errorAccountVisibilityLowerThanCollectible
}
found = true
break
}
}
if !found {
return errorNoAccountPresentedForCollectible
}
}
return nil
}
func (m *Messenger) toProfileShowcaseCommunityProto(preferences []*identity.ProfileShowcaseCommunityPreference, visibility identity.ProfileShowcaseVisibility) []*protobuf.ProfileShowcaseCommunity { func (m *Messenger) toProfileShowcaseCommunityProto(preferences []*identity.ProfileShowcaseCommunityPreference, visibility identity.ProfileShowcaseVisibility) []*protobuf.ProfileShowcaseCommunity {
entries := []*protobuf.ProfileShowcaseCommunity{} entries := []*protobuf.ProfileShowcaseCommunity{}
@ -182,15 +259,16 @@ func (m *Messenger) fromProfileShowcaseAccountProto(messages []*protobuf.Profile
func (m *Messenger) fromProfileShowcaseCollectibleProto(messages []*protobuf.ProfileShowcaseCollectible) []*identity.ProfileShowcaseCollectible { func (m *Messenger) fromProfileShowcaseCollectibleProto(messages []*protobuf.ProfileShowcaseCollectible) []*identity.ProfileShowcaseCollectible {
entries := []*identity.ProfileShowcaseCollectible{} entries := []*identity.ProfileShowcaseCollectible{}
for _, entry := range messages { for _, message := range messages {
entries = append(entries, &identity.ProfileShowcaseCollectible{ entry := &identity.ProfileShowcaseCollectible{
ContractAddress: entry.ContractAddress, ContractAddress: message.ContractAddress,
ChainID: entry.ChainId, ChainID: message.ChainId,
TokenID: entry.TokenId, TokenID: message.TokenId,
CommunityID: entry.CommunityId, CommunityID: message.CommunityId,
AccountAddress: entry.AccountAddress, AccountAddress: message.AccountAddress,
Order: int(entry.Order), Order: int(message.Order),
}) }
entries = append(entries, entry)
} }
return entries return entries
} }
@ -230,6 +308,11 @@ func (m *Messenger) setProfileShowcasePreferences(preferences *identity.ProfileS
return err return err
} }
err = m.validateCollectiblesOwnership(preferences.Accounts, preferences.Collectibles)
if err != nil {
return err
}
err = m.persistence.SaveProfileShowcasePreferences(preferences) err = m.persistence.SaveProfileShowcasePreferences(preferences)
if err != nil { if err != nil {
return err return err

View File

@ -2,10 +2,17 @@ package protocol
import ( import (
"context" "context"
"crypto/ecdsa"
"errors"
"math/big"
"testing" "testing"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"go.uber.org/zap"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/appdatabase"
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts/accounts" "github.com/status-im/status-go/multiaccounts/accounts"
@ -13,14 +20,97 @@ import (
"github.com/status-im/status-go/protocol/identity" "github.com/status-im/status-go/protocol/identity"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests" "github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/sqlite"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/services/wallet/bigint"
walletCommon "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/thirdparty"
"github.com/status-im/status-go/t/helpers"
"github.com/status-im/status-go/waku"
) )
type CollectiblesManagerMock struct {
response map[thirdparty.CollectibleUniqueID][]thirdparty.AccountBalance
}
func (m *CollectiblesManagerMock) FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletCommon.ChainID,
ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) {
return nil, errors.New("FetchBalancesByOwnerAndContractAddress is not implemented for testCollectiblesManager")
}
func (m *CollectiblesManagerMock) GetCollectibleOwnership(requestedID thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) {
// NOTE: TokenID inside of thirdparty.CollectibleUniqueID is a pointer so m.response[id] is now working
for id, balances := range m.response {
if id.ContractID.Address == requestedID.ContractID.Address &&
id.ContractID.ChainID == requestedID.ContractID.ChainID {
return balances, nil
}
}
return []thirdparty.AccountBalance{}, nil
}
func (m *CollectiblesManagerMock) SetResponse(id thirdparty.CollectibleUniqueID, balances []thirdparty.AccountBalance) {
if m.response == nil {
m.response = map[thirdparty.CollectibleUniqueID][]thirdparty.AccountBalance{}
}
m.response[id] = balances
}
func TestMessengerProfileShowcaseSuite(t *testing.T) { // nolint: deadcode,unused func TestMessengerProfileShowcaseSuite(t *testing.T) { // nolint: deadcode,unused
suite.Run(t, new(TestMessengerProfileShowcase)) suite.Run(t, new(TestMessengerProfileShowcase))
} }
type TestMessengerProfileShowcase struct { type TestMessengerProfileShowcase struct {
MessengerBaseTestSuite suite.Suite
m *Messenger // main instance of Messenger
privateKey *ecdsa.PrivateKey // private key for the main instance of Messenger
// If one wants to send messages between different instances of Messenger,
// a single waku service should be shared.
shh types.Waku
logger *zap.Logger
collectiblesMock *CollectiblesManagerMock
}
func (s *TestMessengerProfileShowcase) SetupTest() {
s.logger = tt.MustCreateTestLogger()
config := waku.DefaultConfig
config.MinimumAcceptedPoW = 0
shh := waku.New(&config, s.logger)
s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start())
s.m = s.newMessengerForProfileShowcase()
s.privateKey = s.m.identity
}
func (s *TestMessengerProfileShowcase) TearDownTest() {
TearDownMessenger(&s.Suite, s.m)
_ = s.logger.Sync()
}
func (s *TestMessengerProfileShowcase) newMessengerForProfileShowcase() *Messenger {
db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
s.NoError(err, "creating sqlite db instance")
err = sqlite.Migrate(db)
s.NoError(err, "protocol migrate")
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
s.collectiblesMock = &CollectiblesManagerMock{}
options := []Option{
WithCollectiblesManager(s.collectiblesMock),
}
m, err := newMessengerWithKey(s.shh, privateKey, s.logger, options)
s.Require().NoError(err)
_, err = m.Start()
s.Require().NoError(err)
return m
} }
func (s *TestMessengerProfileShowcase) mutualContact(theirMessenger *Messenger) { func (s *TestMessengerProfileShowcase) mutualContact(theirMessenger *Messenger) {
@ -113,8 +203,22 @@ func (s *TestMessengerProfileShowcase) verifiedContact(theirMessenger *Messenger
} }
func (s *TestMessengerProfileShowcase) TestSaveAndGetProfileShowcasePreferences() { func (s *TestMessengerProfileShowcase) TestSaveAndGetProfileShowcasePreferences() {
request := DummyProfileShowcasePreferences() request := DummyProfileShowcasePreferences(true)
err := s.m.SetProfileShowcasePreferences(request, false)
// Provide collectible balances test response
collectible := request.Collectibles[0]
collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID)
s.Require().NoError(err)
balances := []thirdparty.AccountBalance{
thirdparty.AccountBalance{
Address: gethcommon.HexToAddress(request.Accounts[0].Address),
Balance: &bigint.BigInt{Int: big.NewInt(5)},
TxTimestamp: 0,
},
}
s.collectiblesMock.SetResponse(collectibleID, balances)
err = s.m.SetProfileShowcasePreferences(request, false)
s.Require().NoError(err) s.Require().NoError(err)
// Restored preferences shoulf be same as stored // Restored preferences shoulf be same as stored
@ -149,7 +253,7 @@ func (s *TestMessengerProfileShowcase) TestSaveAndGetProfileShowcasePreferences(
func (s *TestMessengerProfileShowcase) TestFailToSaveProfileShowcasePreferencesWithWrongVisibility() { func (s *TestMessengerProfileShowcase) TestFailToSaveProfileShowcasePreferencesWithWrongVisibility() {
accountEntry := &identity.ProfileShowcaseAccountPreference{ accountEntry := &identity.ProfileShowcaseAccountPreference{
Address: "0x32433445133424", Address: "0x0000000000000000000000000032433445133424",
Name: "Status Account", Name: "Status Account",
ColorID: "blue", ColorID: "blue",
Emoji: ">:-]", Emoji: ">:-]",
@ -159,10 +263,9 @@ func (s *TestMessengerProfileShowcase) TestFailToSaveProfileShowcasePreferencesW
collectibleEntry := &identity.ProfileShowcaseCollectiblePreference{ collectibleEntry := &identity.ProfileShowcaseCollectiblePreference{
ContractAddress: "0x12378534257568678487683576", ContractAddress: "0x12378534257568678487683576",
ChainID: 8, ChainID: 11155111,
TokenID: "0x12321389592999f903", TokenID: "12321389592999903",
CommunityID: "0x01312357798976535", CommunityID: "0x01312357798976535",
AccountAddress: "0x32433445133424",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts, ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
Order: 17, Order: 17,
} }
@ -172,13 +275,26 @@ func (s *TestMessengerProfileShowcase) TestFailToSaveProfileShowcasePreferencesW
Collectibles: []*identity.ProfileShowcaseCollectiblePreference{collectibleEntry}, Collectibles: []*identity.ProfileShowcaseCollectiblePreference{collectibleEntry},
} }
err := s.m.SetProfileShowcasePreferences(request, false) // Provide collectible balances test response
s.Require().Equal(identity.ErrorAccountVisibilityLowerThanCollectible, err) collectible := request.Collectibles[0]
collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID)
s.Require().NoError(err)
balances := []thirdparty.AccountBalance{
thirdparty.AccountBalance{
Address: gethcommon.HexToAddress(request.Accounts[0].Address),
Balance: &bigint.BigInt{Int: big.NewInt(5)},
TxTimestamp: 0,
},
}
s.collectiblesMock.SetResponse(collectibleID, balances)
err = s.m.SetProfileShowcasePreferences(request, false)
s.Require().Equal(errorAccountVisibilityLowerThanCollectible, err)
} }
func (s *TestMessengerProfileShowcase) TestEncryptAndDecryptProfileShowcaseEntries() { func (s *TestMessengerProfileShowcase) TestEncryptAndDecryptProfileShowcaseEntries() {
// Add mutual contact // Add mutual contact
theirMessenger := s.newMessenger() theirMessenger := s.newMessengerForProfileShowcase()
_, err := theirMessenger.Start() _, err := theirMessenger.Start()
s.Require().NoError(err) s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, theirMessenger) defer TearDownMessenger(&s.Suite, theirMessenger)
@ -208,8 +324,8 @@ func (s *TestMessengerProfileShowcase) TestEncryptAndDecryptProfileShowcaseEntri
Collectibles: []*protobuf.ProfileShowcaseCollectible{ Collectibles: []*protobuf.ProfileShowcaseCollectible{
&protobuf.ProfileShowcaseCollectible{ &protobuf.ProfileShowcaseCollectible{
ContractAddress: "0x12378534257568678487683576", ContractAddress: "0x12378534257568678487683576",
ChainId: 7, ChainId: 1,
TokenId: "0x12321389592999f903", TokenId: "12321389592999903",
AccountAddress: "0x32433445133424", AccountAddress: "0x32433445133424",
CommunityId: "0x12378534257568678487683576", CommunityId: "0x12378534257568678487683576",
Order: 0, Order: 0,
@ -232,12 +348,12 @@ func (s *TestMessengerProfileShowcase) TestEncryptAndDecryptProfileShowcaseEntri
UnverifiedTokens: []*protobuf.ProfileShowcaseUnverifiedToken{ UnverifiedTokens: []*protobuf.ProfileShowcaseUnverifiedToken{
&protobuf.ProfileShowcaseUnverifiedToken{ &protobuf.ProfileShowcaseUnverifiedToken{
ContractAddress: "0x454525452023452", ContractAddress: "0x454525452023452",
ChainId: 3, ChainId: 11155111,
Order: 0, Order: 0,
}, },
&protobuf.ProfileShowcaseUnverifiedToken{ &protobuf.ProfileShowcaseUnverifiedToken{
ContractAddress: "0x12312323323233", ContractAddress: "0x12312323323233",
ChainId: 2, ChainId: 1,
Order: 1, Order: 1,
}, },
}, },
@ -302,7 +418,7 @@ func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() {
s.Require().NoError(err) s.Require().NoError(err)
// Add mutual contact // Add mutual contact
mutualContact := s.newMessenger() mutualContact := s.newMessengerForProfileShowcase()
_, err = mutualContact.Start() _, err = mutualContact.Start()
s.Require().NoError(err) s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, mutualContact) defer TearDownMessenger(&s.Suite, mutualContact)
@ -310,7 +426,7 @@ func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() {
s.mutualContact(mutualContact) s.mutualContact(mutualContact)
// Add identity verified contact // Add identity verified contact
verifiedContact := s.newMessenger() verifiedContact := s.newMessengerForProfileShowcase()
_, err = verifiedContact.Start() _, err = verifiedContact.Start()
s.Require().NoError(err) s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, verifiedContact) defer TearDownMessenger(&s.Suite, verifiedContact)
@ -319,7 +435,21 @@ func (s *TestMessengerProfileShowcase) TestShareShowcasePreferences() {
s.verifiedContact(verifiedContact) s.verifiedContact(verifiedContact)
// Save preferences to dispatch changes // Save preferences to dispatch changes
request := DummyProfileShowcasePreferences() request := DummyProfileShowcasePreferences(true)
// Provide collectible balances test response
collectible := request.Collectibles[0]
collectibleID, err := toCollectibleUniqueID(collectible.ContractAddress, collectible.TokenID, collectible.ChainID)
s.Require().NoError(err)
balances := []thirdparty.AccountBalance{
thirdparty.AccountBalance{
Address: gethcommon.HexToAddress(request.Accounts[0].Address),
Balance: &bigint.BigInt{Int: big.NewInt(1)},
TxTimestamp: 32443424,
},
}
s.collectiblesMock.SetResponse(collectibleID, balances)
err = s.m.SetProfileShowcasePreferences(request, false) err = s.m.SetProfileShowcasePreferences(request, false)
s.Require().NoError(err) s.Require().NoError(err)
@ -433,7 +563,7 @@ func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipUnenc
s.Require().NoError(err) s.Require().NoError(err)
// Add bob as a mutual contact // Add bob as a mutual contact
bob := s.newMessenger() bob := s.newMessengerForProfileShowcase()
_, err = bob.Start() _, err = bob.Start()
s.Require().NoError(err) s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob) defer TearDownMessenger(&s.Suite, bob)
@ -500,7 +630,7 @@ func (s *TestMessengerProfileShowcase) TestProfileShowcaseProofOfMembershipEncry
s.Require().NoError(err) s.Require().NoError(err)
// Add bob as a mutual contact // Add bob as a mutual contact
bob := s.newMessenger() bob := s.newMessengerForProfileShowcase()
_, err = bob.Start() _, err = bob.Start()
s.Require().NoError(err) s.Require().NoError(err)
defer TearDownMessenger(&s.Suite, bob) defer TearDownMessenger(&s.Suite, bob)

View File

@ -414,12 +414,12 @@ func RandomBytes(length int) []byte {
return out return out
} }
func DummyProfileShowcasePreferences() *identity.ProfileShowcasePreferences { func DummyProfileShowcasePreferences(withCollectibles bool) *identity.ProfileShowcasePreferences {
return &identity.ProfileShowcasePreferences{ preferences := &identity.ProfileShowcasePreferences{
Communities: []*identity.ProfileShowcaseCommunityPreference{}, // empty to avoid fetching Communities: []*identity.ProfileShowcaseCommunityPreference{}, // empty to avoid fetching
Accounts: []*identity.ProfileShowcaseAccountPreference{ Accounts: []*identity.ProfileShowcaseAccountPreference{
{ {
Address: "0x32433445133424", Address: "0x0000000000000000000000000033433445133423",
Name: "Status Account", Name: "Status Account",
ColorID: "blue", ColorID: "blue",
Emoji: "-_-", Emoji: "-_-",
@ -427,7 +427,7 @@ func DummyProfileShowcasePreferences() *identity.ProfileShowcasePreferences {
Order: 0, Order: 0,
}, },
{ {
Address: "0x3845354643324", Address: "0x0000000000000000000000000032433445133424",
Name: "Money Box", Name: "Money Box",
ColorID: "red", ColorID: "red",
Emoji: ":o)", Emoji: ":o)",
@ -435,17 +435,6 @@ func DummyProfileShowcasePreferences() *identity.ProfileShowcasePreferences {
Order: 1, Order: 1,
}, },
}, },
Collectibles: []*identity.ProfileShowcaseCollectiblePreference{
{
ContractAddress: "0x12378534257568678487683576",
ChainID: 1,
TokenID: "0x12321389592999f903",
CommunityID: "0x01312357798976535",
AccountAddress: "0x32433445133424",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
Order: 0,
},
},
VerifiedTokens: []*identity.ProfileShowcaseVerifiedTokenPreference{ VerifiedTokens: []*identity.ProfileShowcaseVerifiedTokenPreference{
{ {
Symbol: "ETH", Symbol: "ETH",
@ -466,16 +455,34 @@ func DummyProfileShowcasePreferences() *identity.ProfileShowcasePreferences {
UnverifiedTokens: []*identity.ProfileShowcaseUnverifiedTokenPreference{ UnverifiedTokens: []*identity.ProfileShowcaseUnverifiedTokenPreference{
{ {
ContractAddress: "0x454525452023452", ContractAddress: "0x454525452023452",
ChainID: 3, ChainID: 11155111,
ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone, ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
Order: 0, Order: 0,
}, },
{ {
ContractAddress: "0x12312323323233", ContractAddress: "0x12312323323233",
ChainID: 6, ChainID: 1,
ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts, ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
Order: 1, Order: 1,
}, },
}, },
} }
if withCollectibles {
preferences.Collectibles = []*identity.ProfileShowcaseCollectiblePreference{
{
ContractAddress: "0x12378534257568678487683576",
ChainID: 1,
TokenID: "12321389592999903",
CommunityID: "0x01312357798976535",
AccountAddress: "0x32433445133424",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
Order: 0,
},
}
} else {
preferences.Collectibles = []*identity.ProfileShowcaseCollectiblePreference{}
}
return preferences
} }

View File

@ -31,7 +31,7 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcasePreferences() {
}, },
Accounts: []*identity.ProfileShowcaseAccountPreference{ Accounts: []*identity.ProfileShowcaseAccountPreference{
&identity.ProfileShowcaseAccountPreference{ &identity.ProfileShowcaseAccountPreference{
Address: "0x32433445133424", Address: "0x0000000000000000000000000032433445133422",
Name: "Status Account", Name: "Status Account",
ColorID: "blue", ColorID: "blue",
Emoji: "-_-", Emoji: "-_-",
@ -39,7 +39,7 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcasePreferences() {
Order: 0, Order: 0,
}, },
&identity.ProfileShowcaseAccountPreference{ &identity.ProfileShowcaseAccountPreference{
Address: "0x3845354643324", Address: "0x0000000000000000000000000032433445133424",
Name: "Money Box", Name: "Money Box",
ColorID: "red", ColorID: "red",
Emoji: ":o)", Emoji: ":o)",
@ -50,8 +50,8 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcasePreferences() {
Collectibles: []*identity.ProfileShowcaseCollectiblePreference{ Collectibles: []*identity.ProfileShowcaseCollectiblePreference{
&identity.ProfileShowcaseCollectiblePreference{ &identity.ProfileShowcaseCollectiblePreference{
ContractAddress: "0x12378534257568678487683576", ContractAddress: "0x12378534257568678487683576",
ChainID: 3, ChainID: 11155111,
TokenID: "0x12321389592999f903", TokenID: "123213895929994903",
CommunityID: "0x01312357798976535", CommunityID: "0x01312357798976535",
AccountAddress: "0x32433445133424", AccountAddress: "0x32433445133424",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone, ShowcaseVisibility: identity.ProfileShowcaseVisibilityEveryone,
@ -85,7 +85,7 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcasePreferences() {
}, },
&identity.ProfileShowcaseUnverifiedTokenPreference{ &identity.ProfileShowcaseUnverifiedTokenPreference{
ContractAddress: "0x12312323323233", ContractAddress: "0x12312323323233",
ChainID: 2, ChainID: 11155111,
CommunityID: "", CommunityID: "",
ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts, ShowcaseVisibility: identity.ProfileShowcaseVisibilityContacts,
Order: 1, Order: 1,
@ -153,7 +153,7 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() {
}, },
&identity.ProfileShowcaseAccount{ &identity.ProfileShowcaseAccount{
ContactID: "contact_1", ContactID: "contact_1",
Address: "0x3845354643324", Address: "0x0000000000000000000000000032433445133424",
Name: "Money Box", Name: "Money Box",
ColorID: "red", ColorID: "red",
Emoji: ":o)", Emoji: ":o)",
@ -163,8 +163,8 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() {
Collectibles: []*identity.ProfileShowcaseCollectible{ Collectibles: []*identity.ProfileShowcaseCollectible{
&identity.ProfileShowcaseCollectible{ &identity.ProfileShowcaseCollectible{
ContractAddress: "0x12378534257568678487683576", ContractAddress: "0x12378534257568678487683576",
ChainID: 2, ChainID: 1,
TokenID: "0x12321389592999f903", TokenID: "123213895929994903",
CommunityID: "0x01312357798976535", CommunityID: "0x01312357798976535",
Order: 0, Order: 0,
}, },
@ -192,7 +192,7 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() {
}, },
&identity.ProfileShowcaseUnverifiedToken{ &identity.ProfileShowcaseUnverifiedToken{
ContractAddress: "0x12312323323233", ContractAddress: "0x12312323323233",
ChainID: 2, ChainID: 11155111,
CommunityID: "0x32433445133424", CommunityID: "0x32433445133424",
Order: 1, Order: 1,
}, },
@ -216,8 +216,8 @@ func (s *TestProfileShowcasePersistence) TestProfileShowcaseContacts() {
Collectibles: []*identity.ProfileShowcaseCollectible{ Collectibles: []*identity.ProfileShowcaseCollectible{
&identity.ProfileShowcaseCollectible{ &identity.ProfileShowcaseCollectible{
ContractAddress: "0x12378534257568678487683576", ContractAddress: "0x12378534257568678487683576",
ChainID: 2, ChainID: 1,
TokenID: "0x12321389592999f903", TokenID: "123213895929994903",
CommunityID: "0x01312357798976535", CommunityID: "0x01312357798976535",
Order: 1, Order: 1,
}, },
@ -382,8 +382,8 @@ func (s *TestProfileShowcasePersistence) TestUpdateProfileShowcaseAccountOnWalle
s.Require().NoError(err) s.Require().NoError(err)
persistence := newSQLitePersistence(db) persistence := newSQLitePersistence(db)
deleteAccountAddress := "0x3243344513424" deleteAccountAddress := "0x0000000000000000000000000033433445133423"
updateAccountAddress := "0x3845354643324" updateAccountAddress := "0x0000000000000000000000000032433445133424"
preferences := &identity.ProfileShowcasePreferences{ preferences := &identity.ProfileShowcasePreferences{
Accounts: []*identity.ProfileShowcaseAccountPreference{ Accounts: []*identity.ProfileShowcaseAccountPreference{

View File

@ -329,7 +329,7 @@ func (s *SyncDeviceSuite) TestPairingSyncDeviceClientAsSender() {
err = clientBackend.StatusNode().EnsService().API().Add(ctx, ensChainID, ensUsername) err = clientBackend.StatusNode().EnsService().API().Add(ctx, ensChainID, ensUsername)
require.NoError(s.T(), err) require.NoError(s.T(), err)
// generate profile showcase preferences // generate profile showcase preferences
profileShowcasePreferences := protocol.DummyProfileShowcasePreferences() profileShowcasePreferences := protocol.DummyProfileShowcasePreferences(false)
err = clientBackend.Messenger().SetProfileShowcasePreferences(profileShowcasePreferences, false) err = clientBackend.Messenger().SetProfileShowcasePreferences(profileShowcasePreferences, false)
require.NoError(s.T(), err) require.NoError(s.T(), err)

View File

@ -316,6 +316,10 @@ func (api *API) FetchBalancesByOwnerAndContractAddress(ctx context.Context, chai
return api.s.collectiblesManager.FetchBalancesByOwnerAndContractAddress(ctx, chainID, ownerAddress, contractAddresses) return api.s.collectiblesManager.FetchBalancesByOwnerAndContractAddress(ctx, chainID, ownerAddress, contractAddresses)
} }
func (api *API) GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) {
return api.s.collectiblesManager.GetCollectibleOwnership(id)
}
func (api *API) RefetchOwnedCollectibles() error { func (api *API) RefetchOwnedCollectibles() error {
log.Debug("wallet.api.RefetchOwnedCollectibles") log.Debug("wallet.api.RefetchOwnedCollectibles")

View File

@ -417,6 +417,10 @@ func (o *Manager) FetchCollectionsDataByContractID(ctx context.Context, ids []th
return mapToList(data), nil return mapToList(data), nil
} }
func (o *Manager) GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) {
return o.ownershipDB.GetOwnership(id)
}
func (o *Manager) getContractOwnershipProviders(chainID walletCommon.ChainID) (mainProvider thirdparty.CollectibleContractOwnershipProvider, fallbackProvider thirdparty.CollectibleContractOwnershipProvider) { func (o *Manager) getContractOwnershipProviders(chainID walletCommon.ChainID) (mainProvider thirdparty.CollectibleContractOwnershipProvider, fallbackProvider thirdparty.CollectibleContractOwnershipProvider) {
mainProvider = nil mainProvider = nil
fallbackProvider = nil fallbackProvider = nil