feat: implemented multi chain collectible ownership provider
This commit is contained in:
parent
0919a87588
commit
1f379aec1f
|
@ -27,7 +27,6 @@ import (
|
|||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/status-im/status-go/account"
|
||||
"github.com/status-im/status-go/eth-node/crypto"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
|
@ -41,7 +40,7 @@ import (
|
|||
"github.com/status-im/status-go/protocol/transport"
|
||||
"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/opensea"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/signal"
|
||||
)
|
||||
|
@ -71,11 +70,11 @@ type Manager struct {
|
|||
identity *ecdsa.PrivateKey
|
||||
accountsManager account.Manager
|
||||
tokenManager TokenManager
|
||||
collectiblesManager CollectiblesManager
|
||||
logger *zap.Logger
|
||||
stdoutLogger *zap.Logger
|
||||
transport *transport.Transport
|
||||
quit chan struct{}
|
||||
openseaClientBuilder openseaClientBuilder
|
||||
torrentConfig *params.TorrentConfig
|
||||
torrentClient *torrent.Client
|
||||
walletConfig *params.WalletConfig
|
||||
|
@ -87,21 +86,6 @@ type Manager struct {
|
|||
stopped bool
|
||||
}
|
||||
|
||||
type openseaClient interface {
|
||||
FetchAllAssetsByOwnerAndContractAddress(owner gethcommon.Address, contractAddresses []gethcommon.Address, cursor string, limit int) (*opensea.AssetContainer, error)
|
||||
}
|
||||
|
||||
type openseaClientBuilder interface {
|
||||
NewOpenseaClient(uint64, string, *event.Feed) (openseaClient, error)
|
||||
}
|
||||
|
||||
type defaultOpenseaBuilder struct {
|
||||
}
|
||||
|
||||
func (b *defaultOpenseaBuilder) NewOpenseaClient(chainID uint64, apiKey string, feed *event.Feed) (openseaClient, error) {
|
||||
return opensea.NewOpenseaClient(chainID, apiKey, nil)
|
||||
}
|
||||
|
||||
type HistoryArchiveDownloadTask struct {
|
||||
CancelChan chan struct{}
|
||||
Waiter sync.WaitGroup
|
||||
|
@ -123,10 +107,10 @@ func (t *HistoryArchiveDownloadTask) Cancel() {
|
|||
}
|
||||
|
||||
type managerOptions struct {
|
||||
accountsManager account.Manager
|
||||
tokenManager TokenManager
|
||||
walletConfig *params.WalletConfig
|
||||
openseaClientBuilder openseaClientBuilder
|
||||
accountsManager account.Manager
|
||||
tokenManager TokenManager
|
||||
collectiblesManager CollectiblesManager
|
||||
walletConfig *params.WalletConfig
|
||||
}
|
||||
|
||||
type TokenManager interface {
|
||||
|
@ -157,6 +141,10 @@ func (m *DefaultTokenManager) GetAllChainIDs() ([]uint64, error) {
|
|||
return chainIDs, nil
|
||||
}
|
||||
|
||||
type CollectiblesManager interface {
|
||||
FetchBalancesByOwnerAndContractAddress(chainID uint64, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error)
|
||||
}
|
||||
|
||||
func (m *DefaultTokenManager) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (BalancesByChain, error) {
|
||||
clients, err := m.tokenManager.RPCClient.EthClients(chainIDs)
|
||||
if err != nil {
|
||||
|
@ -175,9 +163,9 @@ func WithAccountManager(accountsManager account.Manager) ManagerOption {
|
|||
}
|
||||
}
|
||||
|
||||
func WithOpenseaClientBuilder(builder openseaClientBuilder) ManagerOption {
|
||||
func WithCollectiblesManager(collectiblesManager CollectiblesManager) ManagerOption {
|
||||
return func(opts *managerOptions) {
|
||||
opts.openseaClientBuilder = builder
|
||||
opts.collectiblesManager = collectiblesManager
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -235,6 +223,10 @@ func NewManager(identity *ecdsa.PrivateKey, db *sql.DB, encryptor *encryption.Pr
|
|||
manager.accountsManager = managerConfig.accountsManager
|
||||
}
|
||||
|
||||
if managerConfig.collectiblesManager != nil {
|
||||
manager.collectiblesManager = managerConfig.collectiblesManager
|
||||
}
|
||||
|
||||
if managerConfig.tokenManager != nil {
|
||||
manager.tokenManager = managerConfig.tokenManager
|
||||
}
|
||||
|
@ -243,12 +235,6 @@ func NewManager(identity *ecdsa.PrivateKey, db *sql.DB, encryptor *encryption.Pr
|
|||
manager.walletConfig = managerConfig.walletConfig
|
||||
}
|
||||
|
||||
if managerConfig.openseaClientBuilder != nil {
|
||||
manager.openseaClientBuilder = managerConfig.openseaClientBuilder
|
||||
} else {
|
||||
manager.openseaClientBuilder = &defaultOpenseaBuilder{}
|
||||
}
|
||||
|
||||
if verifier != nil {
|
||||
|
||||
sub := verifier.Subscribe()
|
||||
|
@ -2002,8 +1988,9 @@ func (m *Manager) checkPermissions(permissions []*protobuf.CommunityTokenPermiss
|
|||
continue
|
||||
}
|
||||
|
||||
if _, exists := ownedERC721Tokens[chainID][account][strings.ToLower(address)]; exists {
|
||||
|
||||
tokenBalances := ownedERC721Tokens[chainID][account][gethcommon.HexToAddress(address)]
|
||||
if len(tokenBalances) > 0 {
|
||||
// 'account' owns some TokenID owned from contract 'address'
|
||||
if _, exists := accountsChainIDsCombinations[account]; !exists {
|
||||
accountsChainIDsCombinations[account] = make(map[uint64]bool)
|
||||
}
|
||||
|
@ -2019,8 +2006,8 @@ func (m *Manager) checkPermissions(permissions []*protobuf.CommunityTokenPermiss
|
|||
for _, tokenID := range tokenRequirement.TokenIds {
|
||||
tokenIDBigInt := new(big.Int).SetUint64(tokenID)
|
||||
|
||||
for _, asset := range ownedERC721Tokens[chainID][account][strings.ToLower(address)] {
|
||||
if asset.TokenID.Cmp(tokenIDBigInt) == 0 {
|
||||
for _, asset := range tokenBalances {
|
||||
if asset.TokenID.Cmp(tokenIDBigInt) == 0 && asset.Balance.Sign() > 0 {
|
||||
tokenRequirementMet = true
|
||||
accountsChainIDsCombinations[account][chainID] = true
|
||||
break tokenIDsLoop
|
||||
|
@ -2143,15 +2130,14 @@ func (m *Manager) checkPermissions(permissions []*protobuf.CommunityTokenPermiss
|
|||
return response, nil
|
||||
}
|
||||
|
||||
type CollectiblesByChain = map[uint64]map[gethcommon.Address]map[string][]opensea.Asset
|
||||
type CollectiblesByChain = map[uint64]map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress
|
||||
|
||||
func (m *Manager) GetOwnedERC721Tokens(walletAddresses []gethcommon.Address, tokenRequirements map[uint64]map[string]*protobuf.TokenCriteria, chainIDs []uint64) (CollectiblesByChain, error) {
|
||||
|
||||
if m.walletConfig == nil || m.walletConfig.OpenseaAPIKey == "" {
|
||||
return nil, errors.New("no opensea client")
|
||||
if m.collectiblesManager == nil {
|
||||
return nil, errors.New("no collectibles manager")
|
||||
}
|
||||
|
||||
ownedERC721Tokens := make(map[uint64]map[gethcommon.Address]map[string][]opensea.Asset)
|
||||
ownedERC721Tokens := make(CollectiblesByChain)
|
||||
|
||||
for chainID, erc721Tokens := range tokenRequirements {
|
||||
|
||||
|
@ -2166,41 +2152,22 @@ func (m *Manager) GetOwnedERC721Tokens(walletAddresses []gethcommon.Address, tok
|
|||
continue
|
||||
}
|
||||
|
||||
client, err := m.openseaClientBuilder.NewOpenseaClient(chainID, m.walletConfig.OpenseaAPIKey, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contractAddresses := make([]gethcommon.Address, 0)
|
||||
for contractAddress := range erc721Tokens {
|
||||
contractAddresses = append(contractAddresses, gethcommon.HexToAddress(contractAddress))
|
||||
}
|
||||
|
||||
if _, exists := ownedERC721Tokens[chainID]; !exists {
|
||||
ownedERC721Tokens[chainID] = make(map[gethcommon.Address]map[string][]opensea.Asset)
|
||||
ownedERC721Tokens[chainID] = make(map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress)
|
||||
}
|
||||
|
||||
for _, owner := range walletAddresses {
|
||||
assets, err := client.FetchAllAssetsByOwnerAndContractAddress(owner, contractAddresses, "", 5)
|
||||
balances, err := m.collectiblesManager.FetchBalancesByOwnerAndContractAddress(chainID, owner, contractAddresses)
|
||||
if err != nil {
|
||||
m.logger.Info("couldn't fetch owner assets", zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(assets.Assets) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, exists := ownedERC721Tokens[chainID][owner]; !exists {
|
||||
ownedERC721Tokens[chainID][owner] = make(map[string][]opensea.Asset, 0)
|
||||
}
|
||||
|
||||
for _, asset := range assets.Assets {
|
||||
if _, exists := ownedERC721Tokens[chainID][owner][asset.Contract.Address]; !exists {
|
||||
ownedERC721Tokens[chainID][owner][asset.Contract.Address] = make([]opensea.Asset, 0)
|
||||
}
|
||||
ownedERC721Tokens[chainID][owner][asset.Contract.Address] = append(ownedERC721Tokens[chainID][owner][asset.Contract.Address], asset)
|
||||
}
|
||||
ownedERC721Tokens[chainID][owner] = balances
|
||||
}
|
||||
}
|
||||
return ownedERC721Tokens, nil
|
||||
|
|
|
@ -14,14 +14,14 @@ import (
|
|||
|
||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/status-im/status-go/appdatabase"
|
||||
"github.com/status-im/status-go/eth-node/types"
|
||||
userimages "github.com/status-im/status-go/images"
|
||||
"github.com/status-im/status-go/params"
|
||||
"github.com/status-im/status-go/protocol/requests"
|
||||
"github.com/status-im/status-go/protocol/transport"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/opensea"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
_ "github.com/mutecomm/go-sqlcipher/v4" // require go-sqlcipher that overrides default implementation
|
||||
|
@ -63,6 +63,17 @@ func intToBig(n int64) *hexutil.Big {
|
|||
return (*hexutil.Big)(big.NewInt(n))
|
||||
}
|
||||
|
||||
func uintToDecBig(n uint64) *bigint.BigInt {
|
||||
return &bigint.BigInt{Int: big.NewInt(int64(n))}
|
||||
}
|
||||
|
||||
func tokenBalance(tokenID uint64, balance uint64) thirdparty.TokenBalance {
|
||||
return thirdparty.TokenBalance{
|
||||
TokenID: uintToDecBig(tokenID),
|
||||
Balance: uintToDecBig(balance),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) getHistoryTasksCount() int {
|
||||
// sync.Map doesn't have a Len function, so we need to count manually
|
||||
count := 0
|
||||
|
@ -73,11 +84,26 @@ func (s *ManagerSuite) getHistoryTasksCount() int {
|
|||
return count
|
||||
}
|
||||
|
||||
type openseaClientTestBuilder struct {
|
||||
type testCollectiblesManager struct {
|
||||
response map[uint64]map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress
|
||||
}
|
||||
|
||||
func (b *openseaClientTestBuilder) NewOpenseaClient(chainID uint64, apiKey string, feed *event.Feed) (openseaClient, error) {
|
||||
return opensea.NewOpenseaClient(chainID, apiKey, nil)
|
||||
func (m *testCollectiblesManager) setResponse(chainID uint64, walletAddress gethcommon.Address, contractAddress gethcommon.Address, balances []thirdparty.TokenBalance) {
|
||||
if m.response == nil {
|
||||
m.response = make(map[uint64]map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress)
|
||||
}
|
||||
if m.response[chainID] == nil {
|
||||
m.response[chainID] = make(map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress)
|
||||
}
|
||||
if m.response[chainID][walletAddress] == nil {
|
||||
m.response[chainID][walletAddress] = make(thirdparty.TokenBalancesPerContractAddress)
|
||||
}
|
||||
|
||||
m.response[chainID][walletAddress][contractAddress] = balances
|
||||
}
|
||||
|
||||
func (m *testCollectiblesManager) FetchBalancesByOwnerAndContractAddress(chainID uint64, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) {
|
||||
return m.response[chainID][ownerAddress], nil
|
||||
}
|
||||
|
||||
type testTokenManager struct {
|
||||
|
@ -110,7 +136,7 @@ func (m *testTokenManager) GetBalancesByChain(ctx context.Context, accounts, tok
|
|||
return m.response, nil
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) setupManagerForTokenPermissions() (*Manager, *testTokenManager) {
|
||||
func (s *ManagerSuite) setupManagerForTokenPermissions() (*Manager, *testCollectiblesManager, *testTokenManager) {
|
||||
db, err := appdatabase.InitializeDB(sqlite.InMemoryPath, "", sqlite.ReducedKDFIterationsNumber)
|
||||
s.NoError(err, "creating sqlite db instance")
|
||||
err = sqlite.Migrate(db)
|
||||
|
@ -120,13 +146,14 @@ func (s *ManagerSuite) setupManagerForTokenPermissions() (*Manager, *testTokenMa
|
|||
s.Require().NoError(err)
|
||||
s.Require().NoError(err)
|
||||
|
||||
cm := &testCollectiblesManager{}
|
||||
tm := &testTokenManager{}
|
||||
|
||||
options := []ManagerOption{
|
||||
WithWalletConfig(¶ms.WalletConfig{
|
||||
OpenseaAPIKey: "some-key",
|
||||
}),
|
||||
WithOpenseaClientBuilder(&openseaClientTestBuilder{}),
|
||||
WithCollectiblesManager(cm),
|
||||
WithTokenManager(tm),
|
||||
}
|
||||
|
||||
|
@ -134,11 +161,11 @@ func (s *ManagerSuite) setupManagerForTokenPermissions() (*Manager, *testTokenMa
|
|||
s.Require().NoError(err)
|
||||
s.Require().NoError(m.Start())
|
||||
|
||||
return m, tm
|
||||
return m, cm, tm
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) TestRetrieveTokens() {
|
||||
m, tm := s.setupManagerForTokenPermissions()
|
||||
m, _, tm := s.setupManagerForTokenPermissions()
|
||||
|
||||
var chainID uint64 = 5
|
||||
contractAddresses := make(map[uint64]string)
|
||||
|
@ -185,6 +212,56 @@ func (s *ManagerSuite) TestRetrieveTokens() {
|
|||
s.Require().False(resp.Satisfied)
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) TestRetrieveCollectibles() {
|
||||
m, cm, _ := s.setupManagerForTokenPermissions()
|
||||
|
||||
var chainID uint64 = 5
|
||||
contractAddresses := make(map[uint64]string)
|
||||
contractAddresses[chainID] = "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a"
|
||||
|
||||
tokenID := uint64(10)
|
||||
var tokenBalances []thirdparty.TokenBalance
|
||||
|
||||
var tokenCriteria = []*protobuf.TokenCriteria{
|
||||
&protobuf.TokenCriteria{
|
||||
ContractAddresses: contractAddresses,
|
||||
TokenIds: []uint64{tokenID},
|
||||
Type: protobuf.CommunityTokenType_ERC721,
|
||||
},
|
||||
}
|
||||
|
||||
var permissions = []*protobuf.CommunityTokenPermission{
|
||||
&protobuf.CommunityTokenPermission{
|
||||
Id: "some-id",
|
||||
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
||||
TokenCriteria: tokenCriteria,
|
||||
},
|
||||
}
|
||||
|
||||
accountChainIDsCombination := []*AccountChainIDsCombination{
|
||||
&AccountChainIDsCombination{
|
||||
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
||||
ChainIDs: []uint64{chainID},
|
||||
},
|
||||
}
|
||||
|
||||
// Set response to exactly the right one
|
||||
tokenBalances = []thirdparty.TokenBalance{tokenBalance(tokenID, 1)}
|
||||
cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances)
|
||||
resp, err := m.checkPermissionToJoin(permissions, accountChainIDsCombination, false)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(resp)
|
||||
s.Require().True(resp.Satisfied)
|
||||
|
||||
// Set balances to 0
|
||||
tokenBalances = []thirdparty.TokenBalance{}
|
||||
cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances)
|
||||
resp, err = m.checkPermissionToJoin(permissions, accountChainIDsCombination, false)
|
||||
s.Require().NoError(err)
|
||||
s.Require().NotNil(resp)
|
||||
s.Require().False(resp.Satisfied)
|
||||
}
|
||||
|
||||
func (s *ManagerSuite) TestCreateCommunity() {
|
||||
|
||||
request := &requests.CreateCommunity{
|
||||
|
@ -812,7 +889,7 @@ func (s *ManagerSuite) TestUnseedHistoryArchiveTorrent() {
|
|||
|
||||
func (s *ManagerSuite) TestCheckChannelPermissions_NoPermissions() {
|
||||
|
||||
m, tm := s.setupManagerForTokenPermissions()
|
||||
m, _, tm := s.setupManagerForTokenPermissions()
|
||||
|
||||
var chainID uint64 = 5
|
||||
contractAddresses := make(map[uint64]string)
|
||||
|
@ -841,7 +918,7 @@ func (s *ManagerSuite) TestCheckChannelPermissions_NoPermissions() {
|
|||
|
||||
func (s *ManagerSuite) TestCheckChannelPermissions_ViewOnlyPermissions() {
|
||||
|
||||
m, tm := s.setupManagerForTokenPermissions()
|
||||
m, _, tm := s.setupManagerForTokenPermissions()
|
||||
|
||||
var chainID uint64 = 5
|
||||
contractAddresses := make(map[uint64]string)
|
||||
|
@ -899,7 +976,7 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewOnlyPermissions() {
|
|||
|
||||
func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissions() {
|
||||
|
||||
m, tm := s.setupManagerForTokenPermissions()
|
||||
m, _, tm := s.setupManagerForTokenPermissions()
|
||||
|
||||
var chainID uint64 = 5
|
||||
contractAddresses := make(map[uint64]string)
|
||||
|
@ -958,7 +1035,7 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissions() {
|
|||
|
||||
func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissionsCombination() {
|
||||
|
||||
m, tm := s.setupManagerForTokenPermissions()
|
||||
m, _, tm := s.setupManagerForTokenPermissions()
|
||||
|
||||
var chainID uint64 = 5
|
||||
contractAddresses := make(map[uint64]string)
|
||||
|
@ -1032,7 +1109,7 @@ func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissionsCombina
|
|||
|
||||
func (s *ManagerSuite) TestCheckAllChannelsPermissions_EmptyPermissions() {
|
||||
|
||||
m, _ := s.setupManagerForTokenPermissions()
|
||||
m, _, _ := s.setupManagerForTokenPermissions()
|
||||
|
||||
createRequest := &requests.CreateCommunity{
|
||||
Name: "channel permission community",
|
||||
|
@ -1079,7 +1156,7 @@ func (s *ManagerSuite) TestCheckAllChannelsPermissions_EmptyPermissions() {
|
|||
|
||||
func (s *ManagerSuite) TestCheckAllChannelsPermissions() {
|
||||
|
||||
m, tm := s.setupManagerForTokenPermissions()
|
||||
m, _, tm := s.setupManagerForTokenPermissions()
|
||||
|
||||
var chatID1 string
|
||||
var chatID2 string
|
||||
|
|
|
@ -422,10 +422,19 @@ func NewMessenger(
|
|||
|
||||
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{
|
||||
communities.WithAccountManager(accountsManager),
|
||||
}
|
||||
|
||||
if walletAPI != nil {
|
||||
managerOptions = append(managerOptions, communities.WithCollectiblesManager(walletAPI))
|
||||
}
|
||||
|
||||
if c.tokenManager != nil {
|
||||
managerOptions = append(managerOptions, communities.WithTokenManager(c.tokenManager))
|
||||
} else if c.rpcClient != nil {
|
||||
|
@ -545,7 +554,7 @@ func NewMessenger(
|
|||
messenger.mentionsManager = NewMentionManager(messenger)
|
||||
|
||||
if c.walletService != nil {
|
||||
messenger.walletAPI = wallet.NewAPI(c.walletService)
|
||||
messenger.walletAPI = walletAPI
|
||||
}
|
||||
|
||||
if c.outputMessagesCSV {
|
||||
|
|
|
@ -338,6 +338,11 @@ func (api *API) GetCollectibleOwnersByContractAddress(chainID uint64, contractAd
|
|||
return api.s.collectiblesManager.FetchNFTOwnersByContractAddress(chainID, contractAddress)
|
||||
}
|
||||
|
||||
func (api *API) FetchBalancesByOwnerAndContractAddress(chainID uint64, ownerAddress common.Address, contractAddresses []common.Address) (thirdparty.TokenBalancesPerContractAddress, error) {
|
||||
log.Debug("call to FetchBalancesByOwnerAndContractAddress")
|
||||
return api.s.collectiblesManager.FetchBalancesByOwnerAndContractAddress(chainID, ownerAddress, contractAddresses)
|
||||
}
|
||||
|
||||
func (api *API) AddEthereumChain(ctx context.Context, network params.Network) error {
|
||||
log.Debug("call to AddEthereumChain")
|
||||
return api.s.rpcClient.NetworkManager.Upsert(&network)
|
||||
|
|
|
@ -3,6 +3,7 @@ package collectibles
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -14,6 +15,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/status-im/status-go/contracts/collectibles"
|
||||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/wallet/bigint"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/opensea"
|
||||
)
|
||||
|
@ -121,6 +123,48 @@ func (o *Manager) FetchAllAssetsByOwnerAndCollection(chainID uint64, owner commo
|
|||
return assetContainer, nil
|
||||
}
|
||||
|
||||
// Need to combine different providers to support all needed ChainIDs
|
||||
func (o *Manager) FetchBalancesByOwnerAndContractAddress(chainID uint64, ownerAddress common.Address, contractAddresses []common.Address) (thirdparty.TokenBalancesPerContractAddress, error) {
|
||||
ret := make(thirdparty.TokenBalancesPerContractAddress)
|
||||
|
||||
for _, contractAddress := range contractAddresses {
|
||||
ret[contractAddress] = make([]thirdparty.TokenBalance, 0)
|
||||
}
|
||||
|
||||
// Try with more direct endpoint first (OpenSea)
|
||||
assetsContainer, err := o.FetchAllAssetsByOwnerAndContractAddress(chainID, ownerAddress, contractAddresses, "", 0)
|
||||
if err == opensea.ErrChainIDNotSupported {
|
||||
// Use contract ownership providers
|
||||
for _, contractAddress := range contractAddresses {
|
||||
ownership, err := o.FetchNFTOwnersByContractAddress(chainID, contractAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, nftOwner := range ownership.Owners {
|
||||
if nftOwner.OwnerAddress == ownerAddress {
|
||||
ret[contractAddress] = nftOwner.TokenBalances
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if err == nil {
|
||||
// OpenSea could provide
|
||||
for _, asset := range assetsContainer.Assets {
|
||||
contractAddress := common.HexToAddress(asset.Contract.Address)
|
||||
balance := thirdparty.TokenBalance{
|
||||
TokenID: asset.TokenID,
|
||||
Balance: &bigint.BigInt{Int: big.NewInt(1)},
|
||||
}
|
||||
ret[contractAddress] = append(ret[contractAddress], balance)
|
||||
}
|
||||
} else {
|
||||
// OpenSea could have provided, but returned error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (o *Manager) FetchAllAssetsByOwnerAndContractAddress(chainID uint64, owner common.Address, contractAddresses []common.Address, cursor string, limit int) (*opensea.AssetContainer, error) {
|
||||
client, err := opensea.NewOpenseaClient(chainID, o.openseaAPIKey, o.walletFeed)
|
||||
if err != nil {
|
||||
|
|
|
@ -67,6 +67,8 @@ type TokenBalance struct {
|
|||
Balance *bigint.BigInt `json:"balance"`
|
||||
}
|
||||
|
||||
type TokenBalancesPerContractAddress = map[common.Address][]TokenBalance
|
||||
|
||||
type NFTOwner struct {
|
||||
OwnerAddress common.Address `json:"ownerAddress"`
|
||||
TokenBalances []TokenBalance `json:"tokenBalances"`
|
||||
|
|
Loading…
Reference in New Issue