diff --git a/services/wallet/service.go b/services/wallet/service.go index 18f680bcc..62874d792 100644 --- a/services/wallet/service.go +++ b/services/wallet/service.go @@ -28,7 +28,6 @@ import ( "github.com/status-im/status-go/services/wallet/thirdparty/alchemy" "github.com/status-im/status-go/services/wallet/thirdparty/coingecko" "github.com/status-im/status-go/services/wallet/thirdparty/cryptocompare" - "github.com/status-im/status-go/services/wallet/thirdparty/infura" "github.com/status-im/status-go/services/wallet/thirdparty/opensea" "github.com/status-im/status-go/services/wallet/token" "github.com/status-im/status-go/services/wallet/transfer" @@ -110,32 +109,27 @@ func NewService( openseaHTTPClient := opensea.NewHTTPClient() openseaClient := opensea.NewClient(config.WalletConfig.OpenseaAPIKey, openseaHTTPClient) openseaV2Client := opensea.NewClientV2(config.WalletConfig.OpenseaAPIKey, openseaHTTPClient) - infuraClient := infura.NewClient(config.WalletConfig.InfuraAPIKey, config.WalletConfig.InfuraAPIKeySecret) alchemyClient := alchemy.NewClient(config.WalletConfig.AlchemyAPIKeys) // Try OpenSea, Infura, Alchemy in that order contractOwnershipProviders := []thirdparty.CollectibleContractOwnershipProvider{ - infuraClient, alchemyClient, } accountOwnershipProviders := []thirdparty.CollectibleAccountOwnershipProvider{ openseaClient, openseaV2Client, - infuraClient, alchemyClient, } collectibleDataProviders := []thirdparty.CollectibleDataProvider{ openseaClient, openseaV2Client, - infuraClient, alchemyClient, } collectionDataProviders := []thirdparty.CollectionDataProvider{ openseaClient, - infuraClient, alchemyClient, } diff --git a/services/wallet/thirdparty/infura/client.go b/services/wallet/thirdparty/infura/client.go deleted file mode 100644 index a1e66c368..000000000 --- a/services/wallet/thirdparty/infura/client.go +++ /dev/null @@ -1,275 +0,0 @@ -package infura - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" - walletCommon "github.com/status-im/status-go/services/wallet/common" - "github.com/status-im/status-go/services/wallet/connection" - "github.com/status-im/status-go/services/wallet/thirdparty" -) - -const baseURL = "https://nft.api.infura.io" - -type Client struct { - thirdparty.CollectibleContractOwnershipProvider - client *http.Client - apiKey string - apiKeySecret string - connectionStatus *connection.Status -} - -func NewClient(apiKey string, apiKeySecret string) *Client { - if apiKey == "" { - log.Warn("Infura API key not available") - } - if apiKeySecret == "" { - log.Warn("Infura API key secret not available") - } - - return &Client{ - client: &http.Client{Timeout: time.Minute}, - apiKey: apiKey, - connectionStatus: connection.NewStatus(), - } -} - -func (o *Client) doQuery(url string) (*http.Response, error) { - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return nil, err - } - - req.Header.Set("Content-Type", "application/json") - req.SetBasicAuth(o.apiKey, o.apiKeySecret) - - resp, err := o.client.Do(req) - - if err != nil { - return nil, err - } - - return resp, nil -} - -func (o *Client) ID() string { - return InfuraID -} - -func (o *Client) IsChainSupported(chainID walletCommon.ChainID) bool { - switch uint64(chainID) { - case walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet, walletCommon.EthereumGoerli, walletCommon.EthereumSepolia: - return true - } - return false -} - -func (o *Client) IsConnected() bool { - return o.connectionStatus.IsConnected() -} - -func (o *Client) FetchCollectibleOwnersByContractAddress(chainID walletCommon.ChainID, contractAddress common.Address) (*thirdparty.CollectibleContractOwnership, error) { - cursor := "" - ownersMap := make(map[common.Address][]CollectibleOwner) - - for { - url := fmt.Sprintf("%s/networks/%d/nfts/%s/owners", baseURL, chainID, contractAddress.String()) - - if cursor != "" { - url = url + "?cursor=" + cursor - } - - resp, err := o.doQuery(url) - if err != nil { - o.connectionStatus.SetIsConnected(false) - return nil, err - } - o.connectionStatus.SetIsConnected(true) - - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - var infuraOwnership CollectibleContractOwnership - err = json.Unmarshal(body, &infuraOwnership) - if err != nil { - return nil, err - } - - for _, infuraOwner := range infuraOwnership.Owners { - ownersMap[infuraOwner.OwnerAddress] = append(ownersMap[infuraOwner.OwnerAddress], infuraOwner) - } - - cursor = infuraOwnership.Cursor - - if cursor == "" { - break - } - } - - return infuraOwnershipToCommon(contractAddress, ownersMap) -} - -func (o *Client) FetchAllAssetsByOwner(chainID walletCommon.ChainID, owner common.Address, cursor string, limit int) (*thirdparty.FullCollectibleDataContainer, error) { - queryParams := url.Values{} - - if len(cursor) > 0 { - queryParams["cursor"] = []string{cursor} - } - - return o.fetchOwnedAssets(chainID, owner, queryParams, limit) -} - -func (o *Client) FetchAllAssetsByOwnerAndContractAddress(chainID walletCommon.ChainID, owner common.Address, contractAddresses []common.Address, cursor string, limit int) (*thirdparty.FullCollectibleDataContainer, error) { - queryParams := url.Values{} - - if len(cursor) > 0 { - queryParams["cursor"] = []string{cursor} - } - - for _, contractAddress := range contractAddresses { - queryParams.Add("tokenAddress", contractAddress.String()) - } - - return o.fetchOwnedAssets(chainID, owner, queryParams, limit) -} - -func (o *Client) fetchOwnedAssets(chainID walletCommon.ChainID, owner common.Address, queryParams url.Values, limit int) (*thirdparty.FullCollectibleDataContainer, error) { - assets := new(thirdparty.FullCollectibleDataContainer) - - if len(queryParams["cursor"]) > 0 { - assets.PreviousCursor = queryParams["cursor"][0] - } - assets.Provider = o.ID() - - for { - url := fmt.Sprintf("%s/networks/%d/accounts/%s/assets/nfts?%s", baseURL, chainID, owner.String(), queryParams.Encode()) - - resp, err := o.doQuery(url) - if err != nil { - o.connectionStatus.SetIsConnected(false) - return nil, err - } - o.connectionStatus.SetIsConnected(true) - - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - // if Json is not returned there must be an error - if !json.Valid(body) { - return nil, fmt.Errorf("invalid json: %s", string(body)) - } - - container := NFTList{} - err = json.Unmarshal(body, &container) - if err != nil { - return nil, err - } - - assets.Items = append(assets.Items, container.toCommon()...) - assets.NextCursor = container.Cursor - - if len(assets.NextCursor) == 0 { - break - } - - queryParams["cursor"] = []string{assets.NextCursor} - - if limit != thirdparty.FetchNoLimit && len(assets.Items) >= limit { - break - } - } - - return assets, nil -} - -func (o *Client) FetchAssetsByCollectibleUniqueID(uniqueIDs []thirdparty.CollectibleUniqueID) ([]thirdparty.FullCollectibleData, error) { - ret := make([]thirdparty.FullCollectibleData, 0, len(uniqueIDs)) - - for _, id := range uniqueIDs { - url := fmt.Sprintf("%s/networks/%d/nfts/%s/tokens/%s", baseURL, id.ContractID.ChainID, id.ContractID.Address.String(), id.TokenID.String()) - - resp, err := o.doQuery(url) - if err != nil { - o.connectionStatus.SetIsConnected(false) - return nil, err - } - o.connectionStatus.SetIsConnected(true) - - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - // if Json is not returned there must be an error - if !json.Valid(body) { - return nil, fmt.Errorf("invalid json: %s", string(body)) - } - - asset := Asset{} - err = json.Unmarshal(body, &asset) - if err != nil { - return nil, err - } - - item := asset.toCommon(id) - - ret = append(ret, item) - } - - return ret, nil -} - -func (o *Client) FetchCollectionsDataByContractID(contractIDs []thirdparty.ContractID) ([]thirdparty.CollectionData, error) { - ret := make([]thirdparty.CollectionData, 0, len(contractIDs)) - - for _, id := range contractIDs { - url := fmt.Sprintf("%s/networks/%d/nfts/%s", baseURL, id.ChainID, id.Address.String()) - - resp, err := o.doQuery(url) - if err != nil { - o.connectionStatus.SetIsConnected(false) - return nil, err - } - o.connectionStatus.SetIsConnected(true) - - defer resp.Body.Close() - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - // if Json is not returned there must be an error - if !json.Valid(body) { - return nil, fmt.Errorf("invalid json: %s", string(body)) - } - - contract := ContractMetadata{} - err = json.Unmarshal(body, &contract) - if err != nil { - return nil, err - } - - item := contract.toCommon(id) - - ret = append(ret, item) - } - - return ret, nil -} diff --git a/services/wallet/thirdparty/infura/types.go b/services/wallet/thirdparty/infura/types.go deleted file mode 100644 index bceceda87..000000000 --- a/services/wallet/thirdparty/infura/types.go +++ /dev/null @@ -1,188 +0,0 @@ -package infura - -import ( - "encoding/json" - "strconv" - "strings" - - "github.com/ethereum/go-ethereum/common" - - "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" - - "golang.org/x/text/cases" - "golang.org/x/text/language" -) - -const InfuraID = "infura" - -func chainStringToChainID(chainString string) walletCommon.ChainID { - chainID := walletCommon.UnknownChainID - switch chainString { - case "ETHEREUM": - chainID = walletCommon.EthereumMainnet - case "ARBITRUM": - chainID = walletCommon.ArbitrumMainnet - case "GOERLI": - chainID = walletCommon.EthereumGoerli - case "SEPOLIA": - chainID = walletCommon.EthereumSepolia - } - return walletCommon.ChainID(chainID) -} - -type CollectibleOwner struct { - ContractAddress common.Address `json:"tokenAddress"` - TokenID *bigint.BigInt `json:"tokenId"` - Amount *bigint.BigInt `json:"amount"` - OwnerAddress common.Address `json:"ownerOf"` -} - -type CollectibleContractOwnership struct { - Owners []CollectibleOwner `json:"owners"` - Network string `json:"network"` - Cursor string `json:"cursor"` -} - -func infuraOwnershipToCommon(contractAddress common.Address, ownersMap map[common.Address][]CollectibleOwner) (*thirdparty.CollectibleContractOwnership, error) { - owners := make([]thirdparty.CollectibleOwner, 0, len(ownersMap)) - - for ownerAddress, ownerTokens := range ownersMap { - tokenBalances := make([]thirdparty.TokenBalance, 0, len(ownerTokens)) - - for _, token := range ownerTokens { - tokenBalances = append(tokenBalances, thirdparty.TokenBalance{ - TokenID: token.TokenID, - Balance: token.Amount, - }) - } - - owners = append(owners, thirdparty.CollectibleOwner{ - OwnerAddress: ownerAddress, - TokenBalances: tokenBalances, - }) - } - - ownership := thirdparty.CollectibleContractOwnership{ - ContractAddress: contractAddress, - Owners: owners, - } - - return &ownership, nil -} - -type AttributeValue string - -func (st *AttributeValue) UnmarshalJSON(b []byte) error { - var item interface{} - if err := json.Unmarshal(b, &item); err != nil { - return err - } - - switch v := item.(type) { - case float64: - *st = AttributeValue(strconv.FormatFloat(v, 'f', 2, 64)) - case int: - *st = AttributeValue(strconv.Itoa(v)) - case string: - *st = AttributeValue(v) - } - return nil -} - -type Attribute struct { - TraitType string `json:"trait_type"` - Value AttributeValue `json:"value"` -} - -type AssetMetadata struct { - Name string `json:"name"` - Description string `json:"description"` - Permalink string `json:"permalink"` - ImageURL string `json:"image"` - AnimationURL string `json:"animation_url"` - Attributes []Attribute `json:"attributes"` -} - -type ContractMetadata struct { - ContractAddress string `json:"contract"` - Name string `json:"name"` - Symbol string `json:"symbol"` - TokenType string `json:"tokenType"` -} - -type Asset struct { - ContractAddress common.Address `json:"contract"` - TokenID *bigint.BigInt `json:"tokenId"` - Metadata AssetMetadata `json:"metadata"` -} - -type NFTList struct { - Total *bigint.BigInt `json:"total"` - PageNumber int `json:"pageNumber"` - PageSize int `json:"pageSize"` - Network string `json:"network"` - Account string `json:"account"` - Cursor string `json:"cursor"` - Assets []Asset `json:"assets"` -} - -func (c *Asset) toCollectiblesData(id thirdparty.CollectibleUniqueID) thirdparty.CollectibleData { - return thirdparty.CollectibleData{ - ID: id, - Provider: InfuraID, - Name: c.Metadata.Name, - Description: c.Metadata.Description, - Permalink: c.Metadata.Permalink, - ImageURL: c.Metadata.ImageURL, - AnimationURL: c.Metadata.AnimationURL, - Traits: infuraToCollectibleTraits(c.Metadata.Attributes), - } -} - -func (c *Asset) toCommon(id thirdparty.CollectibleUniqueID) thirdparty.FullCollectibleData { - return thirdparty.FullCollectibleData{ - CollectibleData: c.toCollectiblesData(id), - CollectionData: nil, - } -} - -func (l *NFTList) toCommon() []thirdparty.FullCollectibleData { - ret := make([]thirdparty.FullCollectibleData, 0, len(l.Assets)) - for _, asset := range l.Assets { - id := thirdparty.CollectibleUniqueID{ - ContractID: thirdparty.ContractID{ - ChainID: chainStringToChainID(l.Network), - Address: asset.ContractAddress, - }, - TokenID: asset.TokenID, - } - item := asset.toCommon(id) - ret = append(ret, item) - } - return ret -} - -func infuraToCollectibleTraits(attributes []Attribute) []thirdparty.CollectibleTrait { - ret := make([]thirdparty.CollectibleTrait, 0, len(attributes)) - caser := cases.Title(language.Und, cases.NoLower) - for _, orig := range attributes { - dest := thirdparty.CollectibleTrait{ - TraitType: strings.Replace(orig.TraitType, "_", " ", 1), - Value: caser.String(string(orig.Value)), - } - - ret = append(ret, dest) - } - return ret -} - -func (c *ContractMetadata) toCommon(id thirdparty.ContractID) thirdparty.CollectionData { - return thirdparty.CollectionData{ - ID: id, - Provider: InfuraID, - Name: c.Name, - Traits: make(map[string]thirdparty.CollectionTrait, 0), - } -}