fix(wallet)_: limit max parameter length in cryptocompare price fetches

This commit is contained in:
Dario Gabriel Lipicar 2024-10-16 23:25:38 -03:00
parent 806b2587db
commit 5a19cc2a6e
No known key found for this signature in database
GPG Key ID: 9625E9494309D203
4 changed files with 195 additions and 21 deletions

View File

@ -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)

View File

@ -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)
}

View File

@ -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
} }

View File

@ -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)
}