fix(wallet)_: limit max parameter length in cryptocompare price fetches
This commit is contained in:
parent
806b2587db
commit
5a19cc2a6e
|
@ -73,7 +73,15 @@ func NewClientWithParams(params Params) *Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) {
|
func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]map[string]float64, error) {
|
||||||
chunks := utils.ChunkSymbols(symbols, 60)
|
const maxFsymsLength = 300
|
||||||
|
chunkSymbolParams := utils.ChunkSymbolsParams{
|
||||||
|
MaxCharsPerChunk: maxFsymsLength,
|
||||||
|
ExtraCharsPerSymbol: 1, // joined with a comma
|
||||||
|
}
|
||||||
|
chunks, err := utils.ChunkSymbols(symbols, chunkSymbolParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
result := make(map[string]map[string]float64)
|
result := make(map[string]map[string]float64)
|
||||||
realCurrencies := utils.RenameSymbols(currencies)
|
realCurrencies := utils.RenameSymbols(currencies)
|
||||||
for _, smbls := range chunks {
|
for _, smbls := range chunks {
|
||||||
|
@ -82,6 +90,7 @@ func (c *Client) FetchPrices(symbols []string, currencies []string) (map[string]
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Add("fsyms", strings.Join(realSymbols, ","))
|
params.Add("fsyms", strings.Join(realSymbols, ","))
|
||||||
params.Add("tsyms", strings.Join(realCurrencies, ","))
|
params.Add("tsyms", strings.Join(realCurrencies, ","))
|
||||||
|
params.Add("relaxedValidation", "true")
|
||||||
params.Add("extraParams", extraParamStatus)
|
params.Add("extraParams", extraParamStatus)
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/data/pricemulti", c.baseURL)
|
url := fmt.Sprintf("%s/data/pricemulti", c.baseURL)
|
||||||
|
@ -129,7 +138,15 @@ func (c *Client) FetchTokenDetails(symbols []string) (map[string]thirdparty.Toke
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[string]thirdparty.TokenMarketValues, error) {
|
func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[string]thirdparty.TokenMarketValues, error) {
|
||||||
chunks := utils.ChunkSymbols(symbols)
|
const maxFsymsLength = 300
|
||||||
|
chunkSymbolParams := utils.ChunkSymbolsParams{
|
||||||
|
MaxCharsPerChunk: maxFsymsLength,
|
||||||
|
ExtraCharsPerSymbol: 1, // joined with a comma
|
||||||
|
}
|
||||||
|
chunks, err := utils.ChunkSymbols(symbols, chunkSymbolParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
realCurrency := utils.GetRealSymbol(currency)
|
realCurrency := utils.GetRealSymbol(currency)
|
||||||
item := map[string]thirdparty.TokenMarketValues{}
|
item := map[string]thirdparty.TokenMarketValues{}
|
||||||
for _, smbls := range chunks {
|
for _, smbls := range chunks {
|
||||||
|
@ -138,6 +155,7 @@ func (c *Client) FetchTokenMarketValues(symbols []string, currency string) (map[
|
||||||
params := url.Values{}
|
params := url.Values{}
|
||||||
params.Add("fsyms", strings.Join(realSymbols, ","))
|
params.Add("fsyms", strings.Join(realSymbols, ","))
|
||||||
params.Add("tsyms", realCurrency)
|
params.Add("tsyms", realCurrency)
|
||||||
|
params.Add("relaxedValidation", "true")
|
||||||
params.Add("extraParams", extraParamStatus)
|
params.Add("extraParams", extraParamStatus)
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/data/pricemultifull", c.baseURL)
|
url := fmt.Sprintf("%s/data/pricemultifull", c.baseURL)
|
||||||
|
|
|
@ -3,7 +3,17 @@ package cryptocompare
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/appdatabase"
|
||||||
|
"github.com/status-im/status-go/params"
|
||||||
|
mock_network "github.com/status-im/status-go/rpc/network/mock"
|
||||||
|
w_common "github.com/status-im/status-go/services/wallet/common"
|
||||||
|
"github.com/status-im/status-go/services/wallet/token"
|
||||||
|
"github.com/status-im/status-go/t/helpers"
|
||||||
|
"github.com/status-im/status-go/walletdatabase"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"go.uber.org/mock/gomock"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIDs(t *testing.T) {
|
func TestIDs(t *testing.T) {
|
||||||
|
@ -15,3 +25,71 @@ func TestIDs(t *testing.T) {
|
||||||
})
|
})
|
||||||
require.Equal(t, "testID", clientWithParams.ID())
|
require.Equal(t, "testID", clientWithParams.ID())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getTokenSymbols(t *testing.T) []string {
|
||||||
|
appDB, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
walletDB, err := helpers.SetupTestMemorySQLDB(walletdatabase.DbInitializer{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
networksList := []params.Network{
|
||||||
|
{
|
||||||
|
ChainID: w_common.EthereumMainnet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ChainID: w_common.OptimismMainnet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ChainID: w_common.ArbitrumMainnet,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrNetworkList := make([]*params.Network, 0, len(networksList))
|
||||||
|
for i := range networksList {
|
||||||
|
ptrNetworkList = append(ptrNetworkList, &networksList[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrl := gomock.NewController(t)
|
||||||
|
defer ctrl.Finish()
|
||||||
|
networkManager := mock_network.NewMockManagerInterface(ctrl)
|
||||||
|
networkManager.EXPECT().Get(gomock.Any()).Return(ptrNetworkList, nil).AnyTimes()
|
||||||
|
networkManager.EXPECT().GetAll().Return(ptrNetworkList, nil).AnyTimes()
|
||||||
|
networkManager.EXPECT().GetConfiguredNetworks().Return(networksList).AnyTimes()
|
||||||
|
|
||||||
|
// Skeleton token store to get full list of tokens
|
||||||
|
tm := token.NewTokenManager(walletDB, nil, nil, networkManager, appDB, nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
|
tokens, err := tm.GetAllTokens()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
symbolsMap := make(map[string]bool)
|
||||||
|
for _, token := range tokens {
|
||||||
|
symbolsMap[token.Symbol] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
symbols := make([]string, 0, len(symbolsMap))
|
||||||
|
for symbol := range symbolsMap {
|
||||||
|
symbols = append(symbols, symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbols
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFetchPrices(t *testing.T) {
|
||||||
|
t.Skip("Accesses network, only to be run in dev machine")
|
||||||
|
symbols := getTokenSymbols(t)
|
||||||
|
|
||||||
|
stdClient := NewClient()
|
||||||
|
_, err := stdClient.FetchPrices(symbols, []string{"USD"})
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFetchTokenMarketValues(t *testing.T) {
|
||||||
|
t.Skip("Accesses network, only to be run manually in dev machine")
|
||||||
|
symbols := getTokenSymbols(t)
|
||||||
|
|
||||||
|
stdClient := NewClient()
|
||||||
|
_, err := stdClient.FetchTokenMarketValues(symbols, "USD")
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
var renameMapping = map[string]string{
|
var renameMapping = map[string]string{
|
||||||
"STT": "SNT",
|
"STT": "SNT",
|
||||||
|
@ -32,22 +35,36 @@ func GetRealSymbol(symbol string) string {
|
||||||
return strings.ToUpper(symbol)
|
return strings.ToUpper(symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChunkSymbols(symbols []string, chunkSizeOptional ...int) [][]string {
|
type ChunkSymbolsParams struct {
|
||||||
var chunks [][]string
|
MaxSymbolsPerChunk int
|
||||||
chunkSize := 100
|
MaxCharsPerChunk int
|
||||||
if len(chunkSizeOptional) > 0 {
|
ExtraCharsPerSymbol int
|
||||||
chunkSize = chunkSizeOptional[0]
|
}
|
||||||
}
|
|
||||||
|
func ChunkSymbols(symbols []string, params ChunkSymbolsParams) ([][]string, error) {
|
||||||
for i := 0; i < len(symbols); i += chunkSize {
|
var chunks [][]string
|
||||||
end := i + chunkSize
|
if len(symbols) == 0 {
|
||||||
|
return chunks, nil
|
||||||
if end > len(symbols) {
|
}
|
||||||
end = len(symbols)
|
|
||||||
}
|
chunk := make([]string, 0, 100)
|
||||||
|
chunkChars := 0
|
||||||
chunks = append(chunks, symbols[i:end])
|
for _, symbol := range symbols {
|
||||||
}
|
symbolChars := len(symbol) + params.ExtraCharsPerSymbol
|
||||||
|
if params.MaxCharsPerChunk > 0 && symbolChars > params.MaxCharsPerChunk {
|
||||||
return chunks
|
return nil, errors.New("chunk cannot fit symbol: " + symbol)
|
||||||
|
}
|
||||||
|
if (params.MaxCharsPerChunk > 0 && chunkChars+symbolChars > params.MaxCharsPerChunk) ||
|
||||||
|
(params.MaxSymbolsPerChunk > 0 && len(chunk) >= params.MaxSymbolsPerChunk) {
|
||||||
|
// Max chars/symbols reached, store chunk and start a new one
|
||||||
|
chunks = append(chunks, chunk)
|
||||||
|
chunk = make([]string, 0, 100)
|
||||||
|
chunkChars = 0
|
||||||
|
}
|
||||||
|
chunk = append(chunk, symbol)
|
||||||
|
chunkChars += symbolChars
|
||||||
|
}
|
||||||
|
chunks = append(chunks, chunk)
|
||||||
|
|
||||||
|
return chunks, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_RenameSymbols(t *testing.T) {
|
||||||
|
symbols := []string{"STT", "ETH", "BTC"}
|
||||||
|
renames := RenameSymbols(symbols)
|
||||||
|
require.Equal(t, []string{"SNT", "ETH", "BTC"}, renames)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_RemoveDuplicates(t *testing.T) {
|
||||||
|
strings := []string{"STT", "ETH", "BTC", "ETH", "BTC"}
|
||||||
|
uniqueStrings := RemoveDuplicates(strings)
|
||||||
|
require.Equal(t, []string{"STT", "ETH", "BTC"}, uniqueStrings)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_GetRealSymbol(t *testing.T) {
|
||||||
|
require.Equal(t, "SNT", GetRealSymbol("STT"))
|
||||||
|
require.Equal(t, "ETH", GetRealSymbol("ETH"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_ChunkSymbols(t *testing.T) {
|
||||||
|
symbols := []string{"STT", "ETH", "BTC"}
|
||||||
|
params := ChunkSymbolsParams{MaxCharsPerChunk: 10, ExtraCharsPerSymbol: 1}
|
||||||
|
chunks, err := ChunkSymbols(symbols, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, [][]string{{"STT", "ETH"}, {"BTC"}}, chunks)
|
||||||
|
|
||||||
|
params = ChunkSymbolsParams{MaxCharsPerChunk: 10, ExtraCharsPerSymbol: 2}
|
||||||
|
chunks, err = ChunkSymbols(symbols, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, [][]string{{"STT", "ETH"}, {"BTC"}}, chunks)
|
||||||
|
|
||||||
|
params = ChunkSymbolsParams{MaxCharsPerChunk: 4, ExtraCharsPerSymbol: 1}
|
||||||
|
chunks, err = ChunkSymbols(symbols, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, [][]string{{"STT"}, {"ETH"}, {"BTC"}}, chunks)
|
||||||
|
|
||||||
|
params = ChunkSymbolsParams{MaxSymbolsPerChunk: 1, MaxCharsPerChunk: 10, ExtraCharsPerSymbol: 2}
|
||||||
|
chunks, err = ChunkSymbols(symbols, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, [][]string{{"STT"}, {"ETH"}, {"BTC"}}, chunks)
|
||||||
|
|
||||||
|
params = ChunkSymbolsParams{MaxCharsPerChunk: 9, ExtraCharsPerSymbol: 2}
|
||||||
|
chunks, err = ChunkSymbols(symbols, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, [][]string{{"STT"}, {"ETH"}, {"BTC"}}, chunks)
|
||||||
|
|
||||||
|
params = ChunkSymbolsParams{MaxCharsPerChunk: 2, ExtraCharsPerSymbol: 1}
|
||||||
|
chunks, err = ChunkSymbols([]string{}, params)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, chunks, 0)
|
||||||
|
|
||||||
|
params = ChunkSymbolsParams{MaxCharsPerChunk: 2, ExtraCharsPerSymbol: 1}
|
||||||
|
_, err = ChunkSymbols(symbols, params)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
Loading…
Reference in New Issue