support lighter spot

This commit is contained in:
discountry
2025-12-07 21:37:58 +08:00
parent 5f79b134f2
commit 180f47b6e0
89 changed files with 11921 additions and 124 deletions
@@ -0,0 +1,6 @@
# HTTP
The HTTP module is a simple implementation that aims to provide just 2 things:
- `GetNextNonce` so that users can send transactions w/out calling managing nonces on their side
- `GetApiKey` so that users can call `CheckClient` from other sources, which makes sure that the client was configured correctly.
Other usages, like sending trades, fetching open orders or any WebSocket operations should happen outside the core SDK.
@@ -0,0 +1,45 @@
package http
import (
"crypto/tls"
"net"
"net/http"
"time"
core "github.com/elliottech/lighter-go/client"
)
var (
dialer = &net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 60 * time.Second,
}
transport = &http.Transport{
DialContext: dialer.DialContext,
MaxConnsPerHost: 1000,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 10 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
}
httpClient = &http.Client{
Timeout: time.Second * 30,
Transport: transport,
}
)
var _ core.MinimalHTTPClient = (*client)(nil)
type client struct {
endpoint string
}
func NewClient(baseUrl string) core.MinimalHTTPClient {
if baseUrl == "" {
return nil
}
return &client{
endpoint: baseUrl,
}
}
@@ -0,0 +1,27 @@
package http
const (
CodeOK = 200
)
type ResultCode struct {
Code int32 `json:"code,example=200"`
Message string `json:"message,omitempty"`
}
type NextNonce struct {
ResultCode
Nonce int64 `json:"nonce,example=722"`
}
type ApiKey struct {
AccountIndex int64 `json:"account_index,example=3"`
ApiKeyIndex uint8 `json:"api_key_index,example=0"`
Nonce int64 `json:"nonce,example=722"`
PublicKey string `json:"public_key"`
}
type AccountApiKeys struct {
ResultCode
ApiKeys []*ApiKey `json:"api_keys"`
}
@@ -0,0 +1,75 @@
package http
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
)
func (c *client) parseResultStatus(respBody []byte) error {
resultStatus := &ResultCode{}
if err := json.Unmarshal(respBody, resultStatus); err != nil {
return err
}
if resultStatus.Code != CodeOK {
return errors.New(resultStatus.Message)
}
return nil
}
func (c *client) getAndParseL2HTTPResponse(path string, params map[string]any, result interface{}) error {
u, err := url.Parse(c.endpoint)
if err != nil {
return err
}
u.Path = path
q := u.Query()
for k, v := range params {
q.Set(k, fmt.Sprintf("%v", v))
}
u.RawQuery = q.Encode()
resp, err := httpClient.Get(u.String())
if err != nil {
return err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != http.StatusOK {
return errors.New(string(body))
}
if err = c.parseResultStatus(body); err != nil {
return err
}
if err := json.Unmarshal(body, result); err != nil {
return err
}
return nil
}
func (c *client) GetNextNonce(accountIndex int64, apiKeyIndex uint8) (int64, error) {
result := &NextNonce{}
err := c.getAndParseL2HTTPResponse("api/v1/nextNonce", map[string]any{"account_index": accountIndex, "api_key_index": apiKeyIndex}, result)
if err != nil {
return -1, err
}
return result.Nonce, nil
}
func (c *client) GetApiKey(accountIndex int64, apiKeyIndex uint8) (string, error) {
result := &AccountApiKeys{}
err := c.getAndParseL2HTTPResponse("api/v1/apikeys", map[string]any{"account_index": accountIndex, "api_key_index": apiKeyIndex}, result)
if err != nil {
return "", err
}
if len(result.ApiKeys) == 0 {
return "", fmt.Errorf("no api keys returned")
}
return result.ApiKeys[0].PublicKey, nil
}