HitBTC: Fix panic in FetchTradablePairs (#2091)

```
❯ go test ./engine/... -run TestGetDefaultConfig
--- FAIL: TestGetDefaultConfigurations (2.01s)
    --- FAIL: TestGetDefaultConfigurations/hitbtc (0.39s)
panic: runtime error: slice bounds out of range [:-1] [recovered, repanicked]
```

The code was using strings.Index to find the quote currency position in the
symbol ID, then slicing the ID. When Index returns -1 (not found), it caused
a panic with 'slice bounds out of range [:-1]'.

Fixed by using the BaseCurrency and QuoteCurrency fields directly from the
Symbol struct, which is the correct approach and what the API provides.
This commit is contained in:
Gareth Kirwan
2025-10-22 14:25:13 +07:00
committed by GitHub
parent 5d4e5b0627
commit 0f70cfd8b6
3 changed files with 18 additions and 12 deletions

View File

@@ -103,8 +103,8 @@ func (e *Exchange) GetSymbols(ctx context.Context, symbol string) ([]string, err
// GetSymbolsDetailed is the same as above but returns an array of symbols with
// all their details.
func (e *Exchange) GetSymbolsDetailed(ctx context.Context) ([]Symbol, error) {
var resp []Symbol
func (e *Exchange) GetSymbolsDetailed(ctx context.Context) ([]*Symbol, error) {
var resp []*Symbol
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, apiV2Symbol, &resp)
}

View File

@@ -992,9 +992,14 @@ func TestGetOrderInfo(t *testing.T) {
func TestFetchTradablePairs(t *testing.T) {
t.Parallel()
if _, err := e.FetchTradablePairs(t.Context(), asset.Spot); err != nil {
t.Fatal(err)
}
_, err := e.FetchTradablePairs(t.Context(), asset.Futures)
assert.ErrorIs(t, err, asset.ErrNotSupported)
r, err := e.FetchTradablePairs(t.Context(), asset.Spot)
require.NoError(t, err)
require.NotEmpty(t, r)
assert.Contains(t, r, spotPair, "BTC-USD should be in the fetched pairs")
}
func TestGetCurrencyTradeURL(t *testing.T) {

View File

@@ -177,21 +177,22 @@ func (e *Exchange) Setup(exch *config.Exchange) error {
}
// FetchTradablePairs returns a list of the exchanges tradable pairs
func (e *Exchange) FetchTradablePairs(ctx context.Context, _ asset.Item) (currency.Pairs, error) {
func (e *Exchange) FetchTradablePairs(ctx context.Context, a asset.Item) (currency.Pairs, error) {
if a != asset.Spot {
return nil, fmt.Errorf("%w: %q", asset.ErrNotSupported, a)
}
symbols, err := e.GetSymbolsDetailed(ctx)
if err != nil {
return nil, err
}
pairs := make([]currency.Pair, len(symbols))
for x := range symbols {
index := strings.Index(symbols[x].ID, symbols[x].QuoteCurrency)
var pair currency.Pair
pair, err = currency.NewPairFromStrings(symbols[x].ID[:index], symbols[x].ID[index:])
if err != nil {
for i, s := range symbols {
// s.QuoteCurrency is actually settlement currency, so trim the base currency to get the real quote currency
if pairs[i], err = currency.NewPairFromStrings(s.BaseCurrency, strings.TrimPrefix(s.ID, s.BaseCurrency)); err != nil {
return nil, err
}
pairs[x] = pair
}
return pairs, nil
}