mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-17 17:34:39 +08:00
fix(hyperliquid): stop SDK init panic from crashing trader creation with 500
go-hyperliquid's NewExchange auto-fetches meta/spotMeta/perpDexs and panics if any of those API calls fail (NewInfo: panic(err)). The quick trade flow constructs a probe trader inside POST /api/traders, so any transient Hyperliquid API hiccup crashed the request with a recovered panic and a bare 500. Wrap the constructor in initExchangeClient, which converts the panic into an error that surfaces through the existing exchange-probe validation path as an honest, retryable message.
This commit is contained in:
22
trader/hyperliquid/client_init.go
Normal file
22
trader/hyperliquid/client_init.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package hyperliquid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
hl "github.com/sonirico/go-hyperliquid"
|
||||||
|
)
|
||||||
|
|
||||||
|
// initExchangeClient runs the SDK exchange constructor and converts its
|
||||||
|
// panic-on-failure behavior into an error. go-hyperliquid's NewExchange
|
||||||
|
// auto-fetches meta/spotMeta/perpDexs when they are passed as nil and panics
|
||||||
|
// if any of those API calls fail (NewInfo: panic(err)), so a transient
|
||||||
|
// Hyperliquid API hiccup would otherwise crash the calling HTTP handler
|
||||||
|
// with a 500.
|
||||||
|
func initExchangeClient(build func() *hl.Exchange) (ex *hl.Exchange, err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
err = fmt.Errorf("hyperliquid client initialization failed: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return build(), nil
|
||||||
|
}
|
||||||
37
trader/hyperliquid/client_init_test.go
Normal file
37
trader/hyperliquid/client_init_test.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package hyperliquid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
hl "github.com/sonirico/go-hyperliquid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInitExchangeClientConvertsPanicToError(t *testing.T) {
|
||||||
|
// The SDK constructor panics when its automatic meta fetch fails
|
||||||
|
// (go-hyperliquid info.go NewInfo: panic(err)). The wrapper must turn
|
||||||
|
// that into an error instead of crashing the HTTP handler.
|
||||||
|
_, err := initExchangeClient(func() *hl.Exchange {
|
||||||
|
panic(errors.New("failed to fetch meta: API error 0: "))
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error when the SDK constructor panics, got nil")
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "failed to fetch meta") {
|
||||||
|
t.Errorf("error should carry the panic cause, got %q", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInitExchangeClientPassesThroughSuccess(t *testing.T) {
|
||||||
|
want := &hl.Exchange{}
|
||||||
|
got, err := initExchangeClient(func() *hl.Exchange {
|
||||||
|
return want
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if got != want {
|
||||||
|
t.Fatal("wrapper must return the constructed exchange unchanged")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -143,16 +143,23 @@ func NewHyperliquidTrader(privateKeyHex string, walletAddr string, testnet bool,
|
|||||||
// v0.36 fixed the spot-meta indexing panic that crashed earlier versions
|
// v0.36 fixed the spot-meta indexing panic that crashed earlier versions
|
||||||
// when Hyperliquid added new spot tokens whose Tokens[0] index pointed
|
// when Hyperliquid added new spot tokens whose Tokens[0] index pointed
|
||||||
// past the Tokens array end.
|
// past the Tokens array end.
|
||||||
exchange := hyperliquid.NewExchange(
|
// The constructor still panics if any auto-fetch fails, so it runs inside
|
||||||
ctx,
|
// initExchangeClient which converts the panic into a returned error.
|
||||||
privateKey,
|
exchange, err := initExchangeClient(func() *hyperliquid.Exchange {
|
||||||
apiURL,
|
return hyperliquid.NewExchange(
|
||||||
nil, // Meta — fetched automatically
|
ctx,
|
||||||
"", // vault address (empty for personal account)
|
privateKey,
|
||||||
walletAddr, // wallet address
|
apiURL,
|
||||||
nil, // SpotMeta — fetched automatically
|
nil, // Meta — fetched automatically
|
||||||
nil, // perpDexs — fetched automatically
|
"", // vault address (empty for personal account)
|
||||||
)
|
walletAddr, // wallet address
|
||||||
|
nil, // SpotMeta — fetched automatically
|
||||||
|
nil, // perpDexs — fetched automatically
|
||||||
|
)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Hyperliquid API is temporarily unavailable, please retry: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
logger.Infof("✓ Hyperliquid trader initialized successfully (testnet=%v, wallet=%s)", testnet, walletAddr)
|
logger.Infof("✓ Hyperliquid trader initialized successfully (testnet=%v, wallet=%s)", testnet, walletAddr)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user