feat: add coingecko fallback
This commit is contained in:
parent
999d8c0ee0
commit
e543fda4b5
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/status-im/status-go/services/wallet/currency"
|
||||
"github.com/status-im/status-go/services/wallet/history"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"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"
|
||||
)
|
||||
|
@ -295,24 +296,24 @@ func (api *API) GetCryptoOnRamps(ctx context.Context) ([]CryptoOnRamp, error) {
|
|||
return api.s.cryptoOnRampManager.Get()
|
||||
}
|
||||
|
||||
func (api *API) GetOpenseaCollectionsByOwner(ctx context.Context, chainID uint64, owner common.Address) ([]OpenseaCollection, error) {
|
||||
func (api *API) GetOpenseaCollectionsByOwner(ctx context.Context, chainID uint64, owner common.Address) ([]opensea.Collection, error) {
|
||||
log.Debug("call to get opensea collections")
|
||||
client, err := newOpenseaClient(chainID, api.s.openseaAPIKey)
|
||||
client, err := opensea.NewOpenseaClient(chainID, api.s.openseaAPIKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client.fetchAllCollectionsByOwner(owner)
|
||||
return client.FetchAllCollectionsByOwner(owner)
|
||||
}
|
||||
|
||||
func (api *API) GetOpenseaAssetsByOwnerAndCollection(ctx context.Context, chainID uint64, owner common.Address, collectionSlug string, limit int) ([]OpenseaAsset, error) {
|
||||
func (api *API) GetOpenseaAssetsByOwnerAndCollection(ctx context.Context, chainID uint64, owner common.Address, collectionSlug string, limit int) ([]opensea.Asset, error) {
|
||||
log.Debug("call to get opensea assets")
|
||||
client, err := newOpenseaClient(chainID, api.s.openseaAPIKey)
|
||||
client, err := opensea.NewOpenseaClient(chainID, api.s.openseaAPIKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client.fetchAllAssetsByOwnerAndCollection(owner, collectionSlug, limit)
|
||||
return client.FetchAllAssetsByOwnerAndCollection(owner, collectionSlug, limit)
|
||||
}
|
||||
|
||||
func (api *API) AddEthereumChain(ctx context.Context, network params.Network) error {
|
||||
|
@ -332,27 +333,27 @@ func (api *API) GetEthereumChains(ctx context.Context, onlyEnabled bool) ([]*par
|
|||
|
||||
func (api *API) FetchPrices(ctx context.Context, symbols []string, currencies []string) (map[string]map[string]float64, error) {
|
||||
log.Debug("call to FetchPrices")
|
||||
return api.s.priceManager.FetchPrices(symbols, currencies)
|
||||
return api.s.marketManager.FetchPrices(symbols, currencies)
|
||||
}
|
||||
|
||||
func (api *API) FetchMarketValues(ctx context.Context, symbols []string, currencies []string) (map[string]map[string]thirdparty.MarketCoinValues, error) {
|
||||
func (api *API) FetchMarketValues(ctx context.Context, symbols []string, currency string) (map[string]thirdparty.TokenMarketValues, error) {
|
||||
log.Debug("call to FetchMarketValues")
|
||||
return api.s.cryptoCompare.FetchTokenMarketValues(symbols, currencies)
|
||||
return api.s.marketManager.FetchTokenMarketValues(symbols, currency)
|
||||
}
|
||||
|
||||
func (api *API) GetHourlyMarketValues(ctx context.Context, symbol string, currency string, limit int, aggregate int) ([]thirdparty.TokenHistoricalPairs, error) {
|
||||
func (api *API) GetHourlyMarketValues(ctx context.Context, symbol string, currency string, limit int, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
log.Debug("call to GetHourlyMarketValues")
|
||||
return api.s.cryptoCompare.FetchHourlyMarketValues(symbol, currency, limit, aggregate)
|
||||
return api.s.marketManager.FetchHistoricalHourlyPrices(symbol, currency, limit, aggregate)
|
||||
}
|
||||
|
||||
func (api *API) GetDailyMarketValues(ctx context.Context, symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.TokenHistoricalPairs, error) {
|
||||
func (api *API) GetDailyMarketValues(ctx context.Context, symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
log.Debug("call to GetDailyMarketValues")
|
||||
return api.s.cryptoCompare.FetchDailyMarketValues(symbol, currency, limit, allData, aggregate)
|
||||
return api.s.marketManager.FetchHistoricalDailyPrices(symbol, currency, limit, allData, aggregate)
|
||||
}
|
||||
|
||||
func (api *API) FetchTokenDetails(ctx context.Context, symbols []string) (map[string]thirdparty.Coin, error) {
|
||||
func (api *API) FetchTokenDetails(ctx context.Context, symbols []string) (map[string]thirdparty.TokenDetails, error) {
|
||||
log.Debug("call to FetchTokenDetails")
|
||||
return api.s.cryptoCompare.FetchTokenDetails(symbols)
|
||||
return api.s.marketManager.FetchTokenDetails(symbols)
|
||||
}
|
||||
|
||||
func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*SuggestedFees, error) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
|
||||
iso4217 "github.com/ladydascalie/currency"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/price"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
)
|
||||
|
||||
|
@ -25,12 +25,12 @@ type Format struct {
|
|||
type FormatPerSymbol = map[string]Format
|
||||
|
||||
type Currency struct {
|
||||
priceManager *price.Manager
|
||||
marketManager *market.Manager
|
||||
}
|
||||
|
||||
func NewCurrency(priceManager *price.Manager) *Currency {
|
||||
func NewCurrency(marketManager *market.Manager) *Currency {
|
||||
return &Currency{
|
||||
priceManager: priceManager,
|
||||
marketManager: marketManager,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ func (cm *Currency) FetchTokenCurrencyFormats(symbols []string) (FormatPerSymbol
|
|||
formats := make(FormatPerSymbol)
|
||||
|
||||
// Get latest cached price, fetch only if not available
|
||||
prices, err := cm.priceManager.GetOrFetchPrices(symbols, []string{decimalsCalculationCurrency}, math.MaxInt64)
|
||||
prices, err := cm.marketManager.GetOrFetchPrices(symbols, []string{decimalsCalculationCurrency}, math.MaxInt64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/status-im/status-go/services/wallet/price"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
)
|
||||
|
@ -26,9 +26,9 @@ type Service struct {
|
|||
cancelFn context.CancelFunc
|
||||
}
|
||||
|
||||
func NewService(db *sql.DB, walletFeed *event.Feed, tokenManager *token.Manager, priceManager *price.Manager) *Service {
|
||||
func NewService(db *sql.DB, walletFeed *event.Feed, tokenManager *token.Manager, marketManager *market.Manager) *Service {
|
||||
return &Service{
|
||||
currency: NewCurrency(priceManager),
|
||||
currency: NewCurrency(marketManager),
|
||||
db: NewCurrencyDB(db),
|
||||
tokenManager: tokenManager,
|
||||
walletFeed: walletFeed,
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
)
|
||||
|
||||
type tokenType = string
|
||||
|
@ -26,13 +26,13 @@ type Exchange struct {
|
|||
allTimeCache map[tokenType]map[currencyType][]allTimeEntry
|
||||
fetchMutex sync.Mutex
|
||||
|
||||
cryptoCompare *thirdparty.CryptoCompare
|
||||
marketManager *market.Manager
|
||||
}
|
||||
|
||||
func NewExchange(cryptoCompare *thirdparty.CryptoCompare) *Exchange {
|
||||
func NewExchange(marketManager *market.Manager) *Exchange {
|
||||
return &Exchange{
|
||||
cache: make(map[tokenType]map[currencyType]map[yearType][]float32),
|
||||
cryptoCompare: cryptoCompare,
|
||||
marketManager: marketManager,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ func (e *Exchange) FetchAndCacheMissingRates(token tokenType, currency currencyT
|
|||
return nil
|
||||
}
|
||||
|
||||
res, err := e.cryptoCompare.FetchDailyMarketValues(token, currency, daysToFetch, false, 1)
|
||||
res, err := e.marketManager.FetchHistoricalDailyPrices(token, currency, daysToFetch, false, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ func (e *Exchange) FetchAndCacheMissingRates(token tokenType, currency currencyT
|
|||
}
|
||||
|
||||
// Fetch all time
|
||||
allTime, err := e.cryptoCompare.FetchDailyMarketValues(token, currency, 1, true, 30)
|
||||
allTime, err := e.marketManager.FetchHistoricalDailyPrices(token, currency, 1, true, 30)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"github.com/status-im/status-go/rpc/network"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
)
|
||||
|
@ -55,7 +55,7 @@ type Service struct {
|
|||
|
||||
type chainIdentity uint64
|
||||
|
||||
func NewService(db *sql.DB, eventFeed *event.Feed, rpcClient *statusrpc.Client, tokenManager *token.Manager, cryptoCompare *thirdparty.CryptoCompare) *Service {
|
||||
func NewService(db *sql.DB, eventFeed *event.Feed, rpcClient *statusrpc.Client, tokenManager *token.Manager, marketManager *market.Manager) *Service {
|
||||
return &Service{
|
||||
balance: NewBalance(NewBalanceDB(db)),
|
||||
db: db,
|
||||
|
@ -63,7 +63,7 @@ func NewService(db *sql.DB, eventFeed *event.Feed, rpcClient *statusrpc.Client,
|
|||
rpcClient: rpcClient,
|
||||
networkManager: rpcClient.NetworkManager,
|
||||
tokenManager: tokenManager,
|
||||
exchange: NewExchange(cryptoCompare),
|
||||
exchange: NewExchange(marketManager),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@ import (
|
|||
"github.com/status-im/status-go/appdatabase"
|
||||
"github.com/status-im/status-go/params"
|
||||
statusRPC "github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/cryptocompare"
|
||||
"github.com/status-im/status-go/transactions/fake"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -25,7 +26,7 @@ import (
|
|||
func setupDummyServiceNoDependencies(t *testing.T) (service *Service, closeFn func()) {
|
||||
db, err := appdatabase.InitializeDB(":memory:", "wallet-history-service-tests", 1)
|
||||
require.NoError(t, err)
|
||||
cryptoCompare := thirdparty.NewCryptoCompare()
|
||||
cryptoCompare := cryptocompare.NewClient()
|
||||
|
||||
// Creating a dummy status node to simulate what it's done in get_status_node.go
|
||||
upstreamConfig := params.UpstreamRPCConfig{
|
||||
|
@ -40,7 +41,7 @@ func setupDummyServiceNoDependencies(t *testing.T) (service *Service, closeFn fu
|
|||
rpcClient, err := statusRPC.NewClient(client, 1, upstreamConfig, nil, db)
|
||||
require.NoError(t, err)
|
||||
|
||||
return NewService(db, nil, rpcClient, nil, cryptoCompare), func() {
|
||||
return NewService(db, nil, rpcClient, nil, market.NewManager(cryptoCompare)), func() {
|
||||
require.NoError(t, db.Close())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package price
|
||||
package market
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
)
|
||||
|
||||
type DataPoint struct {
|
||||
|
@ -12,17 +15,38 @@ type DataPoint struct {
|
|||
type DataPerTokenAndCurrency = map[string]map[string]DataPoint
|
||||
|
||||
type Manager struct {
|
||||
priceProvider Provider
|
||||
priceCache DataPerTokenAndCurrency
|
||||
provider thirdparty.MarketDataProvider
|
||||
priceCache DataPerTokenAndCurrency
|
||||
IsConnected bool
|
||||
LastCheckedAt int64
|
||||
IsConnectedLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewManager(priceProvider Provider) *Manager {
|
||||
func NewManager(provider thirdparty.MarketDataProvider) *Manager {
|
||||
return &Manager{
|
||||
priceProvider: priceProvider,
|
||||
provider: provider,
|
||||
priceCache: make(DataPerTokenAndCurrency),
|
||||
IsConnected: true,
|
||||
LastCheckedAt: time.Now().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
func (pm *Manager) FetchHistoricalDailyPrices(symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
return pm.provider.FetchHistoricalDailyPrices(symbol, currency, limit, allData, aggregate)
|
||||
}
|
||||
|
||||
func (pm *Manager) FetchHistoricalHourlyPrices(symbol string, currency string, limit int, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
return pm.provider.FetchHistoricalHourlyPrices(symbol, currency, limit, aggregate)
|
||||
}
|
||||
|
||||
func (pm *Manager) FetchTokenMarketValues(symbols []string, currency string) (map[string]thirdparty.TokenMarketValues, error) {
|
||||
return pm.provider.FetchTokenMarketValues(symbols, currency)
|
||||
}
|
||||
|
||||
func (pm *Manager) FetchTokenDetails(symbols []string) (map[string]thirdparty.TokenDetails, error) {
|
||||
return pm.provider.FetchTokenDetails(symbols)
|
||||
}
|
||||
|
||||
func (pm *Manager) FetchPrice(symbol string, currency string) (float64, error) {
|
||||
symbols := [1]string{symbol}
|
||||
currencies := [1]string{currency}
|
||||
|
@ -37,7 +61,7 @@ func (pm *Manager) FetchPrice(symbol string, currency string) (float64, error) {
|
|||
}
|
||||
|
||||
func (pm *Manager) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) {
|
||||
result, err := pm.priceProvider.FetchPrices(symbols, currencies)
|
||||
result, err := pm.provider.FetchPrices(symbols, currencies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
|
@ -1,9 +1,12 @@
|
|||
package price
|
||||
package market
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
)
|
||||
|
||||
type MockPriceProvider struct {
|
||||
|
@ -18,6 +21,19 @@ func (mpp *MockPriceProvider) setMockPrices(prices map[string]map[string]float64
|
|||
mpp.mockPrices = prices
|
||||
}
|
||||
|
||||
func (mpp *MockPriceProvider) FetchHistoricalDailyPrices(symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
return nil, errors.New("not implmented")
|
||||
}
|
||||
func (mpp *MockPriceProvider) FetchHistoricalHourlyPrices(symbol string, currency string, limit int, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
return nil, errors.New("not implmented")
|
||||
}
|
||||
func (mpp *MockPriceProvider) FetchTokenMarketValues(symbols []string, currency string) (map[string]thirdparty.TokenMarketValues, error) {
|
||||
return nil, errors.New("not implmented")
|
||||
}
|
||||
func (mpp *MockPriceProvider) FetchTokenDetails(symbols []string) (map[string]thirdparty.TokenDetails, error) {
|
||||
return nil, errors.New("not implmented")
|
||||
}
|
||||
|
||||
func (mpp *MockPriceProvider) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) {
|
||||
res := make(map[string]map[string]float64)
|
||||
for _, symbol := range symbols {
|
||||
|
@ -29,8 +45,8 @@ func (mpp *MockPriceProvider) FetchPrices(symbols []string, currencies []string)
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func setupTestPrice(t *testing.T, priceProvider Provider) *Manager {
|
||||
return NewManager(priceProvider)
|
||||
func setupTestPrice(t *testing.T, provider thirdparty.MarketDataProvider) *Manager {
|
||||
return NewManager(provider)
|
||||
}
|
||||
|
||||
func TestPrice(t *testing.T) {
|
|
@ -1,5 +0,0 @@
|
|||
package price
|
||||
|
||||
type Provider interface {
|
||||
FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error)
|
||||
}
|
|
@ -13,9 +13,9 @@ import (
|
|||
"github.com/status-im/status-go/rpc"
|
||||
"github.com/status-im/status-go/services/wallet/async"
|
||||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/price"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/token"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
)
|
||||
|
@ -28,15 +28,14 @@ func getFixedCurrencies() []string {
|
|||
return []string{"USD"}
|
||||
}
|
||||
|
||||
func NewReader(rpcClient *rpc.Client, tokenManager *token.Manager, priceManager *price.Manager, cryptoCompare *thirdparty.CryptoCompare, accountsDB *accounts.Database, walletFeed *event.Feed) *Reader {
|
||||
return &Reader{rpcClient, tokenManager, priceManager, cryptoCompare, accountsDB, walletFeed, nil}
|
||||
func NewReader(rpcClient *rpc.Client, tokenManager *token.Manager, marketManager *market.Manager, accountsDB *accounts.Database, walletFeed *event.Feed) *Reader {
|
||||
return &Reader{rpcClient, tokenManager, marketManager, accountsDB, walletFeed, nil}
|
||||
}
|
||||
|
||||
type Reader struct {
|
||||
rpcClient *rpc.Client
|
||||
tokenManager *token.Manager
|
||||
priceManager *price.Manager
|
||||
cryptoCompare *thirdparty.CryptoCompare
|
||||
marketManager *market.Manager
|
||||
accountsDB *accounts.Database
|
||||
walletFeed *event.Feed
|
||||
cancel context.CancelFunc
|
||||
|
@ -182,13 +181,13 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
|||
var (
|
||||
group = async.NewAtomicGroup(ctx)
|
||||
prices = map[string]map[string]float64{}
|
||||
tokenDetails = map[string]thirdparty.Coin{}
|
||||
tokenMarketValues = map[string]map[string]thirdparty.MarketCoinValues{}
|
||||
tokenDetails = map[string]thirdparty.TokenDetails{}
|
||||
tokenMarketValues = map[string]thirdparty.TokenMarketValues{}
|
||||
balances = map[uint64]map[common.Address]map[common.Address]*hexutil.Big{}
|
||||
)
|
||||
|
||||
group.Add(func(parent context.Context) error {
|
||||
prices, err = r.priceManager.FetchPrices(tokenSymbols, currencies)
|
||||
prices, err = r.marketManager.FetchPrices(tokenSymbols, currencies)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -196,7 +195,7 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
|||
})
|
||||
|
||||
group.Add(func(parent context.Context) error {
|
||||
tokenDetails, err = r.cryptoCompare.FetchTokenDetails(tokenSymbols)
|
||||
tokenDetails, err = r.marketManager.FetchTokenDetails(tokenSymbols)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -204,7 +203,7 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
|||
})
|
||||
|
||||
group.Add(func(parent context.Context) error {
|
||||
tokenMarketValues, err = r.cryptoCompare.FetchTokenMarketValues(tokenSymbols, currencies)
|
||||
tokenMarketValues, err = r.marketManager.FetchTokenMarketValues(tokenSymbols, currency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -257,13 +256,13 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
|
|||
marketValuesPerCurrency := make(map[string]TokenMarketValues)
|
||||
for _, currency := range currencies {
|
||||
marketValuesPerCurrency[currency] = TokenMarketValues{
|
||||
MarketCap: tokenMarketValues[symbol][currency].MKTCAP,
|
||||
HighDay: tokenMarketValues[symbol][currency].HIGHDAY,
|
||||
LowDay: tokenMarketValues[symbol][currency].LOWDAY,
|
||||
ChangePctHour: tokenMarketValues[symbol][currency].CHANGEPCTHOUR,
|
||||
ChangePctDay: tokenMarketValues[symbol][currency].CHANGEPCTDAY,
|
||||
ChangePct24hour: tokenMarketValues[symbol][currency].CHANGEPCT24HOUR,
|
||||
Change24hour: tokenMarketValues[symbol][currency].CHANGE24HOUR,
|
||||
MarketCap: tokenMarketValues[symbol].MKTCAP,
|
||||
HighDay: tokenMarketValues[symbol].HIGHDAY,
|
||||
LowDay: tokenMarketValues[symbol].LOWDAY,
|
||||
ChangePctHour: tokenMarketValues[symbol].CHANGEPCTHOUR,
|
||||
ChangePctDay: tokenMarketValues[symbol].CHANGEPCTDAY,
|
||||
ChangePct24hour: tokenMarketValues[symbol].CHANGEPCT24HOUR,
|
||||
Change24hour: tokenMarketValues[symbol].CHANGE24HOUR,
|
||||
Price: prices[symbol][currency],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -458,7 +458,7 @@ func (r *Router) suggestedRoutes(
|
|||
return nil, err
|
||||
}
|
||||
|
||||
pricesMap, err := r.s.priceManager.FetchPrices([]string{"ETH", tokenSymbol}, []string{"USD"})
|
||||
pricesMap, err := r.s.marketManager.FetchPrices([]string{"ETH", tokenSymbol}, []string{"USD"})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -18,8 +18,9 @@ import (
|
|||
"github.com/status-im/status-go/services/wallet/chain"
|
||||
"github.com/status-im/status-go/services/wallet/currency"
|
||||
"github.com/status-im/status-go/services/wallet/history"
|
||||
"github.com/status-im/status-go/services/wallet/price"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/market"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/cryptocompare"
|
||||
"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"
|
||||
"github.com/status-im/status-go/services/wallet/walletevent"
|
||||
|
@ -32,9 +33,9 @@ type Connection struct {
|
|||
}
|
||||
|
||||
type ConnectedResult struct {
|
||||
Infura map[uint64]Connection `json:"infura"`
|
||||
CryptoCompare Connection `json:"cryptoCompare"`
|
||||
Opensea map[uint64]Connection `json:"opensea"`
|
||||
Blockchain map[uint64]Connection `json:"blockchain"`
|
||||
Market Connection `json:"market"`
|
||||
Collectibles map[uint64]Connection `json:"collectibles"`
|
||||
}
|
||||
|
||||
// NewService initializes service instance.
|
||||
|
@ -61,11 +62,11 @@ func NewService(
|
|||
savedAddressesManager := &SavedAddressesManager{db: db}
|
||||
transactionManager := &TransactionManager{db: db, transactor: transactor, gethManager: gethManager, config: config, accountsDB: accountsDB}
|
||||
transferController := transfer.NewTransferController(db, rpcClient, accountFeed, walletFeed)
|
||||
cryptoCompare := thirdparty.NewCryptoCompare()
|
||||
priceManager := price.NewManager(cryptoCompare)
|
||||
reader := NewReader(rpcClient, tokenManager, priceManager, cryptoCompare, accountsDB, walletFeed)
|
||||
history := history.NewService(db, walletFeed, rpcClient, tokenManager, cryptoCompare)
|
||||
currency := currency.NewService(db, walletFeed, tokenManager, priceManager)
|
||||
cryptoCompare := cryptocompare.NewClient()
|
||||
marketManager := market.NewManager(cryptoCompare)
|
||||
reader := NewReader(rpcClient, tokenManager, marketManager, accountsDB, walletFeed)
|
||||
history := history.NewService(db, walletFeed, rpcClient, tokenManager, marketManager)
|
||||
currency := currency.NewService(db, walletFeed, tokenManager, marketManager)
|
||||
return &Service{
|
||||
db: db,
|
||||
accountsDB: accountsDB,
|
||||
|
@ -78,14 +79,13 @@ func NewService(
|
|||
openseaAPIKey: openseaAPIKey,
|
||||
feesManager: &FeeManager{rpcClient},
|
||||
gethManager: gethManager,
|
||||
priceManager: priceManager,
|
||||
marketManager: marketManager,
|
||||
transactor: transactor,
|
||||
ens: ens,
|
||||
stickers: stickers,
|
||||
feed: accountFeed,
|
||||
signals: signals,
|
||||
reader: reader,
|
||||
cryptoCompare: cryptoCompare,
|
||||
history: history,
|
||||
currency: currency,
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ type Service struct {
|
|||
cryptoOnRampManager *CryptoOnRampManager
|
||||
transferController *transfer.Controller
|
||||
feesManager *FeeManager
|
||||
priceManager *price.Manager
|
||||
marketManager *market.Manager
|
||||
started bool
|
||||
openseaAPIKey string
|
||||
gethManager *account.GethManager
|
||||
|
@ -112,7 +112,6 @@ type Service struct {
|
|||
feed *event.Feed
|
||||
signals *walletevent.SignalsTransmitter
|
||||
reader *Reader
|
||||
cryptoCompare *thirdparty.CryptoCompare
|
||||
history *history.Service
|
||||
currency *currency.Service
|
||||
}
|
||||
|
@ -167,27 +166,27 @@ func (s *Service) IsStarted() bool {
|
|||
}
|
||||
|
||||
func (s *Service) CheckConnected(ctx context.Context) *ConnectedResult {
|
||||
infura := make(map[uint64]Connection)
|
||||
blockchain := make(map[uint64]Connection)
|
||||
for chainID, client := range chain.ChainClientInstances {
|
||||
infura[chainID] = Connection{
|
||||
blockchain[chainID] = Connection{
|
||||
Up: client.IsConnected,
|
||||
LastCheckedAt: client.LastCheckedAt,
|
||||
}
|
||||
}
|
||||
|
||||
opensea := make(map[uint64]Connection)
|
||||
for chainID, client := range OpenseaClientInstances {
|
||||
opensea[chainID] = Connection{
|
||||
collectibles := make(map[uint64]Connection)
|
||||
for chainID, client := range opensea.OpenseaClientInstances {
|
||||
collectibles[chainID] = Connection{
|
||||
Up: client.IsConnected,
|
||||
LastCheckedAt: client.LastCheckedAt,
|
||||
}
|
||||
}
|
||||
return &ConnectedResult{
|
||||
Infura: infura,
|
||||
Opensea: opensea,
|
||||
CryptoCompare: Connection{
|
||||
Up: s.cryptoCompare.IsConnected,
|
||||
LastCheckedAt: s.cryptoCompare.LastCheckedAt,
|
||||
Blockchain: blockchain,
|
||||
Collectibles: collectibles,
|
||||
Market: Connection{
|
||||
Up: s.marketManager.IsConnected,
|
||||
LastCheckedAt: s.marketManager.LastCheckedAt,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,308 @@
|
|||
package coingecko
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/utils"
|
||||
)
|
||||
|
||||
var coinGeckoMapping = map[string]string{
|
||||
"STT": "status",
|
||||
"SNT": "status",
|
||||
"ETH": "ethereum",
|
||||
"AST": "airswap",
|
||||
"AMB": "",
|
||||
"ABT": "arcblock",
|
||||
"ATM": "",
|
||||
"BNB": "binancecoin",
|
||||
"BLT": "bloom",
|
||||
"CDT": "",
|
||||
"COMP": "compound-coin",
|
||||
"EDG": "edgeless",
|
||||
"ELF": "",
|
||||
"ENG": "enigma",
|
||||
"EOS": "eos",
|
||||
"GEN": "daostack",
|
||||
"MANA": "decentraland-wormhole",
|
||||
"LEND": "ethlend",
|
||||
"LRC": "loopring",
|
||||
"MET": "metronome",
|
||||
"POLY": "polymath",
|
||||
"PPT": "populous",
|
||||
"SAN": "santiment-network-token",
|
||||
"DNT": "district0x",
|
||||
"SPN": "sapien",
|
||||
"USDS": "stableusd",
|
||||
"STX": "stox",
|
||||
"SUB": "substratum",
|
||||
"PAY": "tenx",
|
||||
"GRT": "the-graph",
|
||||
"TNT": "tierion",
|
||||
"TRX": "tron",
|
||||
"TGT": "",
|
||||
"RARE": "superrare",
|
||||
"UNI": "uniswap",
|
||||
"USDC": "usd-coin",
|
||||
"USDP": "paxos-standard",
|
||||
"VRS": "",
|
||||
"TIME": "",
|
||||
}
|
||||
|
||||
const baseURL = "https://api.coingecko.com/api/v3/"
|
||||
|
||||
type HistoricalPriceContainer struct {
|
||||
Prices [][]float64 `json:"prices"`
|
||||
}
|
||||
type GeckoMarketValues struct {
|
||||
ID string `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Name string `json:"name"`
|
||||
MarketCap float64 `json:"market_cap"`
|
||||
High24h float64 `json:"high_24h"`
|
||||
Low24h float64 `json:"low_24h"`
|
||||
PriceChange24h float64 `json:"price_change_24h"`
|
||||
PriceChangePercentage24h float64 `json:"price_change_percentage_24h"`
|
||||
PriceChangePercentage1hInCurrency float64 `json:"price_change_percentage_1h_in_currency"`
|
||||
}
|
||||
|
||||
type GeckoToken struct {
|
||||
ID string `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
client *http.Client
|
||||
tokens map[string]GeckoToken
|
||||
}
|
||||
|
||||
func NewClient() *Client {
|
||||
return &Client{client: &http.Client{Timeout: time.Minute}, tokens: make(map[string]GeckoToken)}
|
||||
}
|
||||
|
||||
func (c *Client) DoQuery(url string) (*http.Response, error) {
|
||||
resp, err := c.client.Get(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) getTokens() (map[string]GeckoToken, error) {
|
||||
if len(c.tokens) > 0 {
|
||||
return c.tokens, nil
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%scoins/list", baseURL)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var tokens []GeckoToken
|
||||
err = json.Unmarshal(body, &tokens)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, token := range tokens {
|
||||
if id, ok := coinGeckoMapping[strings.ToUpper(token.Symbol)]; ok {
|
||||
if id != token.ID {
|
||||
continue
|
||||
}
|
||||
}
|
||||
c.tokens[strings.ToUpper(token.Symbol)] = token
|
||||
}
|
||||
|
||||
return c.tokens, nil
|
||||
}
|
||||
|
||||
func (c *Client) mapSymbolsToIds(symbols []string) ([]string, error) {
|
||||
tokens, err := c.getTokens()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ids := make([]string, 0)
|
||||
for _, symbol := range utils.RenameSymbols(symbols) {
|
||||
if token, ok := tokens[symbol]; ok {
|
||||
ids = append(ids, token.ID)
|
||||
}
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (c *Client) getIDFromSymbol(symbol string) (string, error) {
|
||||
tokens, err := c.getTokens()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return tokens[strings.ToUpper(symbol)].ID, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) {
|
||||
ids, err := c.mapSymbolsToIds(symbols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
url := fmt.Sprintf("%ssimple/price?ids=%s&vs_currencies=%s", baseURL, strings.Join(ids, ","), strings.Join(currencies, ","))
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prices := make(map[string]map[string]float64)
|
||||
err = json.Unmarshal(body, &prices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]map[string]float64)
|
||||
for _, symbol := range symbols {
|
||||
result[symbol] = map[string]float64{}
|
||||
id, err := c.getIDFromSymbol(utils.GetRealSymbol(symbol))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, currency := range currencies {
|
||||
result[symbol][currency] = prices[id][strings.ToLower(currency)]
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchTokenDetails(symbols []string) (map[string]thirdparty.TokenDetails, error) {
|
||||
tokens, err := c.getTokens()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]thirdparty.TokenDetails)
|
||||
for _, symbol := range symbols {
|
||||
if value, ok := tokens[utils.GetRealSymbol(symbol)]; ok {
|
||||
result[symbol] = thirdparty.TokenDetails{
|
||||
ID: value.ID,
|
||||
Name: value.Name,
|
||||
Symbol: symbol,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[string]thirdparty.TokenMarketValues, error) {
|
||||
ids, err := c.mapSymbolsToIds(symbols)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queryParams := url.Values{
|
||||
"ids": {strings.Join(ids, ",")},
|
||||
"vs_currency": {currency},
|
||||
"order": {"market_cap_desc"},
|
||||
"per_page": {"250"},
|
||||
"page": {"1"},
|
||||
"sparkline": {"false"},
|
||||
"price_change_percentage": {"1h,24h"},
|
||||
}
|
||||
url := baseURL + "coins/markets" + queryParams.Encode()
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var marketValues []GeckoMarketValues
|
||||
err = json.Unmarshal(body, &marketValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[string]thirdparty.TokenMarketValues)
|
||||
for _, symbol := range symbols {
|
||||
id, err := c.getIDFromSymbol(utils.GetRealSymbol(symbol))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, marketValue := range marketValues {
|
||||
if id != marketValue.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
result[symbol] = thirdparty.TokenMarketValues{
|
||||
MKTCAP: marketValue.MarketCap,
|
||||
HIGHDAY: marketValue.High24h,
|
||||
LOWDAY: marketValue.Low24h,
|
||||
CHANGEPCTHOUR: marketValue.PriceChangePercentage1hInCurrency,
|
||||
CHANGEPCTDAY: marketValue.PriceChangePercentage24h,
|
||||
CHANGEPCT24HOUR: marketValue.PriceChangePercentage24h,
|
||||
CHANGE24HOUR: marketValue.PriceChange24h,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchHistoricalHourlyPrices(symbol string, currency string, limit int, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
return []thirdparty.HistoricalPrice{}, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchHistoricalDailyPrices(symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
id, err := c.getIDFromSymbol(utils.GetRealSymbol(symbol))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%scoins/%s/market_chart?vs_currency=%s&days=30", baseURL, id, currency)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var container HistoricalPriceContainer
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]thirdparty.HistoricalPrice, 0)
|
||||
for _, price := range container.Prices {
|
||||
result = append(result, thirdparty.HistoricalPrice{
|
||||
Timestamp: int64(price[0]),
|
||||
Value: price[1],
|
||||
})
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
|
@ -1,269 +0,0 @@
|
|||
package thirdparty
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const cryptocompareURL = "https://min-api.cryptocompare.com"
|
||||
|
||||
var renameMapping = map[string]string{
|
||||
"STT": "SNT",
|
||||
}
|
||||
|
||||
type Coin struct {
|
||||
ID string `json:"Id"`
|
||||
Name string `json:"Name"`
|
||||
Symbol string `json:"Symbol"`
|
||||
Description string `json:"Description"`
|
||||
TotalCoinsMined float64 `json:"TotalCoinsMined"`
|
||||
AssetLaunchDate string `json:"AssetLaunchDate"`
|
||||
AssetWhitepaperURL string `json:"AssetWhitepaperUrl"`
|
||||
AssetWebsiteURL string `json:"AssetWebsiteUrl"`
|
||||
BuiltOn string `json:"BuiltOn"`
|
||||
SmartContractAddress string `json:"SmartContractAddress"`
|
||||
}
|
||||
|
||||
type MarketCoinValues struct {
|
||||
MKTCAP float64 `json:"MKTCAP"`
|
||||
HIGHDAY float64 `json:"HIGHDAY"`
|
||||
LOWDAY float64 `json:"LOWDAY"`
|
||||
CHANGEPCTHOUR float64 `json:"CHANGEPCTHOUR"`
|
||||
CHANGEPCTDAY float64 `json:"CHANGEPCTDAY"`
|
||||
CHANGEPCT24HOUR float64 `json:"CHANGEPCT24HOUR"`
|
||||
CHANGE24HOUR float64 `json:"CHANGE24HOUR"`
|
||||
}
|
||||
|
||||
type TokenHistoricalPairs struct {
|
||||
Timestamp int64 `json:"time"`
|
||||
Value float64 `json:"close"`
|
||||
Volumefrom float64 `json:"volumefrom"`
|
||||
Volumeto float64 `json:"volumeto"`
|
||||
}
|
||||
|
||||
type HistoricalValuesContainer struct {
|
||||
Aggregated bool `json:"Aggregated"`
|
||||
TimeFrom int64 `json:"TimeFrom"`
|
||||
TimeTo int64 `json:"TimeTo"`
|
||||
HistoricalData []TokenHistoricalPairs `json:"Data"`
|
||||
}
|
||||
|
||||
type HistoricalValuesData struct {
|
||||
Data HistoricalValuesContainer `json:"Data"`
|
||||
}
|
||||
|
||||
type CoinsContainer struct {
|
||||
Data map[string]Coin `json:"Data"`
|
||||
}
|
||||
|
||||
type MarketValuesContainer struct {
|
||||
Raw map[string]map[string]MarketCoinValues `json:"Raw"`
|
||||
}
|
||||
|
||||
type CryptoCompare struct {
|
||||
client *http.Client
|
||||
IsConnected bool
|
||||
LastCheckedAt int64
|
||||
IsConnectedLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewCryptoCompare() *CryptoCompare {
|
||||
return &CryptoCompare{client: &http.Client{Timeout: time.Minute}, IsConnected: true, LastCheckedAt: time.Now().Unix()}
|
||||
}
|
||||
|
||||
func renameSymbols(symbols []string) (renames []string) {
|
||||
for _, symbol := range symbols {
|
||||
renames = append(renames, getRealSymbol(symbol))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getRealSymbol(symbol string) string {
|
||||
if val, ok := renameMapping[strings.ToUpper(symbol)]; ok {
|
||||
return val
|
||||
}
|
||||
return strings.ToUpper(symbol)
|
||||
}
|
||||
|
||||
func chunkSymbols(symbols []string) [][]string {
|
||||
var chunks [][]string
|
||||
chunkSize := 20
|
||||
for i := 0; i < len(symbols); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
|
||||
if end > len(symbols) {
|
||||
end = len(symbols)
|
||||
}
|
||||
|
||||
chunks = append(chunks, symbols[i:end])
|
||||
}
|
||||
|
||||
return chunks
|
||||
}
|
||||
|
||||
func (c *CryptoCompare) DoQuery(url string) (*http.Response, error) {
|
||||
resp, err := c.client.Get(url)
|
||||
|
||||
c.IsConnectedLock.Lock()
|
||||
defer c.IsConnectedLock.Unlock()
|
||||
|
||||
c.LastCheckedAt = time.Now().Unix()
|
||||
if err != nil {
|
||||
c.IsConnected = false
|
||||
return nil, err
|
||||
}
|
||||
c.IsConnected = true
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *CryptoCompare) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) {
|
||||
chunks := chunkSymbols(symbols)
|
||||
result := make(map[string]map[string]float64)
|
||||
realCurrencies := renameSymbols(currencies)
|
||||
for _, smbls := range chunks {
|
||||
realSymbols := renameSymbols(smbls)
|
||||
url := fmt.Sprintf("%s/data/pricemulti?fsyms=%s&tsyms=%s&extraParams=Status.im", cryptocompareURL, strings.Join(realSymbols, ","), strings.Join(realCurrencies, ","))
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prices := make(map[string]map[string]float64)
|
||||
err = json.Unmarshal(body, &prices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, symbol := range smbls {
|
||||
result[symbol] = map[string]float64{}
|
||||
for _, currency := range currencies {
|
||||
result[symbol][currency] = prices[getRealSymbol(symbol)][getRealSymbol(currency)]
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *CryptoCompare) FetchTokenDetails(symbols []string) (map[string]Coin, error) {
|
||||
url := fmt.Sprintf("%s/data/all/coinlist", cryptocompareURL)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
container := CoinsContainer{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coins := make(map[string]Coin)
|
||||
|
||||
for _, symbol := range symbols {
|
||||
coins[symbol] = container.Data[getRealSymbol(symbol)]
|
||||
}
|
||||
|
||||
return coins, nil
|
||||
}
|
||||
|
||||
func (c *CryptoCompare) FetchTokenMarketValues(symbols []string, currencies []string) (map[string]map[string]MarketCoinValues, error) {
|
||||
realCurrencies := renameSymbols(currencies)
|
||||
realSymbols := renameSymbols(symbols)
|
||||
item := map[string]map[string]MarketCoinValues{}
|
||||
|
||||
url := fmt.Sprintf("%s/data/pricemultifull?fsyms=%s&tsyms=%s&extraParams=Status.im", cryptocompareURL, strings.Join(realSymbols, ","), strings.Join(realCurrencies, ","))
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
container := MarketValuesContainer{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
for _, symbol := range symbols {
|
||||
item[symbol] = map[string]MarketCoinValues{}
|
||||
for _, currency := range currencies {
|
||||
item[symbol][currency] = container.Raw[getRealSymbol(symbol)][getRealSymbol(currency)]
|
||||
}
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (c *CryptoCompare) FetchHourlyMarketValues(symbol string, currency string, limit int, aggregate int) ([]TokenHistoricalPairs, error) {
|
||||
item := []TokenHistoricalPairs{}
|
||||
|
||||
url := fmt.Sprintf("%s/data/v2/histohour?fsym=%s&tsym=%s&aggregate=%d&limit=%d&extraParams=Status.im", cryptocompareURL, getRealSymbol(symbol), currency, aggregate, limit)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
container := HistoricalValuesData{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
item = container.Data.HistoricalData
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (c *CryptoCompare) FetchDailyMarketValues(symbol string, currency string, limit int, allData bool, aggregate int) ([]TokenHistoricalPairs, error) {
|
||||
item := []TokenHistoricalPairs{}
|
||||
|
||||
url := fmt.Sprintf("%s/data/v2/histoday?fsym=%s&tsym=%s&aggregate=%d&limit=%d&allData=%v&extraParams=Status.im", cryptocompareURL, getRealSymbol(symbol), currency, aggregate, limit, allData)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
container := HistoricalValuesData{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
item = container.Data.HistoricalData
|
||||
|
||||
return item, nil
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
package cryptocompare
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty"
|
||||
"github.com/status-im/status-go/services/wallet/thirdparty/utils"
|
||||
)
|
||||
|
||||
const baseURL = "https://min-api.cryptocompare.com"
|
||||
|
||||
type HistoricalPricesContainer struct {
|
||||
Aggregated bool `json:"Aggregated"`
|
||||
TimeFrom int64 `json:"TimeFrom"`
|
||||
TimeTo int64 `json:"TimeTo"`
|
||||
HistoricalData []thirdparty.HistoricalPrice `json:"Data"`
|
||||
}
|
||||
|
||||
type HistoricalPricesData struct {
|
||||
Data HistoricalPricesContainer `json:"Data"`
|
||||
}
|
||||
|
||||
type TokenDetailsContainer struct {
|
||||
Data map[string]thirdparty.TokenDetails `json:"Data"`
|
||||
}
|
||||
|
||||
type MarketValuesContainer struct {
|
||||
Raw map[string]map[string]thirdparty.TokenMarketValues `json:"Raw"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func NewClient() *Client {
|
||||
return &Client{client: &http.Client{Timeout: time.Minute}}
|
||||
}
|
||||
|
||||
func (c *Client) DoQuery(url string) (*http.Response, error) {
|
||||
resp, err := c.client.Get(url)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) {
|
||||
chunks := utils.ChunkSymbols(symbols)
|
||||
result := make(map[string]map[string]float64)
|
||||
realCurrencies := utils.RenameSymbols(currencies)
|
||||
for _, smbls := range chunks {
|
||||
realSymbols := utils.RenameSymbols(smbls)
|
||||
url := fmt.Sprintf("%s/data/pricemulti?fsyms=%s&tsyms=%s&extraParams=Status.im", baseURL, strings.Join(realSymbols, ","), strings.Join(realCurrencies, ","))
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prices := make(map[string]map[string]float64)
|
||||
err = json.Unmarshal(body, &prices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, symbol := range smbls {
|
||||
result[symbol] = map[string]float64{}
|
||||
for _, currency := range currencies {
|
||||
result[symbol][currency] = prices[utils.GetRealSymbol(symbol)][utils.GetRealSymbol(currency)]
|
||||
}
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchTokenDetails(symbols []string) (map[string]thirdparty.TokenDetails, error) {
|
||||
url := fmt.Sprintf("%s/data/all/coinlist", baseURL)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
container := TokenDetailsContainer{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tokenDetails := make(map[string]thirdparty.TokenDetails)
|
||||
|
||||
for _, symbol := range symbols {
|
||||
tokenDetails[symbol] = container.Data[utils.GetRealSymbol(symbol)]
|
||||
}
|
||||
|
||||
return tokenDetails, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[string]thirdparty.TokenMarketValues, error) {
|
||||
realCurrency := utils.GetRealSymbol(currency)
|
||||
realSymbols := utils.RenameSymbols(symbols)
|
||||
item := map[string]thirdparty.TokenMarketValues{}
|
||||
|
||||
url := fmt.Sprintf("%s/data/pricemultifull?fsyms=%s&tsyms=%s&extraParams=Status.im", baseURL, strings.Join(realSymbols, ","), realCurrency)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
container := MarketValuesContainer{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
for _, symbol := range symbols {
|
||||
item[symbol] = container.Raw[utils.GetRealSymbol(symbol)][utils.GetRealSymbol(currency)]
|
||||
}
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchHistoricalHourlyPrices(symbol string, currency string, limit int, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
item := []thirdparty.HistoricalPrice{}
|
||||
|
||||
url := fmt.Sprintf("%s/data/v2/histohour?fsym=%s&tsym=%s&aggregate=%d&limit=%d&extraParams=Status.im", baseURL, utils.GetRealSymbol(symbol), currency, aggregate, limit)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
container := HistoricalPricesData{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
item = container.Data.HistoricalData
|
||||
|
||||
return item, nil
|
||||
}
|
||||
|
||||
func (c *Client) FetchHistoricalDailyPrices(symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.HistoricalPrice, error) {
|
||||
item := []thirdparty.HistoricalPrice{}
|
||||
|
||||
url := fmt.Sprintf("%s/data/v2/histoday?fsym=%s&tsym=%s&aggregate=%d&limit=%d&allData=%v&extraParams=Status.im", baseURL, utils.GetRealSymbol(symbol), currency, aggregate, limit, allData)
|
||||
resp, err := c.DoQuery(url)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
container := HistoricalPricesData{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
item = container.Data.HistoricalData
|
||||
|
||||
return item, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package wallet
|
||||
package opensea
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -18,7 +18,7 @@ import (
|
|||
const AssetLimit = 50
|
||||
const CollectionLimit = 300
|
||||
|
||||
var OpenseaClientInstances = make(map[uint64]*OpenseaClient)
|
||||
var OpenseaClientInstances = make(map[uint64]*Client)
|
||||
|
||||
var BaseURLs = map[uint64]string{
|
||||
1: "https://api.opensea.io/api/v1",
|
||||
|
@ -46,26 +46,26 @@ func (st *TraitValue) UnmarshalJSON(b []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type OpenseaAssetContainer struct {
|
||||
Assets []OpenseaAsset `json:"assets"`
|
||||
type AssetContainer struct {
|
||||
Assets []Asset `json:"assets"`
|
||||
}
|
||||
|
||||
type OpenseaAssetCollection struct {
|
||||
type AssetCollection struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type OpenseaContract struct {
|
||||
type Contract struct {
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type OpenseaTrait struct {
|
||||
type Trait struct {
|
||||
TraitType string `json:"trait_type"`
|
||||
Value TraitValue `json:"value"`
|
||||
DisplayType string `json:"display_type"`
|
||||
MaxValue string `json:"max_value"`
|
||||
}
|
||||
|
||||
type OpenseaPaymentToken struct {
|
||||
type PaymentToken struct {
|
||||
ID int `json:"id"`
|
||||
Symbol string `json:"symbol"`
|
||||
Address string `json:"address"`
|
||||
|
@ -76,42 +76,42 @@ type OpenseaPaymentToken struct {
|
|||
UsdPrice string `json:"usd_price"`
|
||||
}
|
||||
|
||||
type OpenseaLastSale struct {
|
||||
PaymentToken OpenseaPaymentToken `json:"payment_token"`
|
||||
type LastSale struct {
|
||||
PaymentToken PaymentToken `json:"payment_token"`
|
||||
}
|
||||
|
||||
type OpenseaSellOrder struct {
|
||||
type SellOrder struct {
|
||||
CurrentPrice string `json:"current_price"`
|
||||
}
|
||||
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"`
|
||||
LastSale OpenseaLastSale `json:"last_sale"`
|
||||
SellOrders []OpenseaSellOrder `json:"sell_orders"`
|
||||
BackgroundColor string `json:"background_color"`
|
||||
type Asset 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 Contract `json:"asset_contract"`
|
||||
Collection AssetCollection `json:"collection"`
|
||||
Traits []Trait `json:"traits"`
|
||||
LastSale LastSale `json:"last_sale"`
|
||||
SellOrders []SellOrder `json:"sell_orders"`
|
||||
BackgroundColor string `json:"background_color"`
|
||||
}
|
||||
|
||||
type OpenseaCollectionTrait struct {
|
||||
type CollectionTrait struct {
|
||||
Min float64 `json:"min"`
|
||||
Max float64 `json:"max"`
|
||||
}
|
||||
|
||||
type OpenseaCollection struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
ImageURL string `json:"image_url"`
|
||||
OwnedAssetCount int `json:"owned_asset_count"`
|
||||
Traits map[string]OpenseaCollectionTrait `json:"traits"`
|
||||
type Collection struct {
|
||||
Name string `json:"name"`
|
||||
Slug string `json:"slug"`
|
||||
ImageURL string `json:"image_url"`
|
||||
OwnedAssetCount int `json:"owned_asset_count"`
|
||||
Traits map[string]CollectionTrait `json:"traits"`
|
||||
}
|
||||
|
||||
type OpenseaClient struct {
|
||||
type Client struct {
|
||||
client *http.Client
|
||||
url string
|
||||
apiKey string
|
||||
|
@ -121,7 +121,7 @@ type OpenseaClient struct {
|
|||
}
|
||||
|
||||
// new opensea client.
|
||||
func newOpenseaClient(chainID uint64, apiKey string) (*OpenseaClient, error) {
|
||||
func NewOpenseaClient(chainID uint64, apiKey string) (*Client, error) {
|
||||
if client, ok := OpenseaClientInstances[chainID]; ok {
|
||||
if client.apiKey == apiKey {
|
||||
return client, nil
|
||||
|
@ -132,7 +132,7 @@ func newOpenseaClient(chainID uint64, apiKey string) (*OpenseaClient, error) {
|
|||
Timeout: time.Second * 5,
|
||||
}
|
||||
if url, ok := BaseURLs[chainID]; ok {
|
||||
openseaClient := &OpenseaClient{client: client, url: url, apiKey: apiKey, IsConnected: true, LastCheckedAt: time.Now().Unix()}
|
||||
openseaClient := &Client{client: client, url: url, apiKey: apiKey, IsConnected: true, LastCheckedAt: time.Now().Unix()}
|
||||
OpenseaClientInstances[chainID] = openseaClient
|
||||
return openseaClient, nil
|
||||
}
|
||||
|
@ -140,9 +140,9 @@ func newOpenseaClient(chainID uint64, apiKey string) (*OpenseaClient, error) {
|
|||
return nil, errors.New("ChainID not supported")
|
||||
}
|
||||
|
||||
func (o *OpenseaClient) fetchAllCollectionsByOwner(owner common.Address) ([]OpenseaCollection, error) {
|
||||
func (o *Client) FetchAllCollectionsByOwner(owner common.Address) ([]Collection, error) {
|
||||
offset := 0
|
||||
var collections []OpenseaCollection
|
||||
var collections []Collection
|
||||
o.IsConnectedLock.Lock()
|
||||
defer o.IsConnectedLock.Unlock()
|
||||
o.LastCheckedAt = time.Now().Unix()
|
||||
|
@ -154,7 +154,7 @@ func (o *OpenseaClient) fetchAllCollectionsByOwner(owner common.Address) ([]Open
|
|||
return nil, err
|
||||
}
|
||||
|
||||
var tmp []OpenseaCollection
|
||||
var tmp []Collection
|
||||
err = json.Unmarshal(body, &tmp)
|
||||
if err != nil {
|
||||
o.IsConnected = false
|
||||
|
@ -171,9 +171,9 @@ func (o *OpenseaClient) fetchAllCollectionsByOwner(owner common.Address) ([]Open
|
|||
return collections, nil
|
||||
}
|
||||
|
||||
func (o *OpenseaClient) fetchAllAssetsByOwnerAndCollection(owner common.Address, collectionSlug string, limit int) ([]OpenseaAsset, error) {
|
||||
func (o *Client) FetchAllAssetsByOwnerAndCollection(owner common.Address, collectionSlug string, limit int) ([]Asset, error) {
|
||||
offset := 0
|
||||
var assets []OpenseaAsset
|
||||
var assets []Asset
|
||||
o.IsConnectedLock.Lock()
|
||||
defer o.IsConnectedLock.Unlock()
|
||||
o.LastCheckedAt = time.Now().Unix()
|
||||
|
@ -185,7 +185,7 @@ func (o *OpenseaClient) fetchAllAssetsByOwnerAndCollection(owner common.Address,
|
|||
return nil, err
|
||||
}
|
||||
|
||||
container := OpenseaAssetContainer{}
|
||||
container := AssetContainer{}
|
||||
err = json.Unmarshal(body, &container)
|
||||
if err != nil {
|
||||
o.IsConnected = false
|
||||
|
@ -213,7 +213,7 @@ func (o *OpenseaClient) fetchAllAssetsByOwnerAndCollection(owner common.Address,
|
|||
return assets, nil
|
||||
}
|
||||
|
||||
func (o *OpenseaClient) doOpenseaRequest(url string) ([]byte, error) {
|
||||
func (o *Client) doOpenseaRequest(url string) ([]byte, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
|
@ -1,4 +1,4 @@
|
|||
package wallet
|
||||
package opensea
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -12,7 +12,7 @@ import (
|
|||
)
|
||||
|
||||
func TestFetchAllCollectionsByOwner(t *testing.T) {
|
||||
expected := []OpenseaCollection{OpenseaCollection{Name: "Rocky", Slug: "rocky", ImageURL: "ImageUrl", OwnedAssetCount: 1}}
|
||||
expected := []Collection{Collection{Name: "Rocky", Slug: "rocky", ImageURL: "ImageUrl", OwnedAssetCount: 1}}
|
||||
response, _ := json.Marshal(expected)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
|
@ -23,27 +23,27 @@ func TestFetchAllCollectionsByOwner(t *testing.T) {
|
|||
}))
|
||||
defer srv.Close()
|
||||
|
||||
opensea := &OpenseaClient{
|
||||
opensea := &Client{
|
||||
client: srv.Client(),
|
||||
url: srv.URL,
|
||||
}
|
||||
res, err := opensea.fetchAllCollectionsByOwner(common.Address{1})
|
||||
res, err := opensea.FetchAllCollectionsByOwner(common.Address{1})
|
||||
assert.Equal(t, expected, res)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestFetchAllAssetsByOwnerAndCollection(t *testing.T) {
|
||||
expected := []OpenseaAsset{OpenseaAsset{
|
||||
expected := []Asset{Asset{
|
||||
ID: 1,
|
||||
Name: "Rocky",
|
||||
Description: "Rocky Balboa",
|
||||
Permalink: "permalink",
|
||||
ImageThumbnailURL: "ImageThumbnailURL",
|
||||
ImageURL: "ImageUrl",
|
||||
Contract: OpenseaContract{Address: "1"},
|
||||
Collection: OpenseaAssetCollection{Name: "Rocky"},
|
||||
Contract: Contract{Address: "1"},
|
||||
Collection: AssetCollection{Name: "Rocky"},
|
||||
}}
|
||||
response, _ := json.Marshal(OpenseaAssetContainer{Assets: expected})
|
||||
response, _ := json.Marshal(AssetContainer{Assets: expected})
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(200)
|
||||
_, err := w.Write(response)
|
||||
|
@ -53,11 +53,11 @@ func TestFetchAllAssetsByOwnerAndCollection(t *testing.T) {
|
|||
}))
|
||||
defer srv.Close()
|
||||
|
||||
opensea := &OpenseaClient{
|
||||
opensea := &Client{
|
||||
client: srv.Client(),
|
||||
url: srv.URL,
|
||||
}
|
||||
res, err := opensea.fetchAllAssetsByOwnerAndCollection(common.Address{1}, "rocky", 200)
|
||||
res, err := opensea.FetchAllAssetsByOwnerAndCollection(common.Address{1}, "rocky", 200)
|
||||
assert.Equal(t, expected, res)
|
||||
assert.Nil(t, err)
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package thirdparty
|
||||
|
||||
type HistoricalPrice struct {
|
||||
Timestamp int64 `json:"time"`
|
||||
Value float64 `json:"close"`
|
||||
}
|
||||
|
||||
type TokenMarketValues struct {
|
||||
MKTCAP float64 `json:"MKTCAP"`
|
||||
HIGHDAY float64 `json:"HIGHDAY"`
|
||||
LOWDAY float64 `json:"LOWDAY"`
|
||||
CHANGEPCTHOUR float64 `json:"CHANGEPCTHOUR"`
|
||||
CHANGEPCTDAY float64 `json:"CHANGEPCTDAY"`
|
||||
CHANGEPCT24HOUR float64 `json:"CHANGEPCT24HOUR"`
|
||||
CHANGE24HOUR float64 `json:"CHANGE24HOUR"`
|
||||
}
|
||||
|
||||
type TokenDetails struct {
|
||||
ID string `json:"Id"`
|
||||
Name string `json:"Name"`
|
||||
Symbol string `json:"Symbol"`
|
||||
Description string `json:"Description"`
|
||||
TotalCoinsMined float64 `json:"TotalCoinsMined"`
|
||||
AssetLaunchDate string `json:"AssetLaunchDate"`
|
||||
AssetWhitepaperURL string `json:"AssetWhitepaperUrl"`
|
||||
AssetWebsiteURL string `json:"AssetWebsiteUrl"`
|
||||
BuiltOn string `json:"BuiltOn"`
|
||||
SmartContractAddress string `json:"SmartContractAddress"`
|
||||
}
|
||||
|
||||
type MarketDataProvider interface {
|
||||
FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error)
|
||||
FetchHistoricalDailyPrices(symbol string, currency string, limit int, allData bool, aggregate int) ([]HistoricalPrice, error)
|
||||
FetchHistoricalHourlyPrices(symbol string, currency string, limit int, aggregate int) ([]HistoricalPrice, error)
|
||||
FetchTokenMarketValues(symbols []string, currency string) (map[string]TokenMarketValues, error)
|
||||
FetchTokenDetails(symbols []string) (map[string]TokenDetails, error)
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package utils
|
||||
|
||||
import "strings"
|
||||
|
||||
var renameMapping = map[string]string{
|
||||
"STT": "SNT",
|
||||
}
|
||||
|
||||
func RenameSymbols(symbols []string) (renames []string) {
|
||||
for _, symbol := range symbols {
|
||||
renames = append(renames, GetRealSymbol(symbol))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetRealSymbol(symbol string) string {
|
||||
if val, ok := renameMapping[strings.ToUpper(symbol)]; ok {
|
||||
return val
|
||||
}
|
||||
return strings.ToUpper(symbol)
|
||||
}
|
||||
|
||||
func ChunkSymbols(symbols []string, chunkSizeOptional ...int) [][]string {
|
||||
var chunks [][]string
|
||||
chunkSize := 20
|
||||
if len(chunkSizeOptional) > 0 {
|
||||
chunkSize = chunkSizeOptional[0]
|
||||
}
|
||||
|
||||
for i := 0; i < len(symbols); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
|
||||
if end > len(symbols) {
|
||||
end = len(symbols)
|
||||
}
|
||||
|
||||
chunks = append(chunks, symbols[i:end])
|
||||
}
|
||||
|
||||
return chunks
|
||||
}
|
Loading…
Reference in New Issue