diff --git a/services/wallet/service.go b/services/wallet/service.go index 6e4be61e5..dfd6b6ce3 100644 --- a/services/wallet/service.go +++ b/services/wallet/service.go @@ -126,6 +126,7 @@ func NewService( } collectionDataProviders := []thirdparty.CollectionDataProvider{ + openseaV2Client, alchemyClient, } diff --git a/services/wallet/thirdparty/opensea/client_v2.go b/services/wallet/thirdparty/opensea/client_v2.go index a57111902..5e8ed8352 100644 --- a/services/wallet/thirdparty/opensea/client_v2.go +++ b/services/wallet/thirdparty/opensea/client_v2.go @@ -231,3 +231,79 @@ func (o *ClientV2) fetchDetailedAssets(uniqueIDs []thirdparty.CollectibleUniqueI return assets, nil } + +func (o *ClientV2) fetchContractDataByContractID(id thirdparty.ContractID) (*ContractData, error) { + path := fmt.Sprintf("chain/%s/contract/%s", chainIDToChainString(id.ChainID), id.Address.String()) + url, err := o.urlGetter(id.ChainID, path) + if err != nil { + return nil, err + } + + body, err := o.client.doGetRequest(url, o.apiKey) + if err != nil { + o.connectionStatus.SetIsConnected(false) + return nil, err + } + o.connectionStatus.SetIsConnected(true) + + // if Json is not returned there must be an error + if !json.Valid(body) { + return nil, fmt.Errorf("invalid json: %s", string(body)) + } + + contract := ContractData{} + err = json.Unmarshal(body, &contract) + if err != nil { + return nil, err + } + + return &contract, nil +} + +func (o *ClientV2) fetchCollectionDataBySlug(chainID walletCommon.ChainID, slug string) (*CollectionData, error) { + path := fmt.Sprintf("collections/%s", slug) + url, err := o.urlGetter(chainID, path) + if err != nil { + return nil, err + } + + body, err := o.client.doGetRequest(url, o.apiKey) + if err != nil { + o.connectionStatus.SetIsConnected(false) + return nil, err + } + o.connectionStatus.SetIsConnected(true) + + // if Json is not returned there must be an error + if !json.Valid(body) { + return nil, fmt.Errorf("invalid json: %s", string(body)) + } + + collection := CollectionData{} + err = json.Unmarshal(body, &collection) + if err != nil { + return nil, err + } + + return &collection, nil +} + +func (o *ClientV2) FetchCollectionsDataByContractID(contractIDs []thirdparty.ContractID) ([]thirdparty.CollectionData, error) { + ret := make([]thirdparty.CollectionData, 0, len(contractIDs)) + + for _, id := range contractIDs { + contractData, err := o.fetchContractDataByContractID(id) + if err != nil { + return nil, err + } + + collectionData, err := o.fetchCollectionDataBySlug(id.ChainID, contractData.Collection) + if err != nil { + return nil, err + } + + ret = append(ret, collectionData.toCommon(id)) + } + + return ret, nil +} diff --git a/services/wallet/thirdparty/opensea/types_v2.go b/services/wallet/thirdparty/opensea/types_v2.go index 1979574a0..1880d74b1 100644 --- a/services/wallet/thirdparty/opensea/types_v2.go +++ b/services/wallet/thirdparty/opensea/types_v2.go @@ -21,6 +21,7 @@ const ( arbitrumMainnetString = "arbitrum" optimismMainnetString = "optimism" ethereumGoerliString = "goerli" + ethereumSepoliaString = "sepolia" arbitrumGoerliString = "arbitrum_goerli" optimismGoerliString = "optimism_goerli" ) @@ -38,6 +39,8 @@ func chainIDToChainString(chainID walletCommon.ChainID) string { chainString = optimismMainnetString case walletCommon.EthereumGoerli: chainString = ethereumGoerliString + case walletCommon.EthereumSepolia: + chainString = ethereumSepoliaString case walletCommon.ArbitrumGoerli: chainString = arbitrumGoerliString case walletCommon.OptimismGoerli: @@ -74,6 +77,7 @@ type DetailedNFT struct { Name string `json:"name"` Description string `json:"description"` ImageURL string `json:"image_url"` + AnimationURL string `json:"animation_url"` MetadataURL string `json:"metadata_url"` Owners []OwnerV2 `json:"owners"` Traits []TraitV2 `json:"traits"` @@ -113,6 +117,28 @@ type TraitV2 struct { Value TraitValue `json:"value"` } +type ContractData struct { + Address common.Address `json:"address"` + Chain string `json:"chain"` + Collection string `json:"collection"` + ContractStandard string `json:"contract_standard"` + Name string `json:"name"` +} + +type ContractID struct { + Address common.Address `json:"address"` + Chain string `json:"chain"` +} + +type CollectionData struct { + Collection string `json:"collection"` + Name string `json:"name"` + Description string `json:"description"` + Owner common.Address `json:"owner"` + ImageURL string `json:"image_url"` + Contracts []ContractID `json:"contracts"` +} + func (c *NFT) id(chainID walletCommon.ChainID) thirdparty.CollectibleUniqueID { return thirdparty.CollectibleUniqueID{ ContractID: thirdparty.ContractID{ @@ -176,7 +202,7 @@ func (c *DetailedNFT) toCollectiblesData(chainID walletCommon.ChainID) thirdpart Name: c.Name, Description: c.Description, ImageURL: c.ImageURL, - AnimationURL: c.ImageURL, + AnimationURL: c.AnimationURL, Traits: openseaV2ToCollectibleTraits(c.Traits), TokenURI: c.MetadataURL, } @@ -188,3 +214,14 @@ func (c *DetailedNFT) toCommon(chainID walletCommon.ChainID) thirdparty.FullColl CollectionData: nil, } } + +func (c *CollectionData) toCommon(id thirdparty.ContractID) thirdparty.CollectionData { + ret := thirdparty.CollectionData{ + ID: id, + Provider: OpenseaV2ID, + Name: c.Name, + Slug: c.Collection, + ImageURL: c.ImageURL, + } + return ret +}