318
vendor/github.com/status-im/status-go/services/wallet/thirdparty/opensea/client_v2.go
generated
vendored
Normal file
318
vendor/github.com/status-im/status-go/services/wallet/thirdparty/opensea/client_v2.go
generated
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
package opensea
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"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 assetLimitV2 = 50
|
||||
|
||||
func getV2BaseURL(chainID walletCommon.ChainID) (string, error) {
|
||||
switch uint64(chainID) {
|
||||
case walletCommon.EthereumMainnet, walletCommon.ArbitrumMainnet, walletCommon.OptimismMainnet:
|
||||
return "https://api.opensea.io/v2", nil
|
||||
case walletCommon.EthereumSepolia, walletCommon.ArbitrumSepolia, walletCommon.OptimismSepolia:
|
||||
return "https://testnets-api.opensea.io/v2", nil
|
||||
}
|
||||
|
||||
return "", thirdparty.ErrChainIDNotSupported
|
||||
}
|
||||
|
||||
func (o *ClientV2) ID() string {
|
||||
return OpenseaV2ID
|
||||
}
|
||||
|
||||
func (o *ClientV2) IsChainSupported(chainID walletCommon.ChainID) bool {
|
||||
_, err := getV2BaseURL(chainID)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (o *ClientV2) IsConnected() bool {
|
||||
return o.connectionStatus.IsConnected()
|
||||
}
|
||||
|
||||
func getV2URL(chainID walletCommon.ChainID, path string) (string, error) {
|
||||
baseURL, err := getV2BaseURL(chainID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/%s", baseURL, path), nil
|
||||
}
|
||||
|
||||
type ClientV2 struct {
|
||||
client *HTTPClient
|
||||
apiKey string
|
||||
connectionStatus *connection.Status
|
||||
urlGetter urlGetter
|
||||
}
|
||||
|
||||
// new opensea v2 client.
|
||||
func NewClientV2(apiKey string, httpClient *HTTPClient) *ClientV2 {
|
||||
if apiKey == "" {
|
||||
log.Warn("OpenseaV2 API key not available")
|
||||
}
|
||||
|
||||
return &ClientV2{
|
||||
client: httpClient,
|
||||
apiKey: apiKey,
|
||||
connectionStatus: connection.NewStatus(),
|
||||
urlGetter: getV2URL,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ClientV2) FetchAllAssetsByOwnerAndContractAddress(ctx context.Context, chainID walletCommon.ChainID, owner common.Address, contractAddresses []common.Address, cursor string, limit int) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
// No dedicated endpoint to filter owned assets by contract address.
|
||||
// Will probably be available at some point, for now do the filtering ourselves.
|
||||
assets := new(thirdparty.FullCollectibleDataContainer)
|
||||
|
||||
// Build map for more efficient contract address check
|
||||
contractHashMap := make(map[string]bool)
|
||||
for _, contractAddress := range contractAddresses {
|
||||
contractID := thirdparty.ContractID{
|
||||
ChainID: chainID,
|
||||
Address: contractAddress,
|
||||
}
|
||||
contractHashMap[contractID.HashKey()] = true
|
||||
}
|
||||
|
||||
assets.PreviousCursor = cursor
|
||||
assets.NextCursor = cursor
|
||||
assets.Provider = o.ID()
|
||||
|
||||
for {
|
||||
assetsPage, err := o.FetchAllAssetsByOwner(ctx, chainID, owner, assets.NextCursor, assetLimitV2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, asset := range assetsPage.Items {
|
||||
if contractHashMap[asset.CollectibleData.ID.ContractID.HashKey()] {
|
||||
assets.Items = append(assets.Items, asset)
|
||||
}
|
||||
}
|
||||
|
||||
assets.NextCursor = assetsPage.NextCursor
|
||||
|
||||
if assets.NextCursor == "" {
|
||||
break
|
||||
}
|
||||
|
||||
if limit > thirdparty.FetchNoLimit && len(assets.Items) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
func (o *ClientV2) FetchAllAssetsByOwner(ctx context.Context, chainID walletCommon.ChainID, owner common.Address, cursor string, limit int) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
pathParams := []string{
|
||||
"chain", chainIDToChainString(chainID),
|
||||
"account", owner.String(),
|
||||
"nfts",
|
||||
}
|
||||
|
||||
queryParams := url.Values{}
|
||||
|
||||
return o.fetchAssets(ctx, chainID, pathParams, queryParams, limit, cursor)
|
||||
}
|
||||
|
||||
func (o *ClientV2) FetchAssetsByCollectibleUniqueID(ctx context.Context, uniqueIDs []thirdparty.CollectibleUniqueID) ([]thirdparty.FullCollectibleData, error) {
|
||||
return o.fetchDetailedAssets(ctx, uniqueIDs)
|
||||
}
|
||||
|
||||
func (o *ClientV2) fetchAssets(ctx context.Context, chainID walletCommon.ChainID, pathParams []string, queryParams url.Values, limit int, cursor string) (*thirdparty.FullCollectibleDataContainer, error) {
|
||||
assets := new(thirdparty.FullCollectibleDataContainer)
|
||||
|
||||
tmpLimit := assetLimitV2
|
||||
if limit > thirdparty.FetchNoLimit && limit < tmpLimit {
|
||||
tmpLimit = limit
|
||||
}
|
||||
queryParams["limit"] = []string{strconv.Itoa(tmpLimit)}
|
||||
|
||||
assets.PreviousCursor = cursor
|
||||
if cursor != "" {
|
||||
queryParams["next"] = []string{cursor}
|
||||
}
|
||||
assets.Provider = o.ID()
|
||||
|
||||
for {
|
||||
path := fmt.Sprintf("%s?%s", strings.Join(pathParams, "/"), queryParams.Encode())
|
||||
url, err := o.urlGetter(chainID, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := o.client.doGetRequest(ctx, url, o.apiKey)
|
||||
if err != nil {
|
||||
o.connectionStatus.SetIsConnected(false)
|
||||
return nil, err
|
||||
}
|
||||
o.connectionStatus.SetIsConnected(true)
|
||||
|
||||
// If body is empty, it means the account has no collectibles for this chain.
|
||||
// (Workaround implemented in http_client.go)
|
||||
if body == nil {
|
||||
assets.NextCursor = ""
|
||||
break
|
||||
}
|
||||
|
||||
// if Json is not returned there must be an error
|
||||
if !json.Valid(body) {
|
||||
return nil, fmt.Errorf("invalid json: %s", string(body))
|
||||
}
|
||||
|
||||
container := NFTContainer{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, asset := range container.NFTs {
|
||||
assets.Items = append(assets.Items, asset.toCommon(chainID))
|
||||
}
|
||||
assets.NextCursor = container.NextCursor
|
||||
|
||||
if assets.NextCursor == "" {
|
||||
break
|
||||
}
|
||||
|
||||
queryParams["next"] = []string{assets.NextCursor}
|
||||
|
||||
if limit > thirdparty.FetchNoLimit && len(assets.Items) >= limit {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
func (o *ClientV2) fetchDetailedAssets(ctx context.Context, uniqueIDs []thirdparty.CollectibleUniqueID) ([]thirdparty.FullCollectibleData, error) {
|
||||
assets := make([]thirdparty.FullCollectibleData, 0, len(uniqueIDs))
|
||||
|
||||
for _, id := range uniqueIDs {
|
||||
path := fmt.Sprintf("chain/%s/contract/%s/nfts/%s", chainIDToChainString(id.ContractID.ChainID), id.ContractID.Address.String(), id.TokenID.String())
|
||||
url, err := o.urlGetter(id.ContractID.ChainID, path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
body, err := o.client.doGetRequest(ctx, url, o.apiKey)
|
||||
if err != nil {
|
||||
if ctx.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))
|
||||
}
|
||||
|
||||
nftContainer := DetailedNFTContainer{}
|
||||
err = json.Unmarshal(body, &nftContainer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
assets = append(assets, nftContainer.NFT.toCommon(id.ContractID.ChainID))
|
||||
}
|
||||
|
||||
return assets, nil
|
||||
}
|
||||
|
||||
func (o *ClientV2) fetchContractDataByContractID(ctx context.Context, 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(ctx, url, o.apiKey)
|
||||
if err != nil {
|
||||
if ctx.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(ctx context.Context, 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(ctx, 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(ctx context.Context, contractIDs []thirdparty.ContractID) ([]thirdparty.CollectionData, error) {
|
||||
ret := make([]thirdparty.CollectionData, 0, len(contractIDs))
|
||||
|
||||
for _, id := range contractIDs {
|
||||
contractData, err := o.fetchContractDataByContractID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if contractData == nil || contractData.Collection == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
collectionData, err := o.fetchCollectionDataBySlug(ctx, id.ChainID, contractData.Collection)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret = append(ret, collectionData.toCommon(id, contractData.ContractStandard))
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
96
vendor/github.com/status-im/status-go/services/wallet/thirdparty/opensea/http_client.go
generated
vendored
Normal file
96
vendor/github.com/status-im/status-go/services/wallet/thirdparty/opensea/http_client.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
package opensea
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
const requestTimeout = 5 * time.Second
|
||||
const getRequestRetryMaxCount = 15
|
||||
const getRequestWaitTime = 300 * time.Millisecond
|
||||
|
||||
type HTTPClient struct {
|
||||
client *http.Client
|
||||
getRequestLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewHTTPClient() *HTTPClient {
|
||||
return &HTTPClient{
|
||||
client: &http.Client{
|
||||
Timeout: requestTimeout,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (o *HTTPClient) doGetRequest(ctx context.Context, url string, apiKey string) ([]byte, error) {
|
||||
// Ensure only one thread makes a request at a time
|
||||
o.getRequestLock.Lock()
|
||||
defer o.getRequestLock.Unlock()
|
||||
|
||||
retryCount := 0
|
||||
statusCode := http.StatusOK
|
||||
|
||||
// Try to do the request without an apiKey first
|
||||
tmpAPIKey := ""
|
||||
|
||||
for {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0")
|
||||
if len(tmpAPIKey) > 0 {
|
||||
req.Header.Set("X-API-KEY", tmpAPIKey)
|
||||
}
|
||||
|
||||
resp, err := o.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
log.Error("failed to close opensea request body", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
statusCode = resp.StatusCode
|
||||
switch resp.StatusCode {
|
||||
case http.StatusOK:
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
return body, err
|
||||
case http.StatusBadRequest:
|
||||
// The OpenSea v2 API will return error 400 if the account holds no collectibles on
|
||||
// the requested chain. This shouldn't be treated as an error, return an empty body.
|
||||
return nil, nil
|
||||
case http.StatusTooManyRequests:
|
||||
if retryCount < getRequestRetryMaxCount {
|
||||
// sleep and retry
|
||||
time.Sleep(getRequestWaitTime)
|
||||
retryCount++
|
||||
continue
|
||||
}
|
||||
// break and error
|
||||
case http.StatusForbidden:
|
||||
// Request requires an apiKey, set it and retry
|
||||
if tmpAPIKey == "" && apiKey != "" {
|
||||
tmpAPIKey = apiKey
|
||||
// sleep and retry
|
||||
time.Sleep(getRequestWaitTime)
|
||||
continue
|
||||
}
|
||||
// break and error
|
||||
default:
|
||||
// break and error
|
||||
}
|
||||
break
|
||||
}
|
||||
return nil, fmt.Errorf("unsuccessful request: %d %s", statusCode, http.StatusText(statusCode))
|
||||
}
|
||||
238
vendor/github.com/status-im/status-go/services/wallet/thirdparty/opensea/types_v2.go
generated
vendored
Normal file
238
vendor/github.com/status-im/status-go/services/wallet/thirdparty/opensea/types_v2.go
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
package opensea
|
||||
|
||||
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 (
|
||||
OpenseaV2ID = "openseaV2"
|
||||
ethereumMainnetString = "ethereum"
|
||||
arbitrumMainnetString = "arbitrum"
|
||||
optimismMainnetString = "optimism"
|
||||
ethereumSepoliaString = "sepolia"
|
||||
arbitrumSepoliaString = "arbitrum_sepolia"
|
||||
optimismSepoliaString = "optimism_sepolia"
|
||||
)
|
||||
|
||||
type urlGetter func(walletCommon.ChainID, string) (string, error)
|
||||
|
||||
func chainIDToChainString(chainID walletCommon.ChainID) string {
|
||||
chainString := ""
|
||||
switch uint64(chainID) {
|
||||
case walletCommon.EthereumMainnet:
|
||||
chainString = ethereumMainnetString
|
||||
case walletCommon.ArbitrumMainnet:
|
||||
chainString = arbitrumMainnetString
|
||||
case walletCommon.OptimismMainnet:
|
||||
chainString = optimismMainnetString
|
||||
case walletCommon.EthereumSepolia:
|
||||
chainString = ethereumSepoliaString
|
||||
case walletCommon.ArbitrumSepolia:
|
||||
chainString = arbitrumSepoliaString
|
||||
case walletCommon.OptimismSepolia:
|
||||
chainString = optimismSepoliaString
|
||||
}
|
||||
return chainString
|
||||
}
|
||||
|
||||
func openseaToContractType(contractType string) walletCommon.ContractType {
|
||||
switch contractType {
|
||||
case "cryptopunks", "erc721":
|
||||
return walletCommon.ContractTypeERC721
|
||||
case "erc1155":
|
||||
return walletCommon.ContractTypeERC1155
|
||||
default:
|
||||
return walletCommon.ContractTypeUnknown
|
||||
}
|
||||
}
|
||||
|
||||
type NFTContainer struct {
|
||||
NFTs []NFT `json:"nfts"`
|
||||
NextCursor string `json:"next"`
|
||||
}
|
||||
|
||||
type NFT struct {
|
||||
TokenID *bigint.BigInt `json:"identifier"`
|
||||
Collection string `json:"collection"`
|
||||
Contract common.Address `json:"contract"`
|
||||
TokenStandard string `json:"token_standard"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
ImageURL string `json:"image_url"`
|
||||
MetadataURL string `json:"metadata_url"`
|
||||
}
|
||||
|
||||
type DetailedNFTContainer struct {
|
||||
NFT DetailedNFT `json:"nft"`
|
||||
}
|
||||
|
||||
type DetailedNFT struct {
|
||||
TokenID *bigint.BigInt `json:"identifier"`
|
||||
Collection string `json:"collection"`
|
||||
Contract common.Address `json:"contract"`
|
||||
TokenStandard string `json:"token_standard"`
|
||||
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"`
|
||||
}
|
||||
|
||||
type OwnerV2 struct {
|
||||
Address common.Address `json:"address"`
|
||||
Quantity *bigint.BigInt `json:"quantity"`
|
||||
}
|
||||
|
||||
type TraitValue string
|
||||
|
||||
func (st *TraitValue) UnmarshalJSON(b []byte) error {
|
||||
var item interface{}
|
||||
if err := json.Unmarshal(b, &item); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch v := item.(type) {
|
||||
case float64:
|
||||
*st = TraitValue(strconv.FormatFloat(v, 'f', 2, 64))
|
||||
case int:
|
||||
*st = TraitValue(strconv.Itoa(v))
|
||||
case string:
|
||||
*st = TraitValue(v)
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type TraitV2 struct {
|
||||
TraitType string `json:"trait_type"`
|
||||
DisplayType string `json:"display_type"`
|
||||
MaxValue string `json:"max_value"`
|
||||
TraitCount int `json:"trait_count"`
|
||||
Order string `json:"order"`
|
||||
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{
|
||||
ChainID: chainID,
|
||||
Address: c.Contract,
|
||||
},
|
||||
TokenID: c.TokenID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *NFT) toCollectiblesData(chainID walletCommon.ChainID) thirdparty.CollectibleData {
|
||||
return thirdparty.CollectibleData{
|
||||
ID: c.id(chainID),
|
||||
ContractType: openseaToContractType(c.TokenStandard),
|
||||
Provider: OpenseaV2ID,
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
ImageURL: c.ImageURL,
|
||||
AnimationURL: c.ImageURL,
|
||||
Traits: make([]thirdparty.CollectibleTrait, 0),
|
||||
TokenURI: c.MetadataURL,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *NFT) toCommon(chainID walletCommon.ChainID) thirdparty.FullCollectibleData {
|
||||
return thirdparty.FullCollectibleData{
|
||||
CollectibleData: c.toCollectiblesData(chainID),
|
||||
CollectionData: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func openseaV2ToCollectibleTraits(traits []TraitV2) []thirdparty.CollectibleTrait {
|
||||
ret := make([]thirdparty.CollectibleTrait, 0, len(traits))
|
||||
caser := cases.Title(language.Und, cases.NoLower)
|
||||
for _, orig := range traits {
|
||||
dest := thirdparty.CollectibleTrait{
|
||||
TraitType: strings.Replace(orig.TraitType, "_", " ", 1),
|
||||
Value: caser.String(string(orig.Value)),
|
||||
DisplayType: orig.DisplayType,
|
||||
MaxValue: orig.MaxValue,
|
||||
}
|
||||
|
||||
ret = append(ret, dest)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *DetailedNFT) id(chainID walletCommon.ChainID) thirdparty.CollectibleUniqueID {
|
||||
return thirdparty.CollectibleUniqueID{
|
||||
ContractID: thirdparty.ContractID{
|
||||
ChainID: chainID,
|
||||
Address: c.Contract,
|
||||
},
|
||||
TokenID: c.TokenID,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DetailedNFT) toCollectiblesData(chainID walletCommon.ChainID) thirdparty.CollectibleData {
|
||||
return thirdparty.CollectibleData{
|
||||
ID: c.id(chainID),
|
||||
ContractType: openseaToContractType(c.TokenStandard),
|
||||
Provider: OpenseaV2ID,
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
ImageURL: c.ImageURL,
|
||||
AnimationURL: c.AnimationURL,
|
||||
Traits: openseaV2ToCollectibleTraits(c.Traits),
|
||||
TokenURI: c.MetadataURL,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DetailedNFT) toCommon(chainID walletCommon.ChainID) thirdparty.FullCollectibleData {
|
||||
return thirdparty.FullCollectibleData{
|
||||
CollectibleData: c.toCollectiblesData(chainID),
|
||||
CollectionData: nil,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CollectionData) toCommon(id thirdparty.ContractID, tokenStandard string) thirdparty.CollectionData {
|
||||
ret := thirdparty.CollectionData{
|
||||
ID: id,
|
||||
ContractType: openseaToContractType(tokenStandard),
|
||||
Provider: OpenseaV2ID,
|
||||
Name: c.Name,
|
||||
Slug: c.Collection,
|
||||
ImageURL: c.ImageURL,
|
||||
}
|
||||
return ret
|
||||
}
|
||||
Reference in New Issue
Block a user