status-go/services/wallet/opensea.go

160 lines
3.6 KiB
Go

package wallet
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
const AssetLimit = 50
const CollectionLimit = 300
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 int:
*st = TraitValue(strconv.Itoa(v))
case string:
*st = TraitValue(v)
}
return nil
}
type OpenseaAssetContainer struct {
Assets []OpenseaAsset `json:"assets"`
}
type OpenseaAssetCollection struct {
Name string `json:"name"`
}
type OpenseaContract struct {
Address string `json:"address"`
}
type OpenseaTrait struct {
TraitType string `json:"trait_type"`
Value TraitValue `json:"value"`
DisplayType string `json:"display_type"`
}
type OpenseaAsset struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Permalink string `json:"permalink"`
ImageThumbnailURL string `json:"image_thumbnail_url"`
ImageURL string `json:"image_url"`
Contract OpenseaContract `json:"asset_contract"`
Collection OpenseaAssetCollection `json:"collection"`
Traits []OpenseaTrait `json:"traits"`
}
type OpenseaCollection struct {
Name string `json:"name"`
Slug string `json:"slug"`
ImageURL string `json:"image_url"`
OwnedAssetCount int `json:"owned_asset_count"`
}
type OpenseaClient struct {
client *http.Client
url string
}
// new opensea client.
func newOpenseaClient() *OpenseaClient {
client := &http.Client{
Timeout: time.Second * 5,
}
return &OpenseaClient{client: client, url: "https://api.opensea.io/api/v1"}
}
func (o *OpenseaClient) fetchAllCollectionsByOwner(owner common.Address) ([]OpenseaCollection, error) {
offset := 0
var collections []OpenseaCollection
for {
url := fmt.Sprintf("%s/collections?asset_owner=%s&offset=%d&limit=%d", o.url, owner, offset, CollectionLimit)
body, err := o.doOpenseaRequest(url)
if err != nil {
return nil, err
}
var tmp []OpenseaCollection
err = json.Unmarshal(body, &tmp)
if err != nil {
return nil, err
}
collections = append(collections, tmp...)
if len(tmp) < CollectionLimit {
break
}
}
return collections, nil
}
func (o *OpenseaClient) fetchAllAssetsByOwnerAndCollection(owner common.Address, collectionSlug string, limit int) ([]OpenseaAsset, error) {
offset := 0
var assets []OpenseaAsset
for {
url := fmt.Sprintf("%s/assets?owner=%s&collection=%s&offset=%d&limit=%d", o.url, owner, collectionSlug, offset, AssetLimit)
body, err := o.doOpenseaRequest(url)
if err != nil {
return nil, err
}
container := OpenseaAssetContainer{}
err = json.Unmarshal(body, &container)
if err != nil {
return nil, err
}
assets = append(assets, container.Assets...)
if len(container.Assets) < AssetLimit {
break
}
if len(assets) >= limit {
break
}
}
return assets, nil
}
func (o *OpenseaClient) doOpenseaRequest(url string) ([]byte, error) {
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
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)
}
}()
body, err := ioutil.ReadAll(resp.Body)
return body, err
}