chore(communities)_: reevaluate permissions with pre-fetched owners

That's an optimisation. Instead of fetching collectibles owners for each
member, it is fetched once, before members iteration.

It should significantly reduce amount of queries to providers.

closes: status-im/status-desktop#14914
This commit is contained in:
Patryk Osmaczko 2024-05-29 16:12:16 +02:00 committed by osmaczko
parent b0213e6a41
commit ec9e29ef92
7 changed files with 210 additions and 43 deletions

View File

@ -302,6 +302,7 @@ func (m *DefaultTokenManager) GetAllChainIDs() ([]uint64, error) {
type CollectiblesManager interface {
FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletcommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error)
GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error)
FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID walletcommon.ChainID, contractAddress gethcommon.Address) (*thirdparty.CollectibleContractOwnership, error)
}
func (m *DefaultTokenManager) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (BalancesByChain, error) {
@ -984,6 +985,27 @@ func (rmr *reevaluateMembersResult) newPrivilegedRoles() (map[protobuf.Community
return result, nil
}
// Fetch all owners for all collectibles.
func (m *Manager) fetchCollectiblesOwners(collectibles map[walletcommon.ChainID]map[gethcommon.Address]struct{}) (CollectiblesOwners, error) {
if m.collectiblesManager == nil {
return nil, errors.New("no collectibles manager")
}
collectiblesOwners := make(CollectiblesOwners)
for chainID, contractAddresses := range collectibles {
collectiblesOwners[chainID] = make(map[gethcommon.Address]*thirdparty.CollectibleContractOwnership)
for contractAddress := range contractAddresses {
ownership, err := m.collectiblesManager.FetchCollectibleOwnersByContractAddress(context.Background(), chainID, contractAddress)
if err != nil {
return nil, err
}
collectiblesOwners[chainID][contractAddress] = ownership
}
}
return collectiblesOwners, nil
}
// use it only for testing purposes
func (m *Manager) ReevaluateMembers(communityID types.HexBytes) (*Community, map[protobuf.CommunityMember_Roles][]*ecdsa.PublicKey, error) {
return m.reevaluateMembers(communityID)
@ -1009,6 +1031,12 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
communityPermissionsPreParsedData, channelPermissionsPreParsedData := PreParsePermissionsData(community.tokenPermissions())
// Optimization: Fetch all collectibles owners before members iteration to avoid asking providers for the same collectibles.
collectiblesOwners, err := m.fetchCollectiblesOwners(CollectibleAddressesFromPreParsedPermissionsData(communityPermissionsPreParsedData, channelPermissionsPreParsedData))
if err != nil {
return nil, nil, err
}
result := &reevaluateMembersResult{
membersToRemove: map[string]struct{}{},
membersRoles: map[string]*reevaluateMemberRole{},
@ -1046,7 +1074,7 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
becomeTokenMasterPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER]
if becomeTokenMasterPermissions != nil {
permissionResponse, err := m.PermissionChecker.CheckPermissions(becomeTokenMasterPermissions, accountsAndChainIDs, true)
permissionResponse, err := m.PermissionChecker.CheckPermissionsWithPreFetchedData(becomeTokenMasterPermissions, accountsAndChainIDs, true, collectiblesOwners)
if err != nil {
return nil, nil, err
}
@ -1060,7 +1088,7 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
becomeAdminPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_ADMIN]
if becomeAdminPermissions != nil {
permissionResponse, err := m.PermissionChecker.CheckPermissions(becomeAdminPermissions, accountsAndChainIDs, true)
permissionResponse, err := m.PermissionChecker.CheckPermissionsWithPreFetchedData(becomeAdminPermissions, accountsAndChainIDs, true, collectiblesOwners)
if err != nil {
return nil, nil, err
}
@ -1074,7 +1102,7 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
becomeMemberPermissions := communityPermissionsPreParsedData[protobuf.CommunityTokenPermission_BECOME_MEMBER]
if becomeMemberPermissions != nil {
permissionResponse, err := m.PermissionChecker.CheckPermissions(becomeMemberPermissions, accountsAndChainIDs, true)
permissionResponse, err := m.PermissionChecker.CheckPermissionsWithPreFetchedData(becomeMemberPermissions, accountsAndChainIDs, true, collectiblesOwners)
if err != nil {
return nil, nil, err
}
@ -1086,7 +1114,7 @@ func (m *Manager) reevaluateMembers(communityID types.HexBytes) (*Community, map
}
}
addToChannels, removeFromChannels, err := m.reevaluateMemberChannelsPermissions(community, memberPubKey, channelPermissionsPreParsedData, accountsAndChainIDs)
addToChannels, removeFromChannels, err := m.reevaluateMemberChannelsPermissions(community, memberPubKey, channelPermissionsPreParsedData, accountsAndChainIDs, collectiblesOwners)
if err != nil {
return nil, nil, err
}
@ -1199,13 +1227,13 @@ func (m *Manager) applyReevaluateMembersResult(communityID types.HexBytes, resul
}
func (m *Manager) reevaluateMemberChannelsPermissions(community *Community, memberPubKey *ecdsa.PublicKey,
channelPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination) (map[string]protobuf.CommunityMember_ChannelRole, map[string]struct{}, error) {
channelPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, collectiblesOwners CollectiblesOwners) (map[string]protobuf.CommunityMember_ChannelRole, map[string]struct{}, error) {
addToChannels := map[string]protobuf.CommunityMember_ChannelRole{}
removeFromChannels := map[string]struct{}{}
// check which permissions we satisfy and which not
channelPermissionsCheckResult, err := m.checkChannelsPermissions(channelPermissionsPreParsedData, accountsAndChainIDs, true)
channelPermissionsCheckResult, err := m.checkChannelsPermissionsWithPreFetchedData(channelPermissionsPreParsedData, accountsAndChainIDs, true, collectiblesOwners)
if err != nil {
return nil, nil, err
}
@ -1241,10 +1269,18 @@ func (m *Manager) reevaluateMemberChannelsPermissions(community *Community, memb
return addToChannels, removeFromChannels, nil
}
func (m *Manager) checkChannelsPermissions(channelsPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (map[string]map[protobuf.CommunityTokenPermission_Type]bool, error) {
func (m *Manager) checkChannelsPermissionsImpl(channelsPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool, collectiblesOwners CollectiblesOwners) (map[string]map[protobuf.CommunityTokenPermission_Type]bool, error) {
checkPermissions := func(channelsPermissionPreParsedData *PreParsedCommunityPermissionsData) (*CheckPermissionsResponse, error) {
if collectiblesOwners != nil {
return m.PermissionChecker.CheckPermissionsWithPreFetchedData(channelsPermissionPreParsedData, accountsAndChainIDs, true, collectiblesOwners)
} else {
return m.PermissionChecker.CheckPermissions(channelsPermissionPreParsedData, accountsAndChainIDs, true)
}
}
channelPermissionsCheckResult := make(map[string]map[protobuf.CommunityTokenPermission_Type]bool)
for _, channelsPermissionPreParsedData := range channelsPermissionsPreParsedData {
permissionResponse, err := m.PermissionChecker.CheckPermissions(channelsPermissionPreParsedData, accountsAndChainIDs, true)
permissionResponse, err := checkPermissions(channelsPermissionPreParsedData)
if err != nil {
return channelPermissionsCheckResult, err
}
@ -1264,6 +1300,14 @@ func (m *Manager) checkChannelsPermissions(channelsPermissionsPreParsedData map[
return channelPermissionsCheckResult, nil
}
func (m *Manager) checkChannelsPermissionsWithPreFetchedData(channelsPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool, collectiblesOwners CollectiblesOwners) (map[string]map[protobuf.CommunityTokenPermission_Type]bool, error) {
return m.checkChannelsPermissionsImpl(channelsPermissionsPreParsedData, accountsAndChainIDs, shortcircuit, collectiblesOwners)
}
func (m *Manager) checkChannelsPermissions(channelsPermissionsPreParsedData map[string]*PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (map[string]map[protobuf.CommunityTokenPermission_Type]bool, error) {
return m.checkChannelsPermissionsImpl(channelsPermissionsPreParsedData, accountsAndChainIDs, shortcircuit, nil)
}
func (m *Manager) StartMembersReevaluationLoop(communityID types.HexBytes, reevaluateOnStart bool) {
go m.reevaluateMembersLoop(communityID, reevaluateOnStart)
}

View File

@ -137,6 +137,32 @@ func (m *testCollectiblesManager) GetCollectibleOwnership(id thirdparty.Collecti
return nil, errors.New("GetCollectibleOwnership is not implemented for testCollectiblesManager")
}
func (m *testCollectiblesManager) FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID walletCommon.ChainID, contractAddress gethcommon.Address) (*thirdparty.CollectibleContractOwnership, error) {
ret := &thirdparty.CollectibleContractOwnership{
ContractAddress: contractAddress,
Owners: []thirdparty.CollectibleOwner{},
}
balancesPerOwner, ok := m.response[uint64(chainID)]
if !ok {
return ret, nil
}
for ownerAddress, collectibles := range balancesPerOwner {
for collectibleAddress, balances := range collectibles {
if collectibleAddress == contractAddress {
ret.Owners = append(ret.Owners, thirdparty.CollectibleOwner{
OwnerAddress: ownerAddress,
TokenBalances: balances,
})
break
}
}
}
return ret, nil
}
type testTokenManager struct {
response map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big
}

View File

@ -23,6 +23,7 @@ import (
type PermissionChecker interface {
CheckPermissionToJoin(*Community, []gethcommon.Address) (*CheckPermissionToJoinResponse, error)
CheckPermissions(permissionsParsedData *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error)
CheckPermissionsWithPreFetchedData(permissionsParsedData *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool, collectiblesOwners CollectiblesOwners) (*CheckPermissionsResponse, error)
}
type DefaultPermissionChecker struct {
@ -62,7 +63,10 @@ func (p *DefaultPermissionChecker) getOwnedENS(addresses []gethcommon.Address) (
}
return ownedENS, nil
}
func (p *DefaultPermissionChecker) GetOwnedERC721Tokens(walletAddresses []gethcommon.Address, tokenRequirements map[uint64]map[string]*protobuf.TokenCriteria, chainIDs []uint64) (CollectiblesByChain, error) {
type collectiblesBalancesGetter = func(ctx context.Context, chainID walletcommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error)
func (p *DefaultPermissionChecker) getOwnedERC721Tokens(walletAddresses []gethcommon.Address, tokenRequirements map[uint64]map[string]*protobuf.TokenCriteria, chainIDs []uint64, getCollectiblesBalances collectiblesBalancesGetter) (CollectiblesByChain, error) {
if p.collectiblesManager == nil {
return nil, errors.New("no collectibles manager")
}
@ -94,7 +98,7 @@ func (p *DefaultPermissionChecker) GetOwnedERC721Tokens(walletAddresses []gethco
}
for _, owner := range walletAddresses {
balances, err := p.collectiblesManager.FetchBalancesByOwnerAndContractAddress(ctx, walletcommon.ChainID(chainID), owner, contractAddresses)
balances, err := getCollectiblesBalances(ctx, walletcommon.ChainID(chainID), owner, contractAddresses)
if err != nil {
p.logger.Info("couldn't fetch owner assets", zap.Error(err))
return nil, err
@ -208,10 +212,9 @@ func (p *DefaultPermissionChecker) checkPermissionsOrDefault(permissions []*Comm
return p.CheckPermissions(preParsedPermissions, accountsAndChainIDs, false)
}
// CheckPermissions will retrieve balances and check whether the user has
// permission to join the community, if shortcircuit is true, it will stop as soon
// as we know the answer
func (p *DefaultPermissionChecker) CheckPermissions(permissionsParsedData *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error) {
type ownedERC721TokensGetter = func(walletAddresses []gethcommon.Address, tokenRequirements map[uint64]map[string]*protobuf.TokenCriteria, chainIDs []uint64) (CollectiblesByChain, error)
func (p *DefaultPermissionChecker) checkPermissions(permissionsParsedData *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool, getOwnedERC721Tokens ownedERC721TokensGetter) (*CheckPermissionsResponse, error) {
response := &CheckPermissionsResponse{
Satisfied: false,
@ -260,7 +263,7 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissionsParsedData *PrePa
ownedERC721Tokens := make(CollectiblesByChain)
if len(chainIDsForERC721) > 0 {
collectibles, err := p.GetOwnedERC721Tokens(accounts, erc721TokenRequirements, chainIDsForERC721)
collectibles, err := getOwnedERC721Tokens(accounts, erc721TokenRequirements, chainIDsForERC721)
if err != nil {
return nil, err
}
@ -445,6 +448,53 @@ func (p *DefaultPermissionChecker) CheckPermissions(permissionsParsedData *PrePa
return response, nil
}
// CheckPermissions will retrieve balances and check whether the user has
// permission to join the community, if shortcircuit is true, it will stop as soon
// as we know the answer
func (p *DefaultPermissionChecker) CheckPermissions(permissionsParsedData *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionsResponse, error) {
var getOwnedERC721Tokens ownedERC721TokensGetter = func(walletAddresses []gethcommon.Address, tokenRequirements map[uint64]map[string]*protobuf.TokenCriteria, chainIDs []uint64) (CollectiblesByChain, error) {
return p.getOwnedERC721Tokens(walletAddresses, tokenRequirements, chainIDs, p.collectiblesManager.FetchBalancesByOwnerAndContractAddress)
}
return p.checkPermissions(permissionsParsedData, accountsAndChainIDs, shortcircuit, getOwnedERC721Tokens)
}
type CollectiblesOwners = map[walletcommon.ChainID]map[gethcommon.Address]*thirdparty.CollectibleContractOwnership
// Same as CheckPermissions but relies on already provided collectibles owners
func (p *DefaultPermissionChecker) CheckPermissionsWithPreFetchedData(permissionsParsedData *PreParsedCommunityPermissionsData, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool, collectiblesOwners CollectiblesOwners) (*CheckPermissionsResponse, error) {
var getCollectiblesBalances collectiblesBalancesGetter = func(ctx context.Context, chainID walletcommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) {
ret := make(thirdparty.TokenBalancesPerContractAddress)
collectiblesByChain, ok := collectiblesOwners[chainID]
if !ok {
return nil, errors.New("no data available for chainID")
}
for _, contractAddress := range contractAddresses {
ownership, ok := collectiblesByChain[contractAddress]
if !ok {
return nil, errors.New("no data available for collectible")
}
for _, nftOwner := range ownership.Owners {
if nftOwner.OwnerAddress == ownerAddress {
ret[contractAddress] = nftOwner.TokenBalances
break
}
}
}
return ret, nil
}
var getOwnedERC721Tokens ownedERC721TokensGetter = func(walletAddresses []gethcommon.Address, tokenRequirements map[uint64]map[string]*protobuf.TokenCriteria, chainIDs []uint64) (CollectiblesByChain, error) {
return p.getOwnedERC721Tokens(walletAddresses, tokenRequirements, chainIDs, getCollectiblesBalances)
}
return p.checkPermissions(permissionsParsedData, accountsAndChainIDs, shortcircuit, getOwnedERC721Tokens)
}
func preParsedPermissionsData(permissions []*CommunityTokenPermission) *PreParsedPermissionsData {
erc20TokenRequirements, erc721TokenRequirements, _ := ExtractTokenCriteria(permissions)
@ -505,3 +555,33 @@ func PreParsePermissionsData(permissions map[string]*CommunityTokenPermission) (
return communityPermissionsPreParsedData, channelPermissionsPreParsedData
}
func CollectibleAddressesFromPreParsedPermissionsData(communityPermissions map[protobuf.CommunityTokenPermission_Type]*PreParsedCommunityPermissionsData, channelPermissions map[string]*PreParsedCommunityPermissionsData) map[walletcommon.ChainID]map[gethcommon.Address]struct{} {
ret := make(map[walletcommon.ChainID]map[gethcommon.Address]struct{})
allPermissionsData := []*PreParsedCommunityPermissionsData{}
for _, permissionsData := range communityPermissions {
if permissionsData != nil {
allPermissionsData = append(allPermissionsData, permissionsData)
}
}
for _, permissionsData := range channelPermissions {
if permissionsData != nil {
allPermissionsData = append(allPermissionsData, permissionsData)
}
}
for _, data := range allPermissionsData {
for chainID, contractAddresses := range data.Erc721TokenRequirements {
if ret[walletcommon.ChainID(chainID)] == nil {
ret[walletcommon.ChainID(chainID)] = make(map[gethcommon.Address]struct{})
}
for contractAddress := range contractAddresses {
ret[walletcommon.ChainID(chainID)][gethcommon.HexToAddress(contractAddress)] = struct{}{}
}
}
}
return ret
}

View File

@ -24,6 +24,8 @@ import (
"github.com/status-im/status-go/protocol/communities/token"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
walletCommon "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/thirdparty"
walletToken "github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
@ -72,6 +74,37 @@ func (m *TokenManagerMock) FindOrCreateTokenByAddress(ctx context.Context, chain
return nil
}
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 (m *CollectiblesManagerMock) FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID walletCommon.ChainID, contractAddress gethcommon.Address) (*thirdparty.CollectibleContractOwnership, error) {
return nil, errors.New("FetchCollectibleOwnersByContractAddress is not implemented for CollectiblesManagerMock")
}
type CollectiblesServiceMock struct {
Collectibles map[uint64]map[string]*communities.CollectibleContractData
Assets map[uint64]map[string]*communities.AssetContractData
@ -227,9 +260,12 @@ func newTestCommunitiesMessenger(s *suite.Suite, waku types.Waku, config testCom
Balances: config.mockedBalances,
}
collectiblesManagerMock := &CollectiblesManagerMock{}
options := []Option{
WithAccountManager(accountsManagerMock),
WithTokenManager(tokenManagerMock),
WithCollectiblesManager(collectiblesManagerMock),
WithCommunityTokensService(config.collectiblesService),
WithAppSettings(*config.appSettings, *config.nodeConfig),
}

View File

@ -3624,6 +3624,10 @@ func (t *testPermissionChecker) CheckPermissions(permissionsParsedData *communit
return &communities.CheckPermissionsResponse{Satisfied: true}, nil
}
func (t *testPermissionChecker) CheckPermissionsWithPreFetchedData(permissionsParsedData *communities.PreParsedCommunityPermissionsData, accountsAndChainIDs []*communities.AccountChainIDsCombination, shortcircuit bool, collectiblesOwners communities.CollectiblesOwners) (*communities.CheckPermissionsResponse, error) {
return &communities.CheckPermissionsResponse{Satisfied: true}, nil
}
func (s *MessengerCommunitiesSuite) TestStartCommunityRekeyLoop() {
community, chat := createEncryptedCommunity(&s.Suite, s.owner)
s.Require().True(community.Encrypted())

View File

@ -25,39 +25,11 @@ import (
"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
suite.Run(t, new(TestMessengerProfileShowcase))
}

View File

@ -326,6 +326,11 @@ func (api *API) GetCollectibleOwnersByContractAddress(ctx context.Context, chain
return api.s.collectiblesManager.FetchCollectibleOwnersByContractAddress(ctx, chainID, contractAddress)
}
func (api *API) FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID wcommon.ChainID, contractAddress common.Address) (*thirdparty.CollectibleContractOwnership, error) {
log.Debug("call to FetchCollectibleOwnersByContractAddress")
return api.s.collectiblesManager.FetchCollectibleOwnersByContractAddress(ctx, chainID, contractAddress)
}
func (api *API) SearchCollectibles(ctx context.Context, chainID wcommon.ChainID, text string, cursor string, limit int, providerID string) (*thirdparty.FullCollectibleDataContainer, error) {
log.Debug("call to SearchCollectibles")
return api.s.collectiblesManager.SearchCollectibles(ctx, chainID, text, cursor, limit, providerID)