mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-14 07:26:47 +00:00
requester: defer execution of request.Item generation in closure (#723)
* requester: defer execution of request.Item generation in closure. * bithumb: fix issue * coinut/itbit: fix linter issues * binance: fix bug on recvWindow setting * requester: standardize sendpayload + add readme update * nonce: remove inc() function * request: defer unlockiflocked * binance: revert changes for open orders * btcmarkets: defer auth generation functionality, rm context deadline as this will be created just before sending HTTP request. * binance: move const to top * exmo: remove debug output as its generated in the requester function * ftx: defer auth functionality * requester: move error to top * bittrex: defer auth functionality * bitmex: defer auth functionality and remove deadline as generation occurs after rate limiting. * btse: defer auth functionality * coinbasepro: defer auth functionality and removed context deadline as this is generated after rate limiting * coinbene: defer auth functionality and remove context deadline as this is generated after rate limiting * huobi: defer auth functionality and remove context deadline as this is generated after rate limiting * huobi-futures: defer auth functionality and remove context deadline as this is generated after rate limiting * kraken: defer auth functionality and remove context deadline as this is generated after rate limiting * kraken: remove deadline protection for timestamp generation * okgroup: defer auth functionality and remove context deadline as this is generated after rate limiting * poloniex: defer auth functionality * zb: defer auth functionality and remove context deadline as this is generated after rate limiting * exchanges: clean up log output which are done and inspected in the requester package * binance: fix path bug on every retry, rm timeout context as this is not needed * coinbene: fix path bug on retry * binance: consolidate functionality * coinbene: fix linter issues * poloniex: linter fix * kraken: change add -> set * bitstamp: fix path bug for retry * BTSE: fix retry path bug * coinbene: fix path bug whoopsie by me * gateio: fix bug where on retry it does not reset reader * localbitcoins: fix path bug on retry * zb: change domain to land * exchanges: make sure io.Reader is generated every request * exchanges: move reader generation into function scope * wrapper_issues: setup exchange manager * engine: expand withdraw manager test * engine: dont look for environment * bitstamp: fix pathing bug (@thrasher-) * engine/withdraw_manager: purge tests as this is covered in repository withdraw
This commit is contained in:
@@ -1279,11 +1279,14 @@ func sendGetReq(path string, result interface{}) error {
|
||||
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
|
||||
request.WithLimiter(request.NewBasicRateLimit(time.Second, 100)))
|
||||
}
|
||||
return requester.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: result,
|
||||
Verbose: verbose})
|
||||
Verbose: verbose}
|
||||
return requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
// sendAuthReq sends auth req
|
||||
@@ -1291,11 +1294,14 @@ func sendAuthReq(method, path string, result interface{}) error {
|
||||
requester := request.New("Apichecker",
|
||||
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
|
||||
request.WithLimiter(request.NewBasicRateLimit(time.Second*10, 100)))
|
||||
return requester.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Result: result,
|
||||
Verbose: verbose})
|
||||
Verbose: verbose}
|
||||
return requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
// trelloGetBoardID gets all board ids on trello for a given user
|
||||
|
||||
@@ -40,6 +40,7 @@ func main() {
|
||||
log.Fatalf("Failed to initialise engine. Err: %s", err)
|
||||
}
|
||||
engine.Bot = bot
|
||||
bot.ExchangeManager = engine.SetupExchangeManager()
|
||||
|
||||
bot.Settings = engine.Settings{
|
||||
DisableExchangeAutoPairUpdates: true,
|
||||
|
||||
@@ -677,14 +677,15 @@ func (c *Coinmarketcap) SendHTTPRequest(method, endpoint string, v url.Values, r
|
||||
if v != nil {
|
||||
path = path + "?" + v.Encode()
|
||||
}
|
||||
|
||||
return c.Requester.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(""),
|
||||
Result: result,
|
||||
Verbose: c.Verbose})
|
||||
Verbose: c.Verbose}
|
||||
return c.Requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
// CheckAccountPlan checks your current account plan to the minimal account
|
||||
|
||||
@@ -160,13 +160,16 @@ func (c *CurrencyConverter) SendHTTPRequest(endPoint string, values url.Values,
|
||||
path = fmt.Sprintf("%s%s%s?", APIEndpointURL, APIEndpointVersion, endPoint)
|
||||
values.Set("apiKey", c.APIKey)
|
||||
}
|
||||
path += values.Encode()
|
||||
|
||||
err := c.Requester.SendPayload(context.Background(), &request.Item{
|
||||
path += values.Encode()
|
||||
item := &request.Item{
|
||||
Method: path,
|
||||
Result: result,
|
||||
AuthRequest: auth,
|
||||
Verbose: c.Verbose})
|
||||
Verbose: c.Verbose}
|
||||
err := c.Requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("currency converter API SendHTTPRequest error %s with path %s",
|
||||
|
||||
@@ -206,11 +206,13 @@ func (c *CurrencyLayer) SendHTTPRequest(endPoint string, values url.Values, resu
|
||||
path = APIEndpointURLSSL + endPoint + "?"
|
||||
}
|
||||
path += values.Encode()
|
||||
|
||||
return c.Requester.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: &result,
|
||||
AuthRequest: auth,
|
||||
Verbose: c.Verbose})
|
||||
Verbose: c.Verbose}
|
||||
return c.Requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -253,10 +253,13 @@ func (e *ExchangeRateHost) GetRates(baseCurrency, symbols string) (map[string]fl
|
||||
// SendHTTPRequest sends a typical get request
|
||||
func (e *ExchangeRateHost) SendHTTPRequest(endpoint string, v url.Values, result interface{}) error {
|
||||
path := common.EncodeURLValues(exchangeRateHostURL+"/"+endpoint, v)
|
||||
return e.Requester.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: &result,
|
||||
Verbose: e.Verbose,
|
||||
}
|
||||
return e.Requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -261,11 +261,14 @@ func (e *ExchangeRates) SendHTTPRequest(endPoint string, values url.Values, resu
|
||||
protocolScheme = "http://"
|
||||
}
|
||||
path := common.EncodeURLValues(protocolScheme+exchangeRatesAPI+"/v1/"+endPoint, values)
|
||||
err := e.Requester.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: result,
|
||||
Verbose: e.Verbose,
|
||||
}
|
||||
err := e.Requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("exchangeRatesAPI: SendHTTPRequest error %s with path %s",
|
||||
|
||||
@@ -230,11 +230,13 @@ func (f *Fixer) SendOpenHTTPRequest(endpoint string, v url.Values, result interf
|
||||
path = fixerAPISSL + endpoint + "?" + v.Encode()
|
||||
auth = true
|
||||
}
|
||||
|
||||
return f.Requester.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: &result,
|
||||
AuthRequest: auth,
|
||||
Verbose: f.Verbose})
|
||||
Verbose: f.Verbose}
|
||||
return f.Requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -218,9 +218,12 @@ func (o *OXR) SendHTTPRequest(endpoint string, values url.Values, result interfa
|
||||
headers["Authorization"] = "Token " + o.APIKey
|
||||
path := APIURL + endpoint + "?" + values.Encode()
|
||||
|
||||
return o.Requester.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: result,
|
||||
Verbose: o.Verbose})
|
||||
Verbose: o.Verbose}
|
||||
return o.Requester.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -351,14 +351,28 @@ This will generate a readme file for the exchange which can be found in the new
|
||||
```go
|
||||
// SendHTTPRequest sends an unauthenticated HTTP request
|
||||
func (f *FTX) SendHTTPRequest(path string, result interface{}) error {
|
||||
return f.SendPayload(context.Background(), &request.Item{
|
||||
// This is used to generate the *http.Request, used in conjunction with the
|
||||
// generate functionality below.
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: result,
|
||||
Verbose: f.Verbose,
|
||||
HTTPDebugging: f.HTTPDebugging,
|
||||
HTTPRecording: f.HTTPRecording,
|
||||
})
|
||||
}
|
||||
|
||||
// Request function that closes over the above request.Item values, which
|
||||
// executes on every attempt after rate limiting.
|
||||
generate := func() (*request.Item, error) { return item, nil }
|
||||
|
||||
endpoint := request.Unset // Used in conjunction with the rate limiting
|
||||
// system defined in the exchange package to slow down outbound requests
|
||||
// depending on each individual endpoint.
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
return f.SendPayload(ctx, endpoint, generate)
|
||||
}
|
||||
```
|
||||
|
||||
@@ -445,38 +459,56 @@ Authenticated request function is created based on the way the exchange document
|
||||
```go
|
||||
// SendAuthHTTPRequest sends an authenticated request
|
||||
func (f *FTX) SendAuthHTTPRequest(method, path string, data, result interface{}) error {
|
||||
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
var body io.Reader
|
||||
var hmac, payload []byte
|
||||
var err error
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
// A potential example below of closing over authenticated variables which may
|
||||
// be required to regenerate on every request between each attempt after rate
|
||||
// limiting. This is for when signatures are based on timestamps/nonces that are
|
||||
// within time receive windows. NOTE: This is not always necessary and the above
|
||||
// SendHTTPRequest example will suffice.
|
||||
generate := func() (*request.Item, error) {
|
||||
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
var body io.Reader
|
||||
var hmac, payload []byte
|
||||
var err error
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
sigPayload := ts + method + "/api" + path + string(payload)
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
} else {
|
||||
sigPayload := ts + method + "/api" + path
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
sigPayload := ts + method + "/api" + path + string(payload)
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
} else {
|
||||
sigPayload := ts + method + "/api" + path
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["FTX-KEY"] = f.API.Credentials.Key
|
||||
headers["FTX-SIGN"] = crypto.HexEncodeToString(hmac)
|
||||
headers["FTX-TS"] = ts
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
// This is used to generate the *http.Request.
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: ftxAPIURL + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: f.Verbose,
|
||||
HTTPDebugging: f.HTTPDebugging,
|
||||
HTTPRecording: f.HTTPRecording,
|
||||
}
|
||||
return item, nil
|
||||
}
|
||||
headers := make(map[string]string)
|
||||
headers["FTX-KEY"] = f.API.Credentials.Key
|
||||
headers["FTX-SIGN"] = crypto.HexEncodeToString(hmac)
|
||||
headers["FTX-TS"] = ts
|
||||
headers["Content-Type"] = "application/json"
|
||||
return f.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: ftxAPIURL + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: f.Verbose,
|
||||
HTTPDebugging: f.HTTPDebugging,
|
||||
HTTPRecording: f.HTTPRecording,
|
||||
})
|
||||
|
||||
endpoint := request.Unset // Used in conjunction with the rate limiting
|
||||
// system defined in the exchange package to slow down outbound requests
|
||||
// depending on each individual endpoint.
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
return f.SendPayload(ctx, endpoint, generate)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -150,9 +150,19 @@ func TestWithdrawalEventByExchange(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = m.WithdrawalEventByExchange(exchangeName, 1)
|
||||
if err == nil {
|
||||
t.Error(err)
|
||||
|
||||
_, err = (*WithdrawManager)(nil).WithdrawalEventByExchange("xxx", 0)
|
||||
if !errors.Is(err, ErrNilSubsystem) {
|
||||
t.Errorf("received: %v but expected: %v",
|
||||
err,
|
||||
ErrNilSubsystem)
|
||||
}
|
||||
|
||||
_, err = m.WithdrawalEventByExchange("xxx", 0)
|
||||
if !errors.Is(err, ErrExchangeNotFound) {
|
||||
t.Errorf("received: %v but expected: %v",
|
||||
err,
|
||||
ErrExchangeNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,9 +173,19 @@ func TestWithdrawEventByDate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = m.WithdrawEventByDate(exchangeName, time.Now(), time.Now(), 1)
|
||||
if err == nil {
|
||||
t.Error(err)
|
||||
|
||||
_, err = (*WithdrawManager)(nil).WithdrawEventByDate("xxx", time.Now(), time.Now(), 1)
|
||||
if !errors.Is(err, ErrNilSubsystem) {
|
||||
t.Errorf("received: %v but expected: %v",
|
||||
err,
|
||||
ErrNilSubsystem)
|
||||
}
|
||||
|
||||
_, err = m.WithdrawEventByDate("xxx", time.Now(), time.Now(), 1)
|
||||
if !errors.Is(err, ErrExchangeNotFound) {
|
||||
t.Errorf("received: %v but expected: %v",
|
||||
err,
|
||||
ErrExchangeNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,8 +196,18 @@ func TestWithdrawalEventByExchangeID(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, err = m.WithdrawalEventByExchangeID(exchangeName, exchangeName)
|
||||
if err == nil {
|
||||
t.Error(err)
|
||||
|
||||
_, err = (*WithdrawManager)(nil).WithdrawalEventByExchangeID("xxx", "xxx")
|
||||
if !errors.Is(err, ErrNilSubsystem) {
|
||||
t.Errorf("received: %v but expected: %v",
|
||||
err,
|
||||
ErrNilSubsystem)
|
||||
}
|
||||
|
||||
_, err = m.WithdrawalEventByExchangeID("xxx", "xxx")
|
||||
if !errors.Is(err, ErrExchangeNotFound) {
|
||||
t.Errorf("received: %v but expected: %v",
|
||||
err,
|
||||
ErrExchangeNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,15 +535,19 @@ func (a *Alphapoint) SendHTTPRequest(ep exchange.URL, method, path string, data
|
||||
return errors.New("unable to JSON request")
|
||||
}
|
||||
|
||||
return a.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(PayloadJSON),
|
||||
Result: result,
|
||||
Verbose: a.Verbose,
|
||||
HTTPDebugging: a.HTTPDebugging,
|
||||
HTTPRecording: a.HTTPRecording})
|
||||
HTTPRecording: a.HTTPRecording}
|
||||
|
||||
return a.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
item.Body = bytes.NewBuffer(PayloadJSON)
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequest sends an authenticated request
|
||||
@@ -574,15 +578,19 @@ func (a *Alphapoint) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path
|
||||
return errors.New("unable to JSON request")
|
||||
}
|
||||
|
||||
return a.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(PayloadJSON),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: a.Verbose,
|
||||
HTTPDebugging: a.HTTPDebugging,
|
||||
HTTPRecording: a.HTTPRecording})
|
||||
HTTPRecording: a.HTTPRecording}
|
||||
|
||||
return a.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
item.Body = bytes.NewBuffer(PayloadJSON)
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package binance
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -71,6 +70,8 @@ const (
|
||||
assetDetail = "/wapi/v3/assetDetail.html"
|
||||
undocumentedInterestHistory = "/gateway-api/v1/public/isolated-margin/pair/vip-level"
|
||||
undocumentedCrossMarginInterestHistory = "/gateway-api/v1/friendly/margin/vip/spec/list-all"
|
||||
|
||||
defaultRecvWindow = 5 * time.Second
|
||||
)
|
||||
|
||||
// GetInterestHistory gets interest history for currency/currencies provided
|
||||
@@ -568,8 +569,8 @@ func (b *Binance) CancelExistingOrder(symbol currency.Pair, orderID int64, origC
|
||||
}
|
||||
|
||||
// OpenOrders Current open orders. Get all open orders on a symbol.
|
||||
// Careful when accessing this with no symbol: The number of requests counted against the rate limiter
|
||||
// is significantly higher
|
||||
// Careful when accessing this with no symbol: The number of requests counted
|
||||
// against the rate limiter is significantly higher
|
||||
func (b *Binance) OpenOrders(pair currency.Pair) ([]QueryOrderData, error) {
|
||||
var resp []QueryOrderData
|
||||
params := url.Values{}
|
||||
@@ -582,7 +583,8 @@ func (b *Binance) OpenOrders(pair currency.Pair) ([]QueryOrderData, error) {
|
||||
}
|
||||
params.Add("symbol", p)
|
||||
} else {
|
||||
// extend the receive window when all currencies to prevent "recvwindow" error
|
||||
// extend the receive window when all currencies to prevent "recvwindow"
|
||||
// error
|
||||
params.Set("recvWindow", "10000")
|
||||
}
|
||||
if err := b.SendAuthHTTPRequest(exchange.RestSpotSupplementary, http.MethodGet, openOrders, params, openOrdersLimit(p), &resp); err != nil {
|
||||
@@ -682,14 +684,17 @@ func (b *Binance) SendHTTPRequest(ePath exchange.URL, path string, f request.End
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpointPath + path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: f})
|
||||
HTTPRecording: b.HTTPRecording}
|
||||
|
||||
return b.SendPayload(context.Background(), f, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
// SendAPIKeyHTTPRequest is a special API request where the api key is
|
||||
@@ -701,15 +706,18 @@ func (b *Binance) SendAPIKeyHTTPRequest(ePath exchange.URL, path string, f reque
|
||||
}
|
||||
headers := make(map[string]string)
|
||||
headers["X-MBX-APIKEY"] = b.API.Credentials.Key
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpointPath + path,
|
||||
Headers: headers,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: f})
|
||||
HTTPRecording: b.HTTPRecording}
|
||||
|
||||
return b.SendPayload(context.Background(), f, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
// SendAuthHTTPRequest sends an authenticated HTTP request
|
||||
@@ -721,57 +729,45 @@ func (b *Binance) SendAuthHTTPRequest(ePath exchange.URL, method, path string, p
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
path = endpointPath + path
|
||||
|
||||
if params == nil {
|
||||
params = url.Values{}
|
||||
}
|
||||
recvWindow := 5 * time.Second
|
||||
if params.Get("recvWindow") != "" {
|
||||
// convert recvWindow value into time.Duration
|
||||
var recvWindowParam int64
|
||||
recvWindowParam, err = convert.Int64FromString(params.Get("recvWindow"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
recvWindow = time.Duration(recvWindowParam) * time.Millisecond
|
||||
} else {
|
||||
params.Set("recvWindow", strconv.FormatInt(convert.RecvWindow(recvWindow), 10))
|
||||
}
|
||||
params.Set("recvWindow", strconv.FormatInt(convert.RecvWindow(recvWindow), 10))
|
||||
params.Set("timestamp", strconv.FormatInt(time.Now().Unix()*1000, 10))
|
||||
signature := params.Encode()
|
||||
hmacSigned := crypto.GetHMAC(crypto.HashSHA256, []byte(signature), []byte(b.API.Credentials.Secret))
|
||||
hmacSignedStr := crypto.HexEncodeToString(hmacSigned)
|
||||
headers := make(map[string]string)
|
||||
headers["X-MBX-APIKEY"] = b.API.Credentials.Key
|
||||
if b.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "sent path: %s", path)
|
||||
|
||||
if params.Get("recvWindow") == "" {
|
||||
params.Set("recvWindow", strconv.FormatInt(convert.RecvWindow(defaultRecvWindow), 10))
|
||||
}
|
||||
|
||||
path = common.EncodeURLValues(path, params)
|
||||
path += "&signature=" + hmacSignedStr
|
||||
interim := json.RawMessage{}
|
||||
err = b.SendPayload(context.Background(), f, func() (*request.Item, error) {
|
||||
fullPath := endpointPath + path
|
||||
params.Set("timestamp", strconv.FormatInt(time.Now().Unix()*1000, 10))
|
||||
signature := params.Encode()
|
||||
hmacSigned := crypto.GetHMAC(crypto.HashSHA256, []byte(signature), []byte(b.API.Credentials.Secret))
|
||||
hmacSignedStr := crypto.HexEncodeToString(hmacSigned)
|
||||
headers := make(map[string]string)
|
||||
headers["X-MBX-APIKEY"] = b.API.Credentials.Key
|
||||
fullPath = common.EncodeURLValues(fullPath, params)
|
||||
fullPath += "&signature=" + hmacSignedStr
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: fullPath,
|
||||
Headers: headers,
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errCap := struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"msg"`
|
||||
Code int64 `json:"code"`
|
||||
}{}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), recvWindow)
|
||||
defer cancel()
|
||||
err = b.SendPayload(ctx, &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(nil),
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: f})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(interim, &errCap); err == nil {
|
||||
if !errCap.Success && errCap.Message != "" && errCap.Code != 200 {
|
||||
return errors.New(errCap.Message)
|
||||
@@ -937,20 +933,23 @@ func (b *Binance) GetWsAuthStreamKey() (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var resp UserAccountStream
|
||||
path := endpointPath + userAccountStream
|
||||
headers := make(map[string]string)
|
||||
headers["X-MBX-APIKEY"] = b.API.Credentials.Key
|
||||
err = b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Path: endpointPath + userAccountStream,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(nil),
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}
|
||||
|
||||
err = b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -974,15 +973,18 @@ func (b *Binance) MaintainWsAuthStreamKey() error {
|
||||
path = common.EncodeURLValues(path, params)
|
||||
headers := make(map[string]string)
|
||||
headers["X-MBX-APIKEY"] = b.API.Credentials.Key
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodPut,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(nil),
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}
|
||||
|
||||
return b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
|
||||
@@ -1501,14 +1500,17 @@ func (b *Bitfinex) SendHTTPRequest(ep exchange.URL, path string, result interfac
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: e})
|
||||
HTTPRecording: b.HTTPRecording}
|
||||
|
||||
return b.SendPayload(context.Background(), e, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequest sends an autheticated http request and json
|
||||
@@ -1523,44 +1525,42 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path st
|
||||
return err
|
||||
}
|
||||
|
||||
n := b.Requester.GetNonce(true)
|
||||
fullPath := ePoint + bitfinexAPIVersion + path
|
||||
return b.SendPayload(context.Background(), endpoint, func() (*request.Item, error) {
|
||||
n := b.Requester.GetNonce(true)
|
||||
req := make(map[string]interface{})
|
||||
req["request"] = bitfinexAPIVersion + path
|
||||
req["nonce"] = n.String()
|
||||
|
||||
req := make(map[string]interface{})
|
||||
req["request"] = bitfinexAPIVersion + path
|
||||
req["nonce"] = n.String()
|
||||
for key, value := range params {
|
||||
req[key] = value
|
||||
}
|
||||
|
||||
for key, value := range params {
|
||||
req[key] = value
|
||||
}
|
||||
PayloadJSON, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
PayloadJSON, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return errors.New("sendAuthenticatedAPIRequest: unable to JSON request")
|
||||
}
|
||||
PayloadBase64 := crypto.Base64Encode(PayloadJSON)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512_384,
|
||||
[]byte(PayloadBase64),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["X-BFX-APIKEY"] = b.API.Credentials.Key
|
||||
headers["X-BFX-PAYLOAD"] = PayloadBase64
|
||||
headers["X-BFX-SIGNATURE"] = crypto.HexEncodeToString(hmac)
|
||||
|
||||
if b.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Request JSON: %s\n", PayloadJSON)
|
||||
}
|
||||
|
||||
PayloadBase64 := crypto.Base64Encode(PayloadJSON)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512_384, []byte(PayloadBase64),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["X-BFX-APIKEY"] = b.API.Credentials.Key
|
||||
headers["X-BFX-PAYLOAD"] = PayloadBase64
|
||||
headers["X-BFX-SIGNATURE"] = crypto.HexEncodeToString(hmac)
|
||||
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: ePoint + bitfinexAPIVersion + path,
|
||||
Headers: headers,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: endpoint})
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: fullPath,
|
||||
Headers: headers,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording}, nil
|
||||
})
|
||||
}
|
||||
|
||||
// SendAuthenticatedHTTPRequestV2 sends an autheticated http request and json
|
||||
@@ -1573,44 +1573,46 @@ func (b *Bitfinex) SendAuthenticatedHTTPRequestV2(ep exchange.URL, method, path
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var body io.Reader
|
||||
var payload []byte
|
||||
if len(params) != 0 {
|
||||
payload, err = json.Marshal(params)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
return b.SendPayload(context.Background(), endpoint, func() (*request.Item, error) {
|
||||
var body io.Reader
|
||||
var payload []byte
|
||||
if len(params) != 0 {
|
||||
payload, err = json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
}
|
||||
|
||||
// This is done in a weird way because bitfinex doesn't accept unixnano
|
||||
n := strconv.FormatInt(int64(b.Requester.GetNonce(false))*1e9, 10)
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["Accept"] = "application/json"
|
||||
headers["bfx-apikey"] = b.API.Credentials.Key
|
||||
headers["bfx-nonce"] = n
|
||||
strPath := "/api" + bitfinexAPIVersion2 + path + string(payload)
|
||||
signStr := strPath + n
|
||||
hmac := crypto.GetHMAC(
|
||||
crypto.HashSHA512_384,
|
||||
[]byte(signStr),
|
||||
[]byte(b.API.Credentials.Secret),
|
||||
)
|
||||
headers["bfx-signature"] = crypto.HexEncodeToString(hmac)
|
||||
// This is done in a weird way because bitfinex doesn't accept unixnano
|
||||
n := strconv.FormatInt(int64(b.Requester.GetNonce(false))*1e9, 10)
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["Accept"] = "application/json"
|
||||
headers["bfx-apikey"] = b.API.Credentials.Key
|
||||
headers["bfx-nonce"] = n
|
||||
strPath := "/api" + bitfinexAPIVersion2 + path + string(payload)
|
||||
signStr := strPath + n
|
||||
hmac := crypto.GetHMAC(
|
||||
crypto.HashSHA512_384,
|
||||
[]byte(signStr),
|
||||
[]byte(b.API.Credentials.Secret),
|
||||
)
|
||||
headers["bfx-signature"] = crypto.HexEncodeToString(hmac)
|
||||
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: ePoint + bitfinexAPIVersion2 + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: endpoint,
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: ePoint + bitfinexAPIVersion2 + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -291,13 +291,16 @@ func (b *Bitflyer) SendHTTPRequest(ep exchange.URL, path string, result interfac
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}
|
||||
return b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -463,13 +463,16 @@ func (b *Bithumb) SendHTTPRequest(ep exchange.URL, path string, result interface
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}
|
||||
return b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -486,47 +489,47 @@ func (b *Bithumb) SendAuthenticatedHTTPRequest(ep exchange.URL, path string, par
|
||||
params = url.Values{}
|
||||
}
|
||||
|
||||
// This is time window sensitive
|
||||
tnMS := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
n := strconv.FormatInt(tnMS, 10)
|
||||
|
||||
params.Set("endpoint", path)
|
||||
payload := params.Encode()
|
||||
hmacPayload := path + string('\x00') + payload + string('\x00') + n
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(hmacPayload),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
hmacStr := crypto.HexEncodeToString(hmac)
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Api-Key"] = b.API.Credentials.Key
|
||||
headers["Api-Sign"] = crypto.Base64Encode([]byte(hmacStr))
|
||||
headers["Api-Nonce"] = n
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
var intermediary json.RawMessage
|
||||
err = b.SendPayload(context.Background(), request.Auth, func() (*request.Item, error) {
|
||||
// This is time window sensitive
|
||||
tnMS := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
n := strconv.FormatInt(tnMS, 10)
|
||||
|
||||
params.Set("endpoint", path)
|
||||
|
||||
payload := params.Encode()
|
||||
hmacPayload := path + string('\x00') + payload + string('\x00') + n
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(hmacPayload),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
hmacStr := crypto.HexEncodeToString(hmac)
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Api-Key"] = b.API.Credentials.Key
|
||||
headers["Api-Sign"] = crypto.Base64Encode([]byte(hmacStr))
|
||||
headers["Api-Nonce"] = n
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
return &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBufferString(payload),
|
||||
Result: &intermediary,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errCapture := struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
|
||||
err = b.SendPayload(context.Background(), &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBufferString(payload),
|
||||
Result: &intermediary,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: request.Auth})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(intermediary, &errCapture)
|
||||
if err == nil {
|
||||
if errCapture.Status != "" && errCapture.Status != noError {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package bitmex
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -807,35 +806,24 @@ func (b *Bitmex) SendHTTPRequest(ep exchange.URL, path string, params Parameter,
|
||||
return err
|
||||
}
|
||||
path = endpoint + path
|
||||
if params != nil {
|
||||
var encodedPath string
|
||||
if !params.IsNil() {
|
||||
encodedPath, err = params.ToURLVals(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = b.SendPayload(context.Background(), &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: encodedPath,
|
||||
Result: &respCheck,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.CaptureError(respCheck, result)
|
||||
if params != nil && !params.IsNil() {
|
||||
path, err = params.ToURLVals(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: &respCheck,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}
|
||||
|
||||
err = b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -853,52 +841,52 @@ func (b *Bitmex) SendAuthenticatedHTTPRequest(ep exchange.URL, verb, path string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
expires := time.Now().Add(time.Second * 10)
|
||||
timestamp := expires.UnixNano()
|
||||
timestampStr := strconv.FormatInt(timestamp, 10)
|
||||
timestampNew := timestampStr[:13]
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["api-expires"] = timestampNew
|
||||
headers["api-key"] = b.API.Credentials.Key
|
||||
|
||||
var payload string
|
||||
if params != nil {
|
||||
err = params.VerifyData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var data []byte
|
||||
data, err = json.Marshal(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload = string(data)
|
||||
}
|
||||
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(verb+"/api/v1"+path+timestampNew+payload),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
|
||||
headers["api-signature"] = crypto.HexEncodeToString(hmac)
|
||||
|
||||
var respCheck interface{}
|
||||
newRequest := func() (*request.Item, error) {
|
||||
expires := time.Now().Add(time.Second * 10)
|
||||
timestamp := expires.UnixNano()
|
||||
timestampStr := strconv.FormatInt(timestamp, 10)
|
||||
timestampNew := timestampStr[:13]
|
||||
|
||||
ctx, cancel := context.WithDeadline(context.Background(), expires)
|
||||
defer cancel()
|
||||
err = b.SendPayload(ctx, &request.Item{
|
||||
Method: verb,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer([]byte(payload)),
|
||||
Result: &respCheck,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: request.Auth,
|
||||
})
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["api-expires"] = timestampNew
|
||||
headers["api-key"] = b.API.Credentials.Key
|
||||
|
||||
var payload string
|
||||
if params != nil {
|
||||
err = params.VerifyData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data []byte
|
||||
data, err = json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
payload = string(data)
|
||||
}
|
||||
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(verb+"/api/v1"+path+timestampNew+payload),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
|
||||
headers["api-signature"] = crypto.HexEncodeToString(hmac)
|
||||
|
||||
return &request.Item{
|
||||
Method: verb,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(payload),
|
||||
Result: &respCheck,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
err = b.SendPayload(context.Background(), request.Auth, newRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -387,9 +387,9 @@ func (b *Bitstamp) PlaceOrder(currencyPair string, price, amount float64, buy, m
|
||||
|
||||
var path string
|
||||
if market {
|
||||
path = orderType + "/" + bitstampAPIMarket + strings.ToLower(currencyPair)
|
||||
path = orderType + "/" + bitstampAPIMarket + "/" + strings.ToLower(currencyPair)
|
||||
} else {
|
||||
path = orderType + "/" + orderType + strings.ToLower(currencyPair)
|
||||
path = orderType + "/" + strings.ToLower(currencyPair)
|
||||
}
|
||||
|
||||
return response,
|
||||
@@ -598,13 +598,16 @@ func (b *Bitstamp) SendHTTPRequest(ep exchange.URL, path string, result interfac
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}
|
||||
return b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -617,59 +620,57 @@ func (b *Bitstamp) SendAuthenticatedHTTPRequest(ep exchange.URL, path string, v2
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n := b.Requester.GetNonce(true).String()
|
||||
|
||||
if values == nil {
|
||||
values = url.Values{}
|
||||
}
|
||||
|
||||
values.Set("key", b.API.Credentials.Key)
|
||||
values.Set("nonce", n)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(n+b.API.Credentials.ClientID+b.API.Credentials.Key),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
values.Set("signature", strings.ToUpper(crypto.HexEncodeToString(hmac)))
|
||||
|
||||
if v2 {
|
||||
path = endpoint + "/v" + bitstampAPIVersion + "/" + path + "/"
|
||||
} else {
|
||||
path = endpoint + "/" + path + "/"
|
||||
}
|
||||
|
||||
if b.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Sending POST request to "+path)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
encodedValues := values.Encode()
|
||||
readerValues := bytes.NewBufferString(encodedValues)
|
||||
|
||||
interim := json.RawMessage{}
|
||||
err = b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
n := b.Requester.GetNonce(true).String()
|
||||
|
||||
values.Set("key", b.API.Credentials.Key)
|
||||
values.Set("nonce", n)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(n+b.API.Credentials.ClientID+b.API.Credentials.Key),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
values.Set("signature", strings.ToUpper(crypto.HexEncodeToString(hmac)))
|
||||
|
||||
var fullPath string
|
||||
if v2 {
|
||||
fullPath = endpoint + "/v" + bitstampAPIVersion + "/" + path + "/"
|
||||
} else {
|
||||
fullPath = endpoint + "/" + path + "/"
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
encodedValues := values.Encode()
|
||||
readerValues := bytes.NewBufferString(encodedValues)
|
||||
|
||||
return &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: fullPath,
|
||||
Headers: headers,
|
||||
Body: readerValues,
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errCap := struct {
|
||||
Error string `json:"error"`
|
||||
Status string `json:"status"`
|
||||
Reason interface{} `json:"reason"`
|
||||
}{}
|
||||
|
||||
err = b.SendPayload(context.Background(), &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: readerValues,
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(interim, &errCap); err == nil {
|
||||
if errCap.Error != "" {
|
||||
return errors.New(errCap.Error)
|
||||
|
||||
@@ -347,7 +347,7 @@ func (b *Bittrex) SendHTTPRequest(ep exchange.URL, path string, result interface
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
requestItem := request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
@@ -356,7 +356,7 @@ func (b *Bittrex) SendHTTPRequest(ep exchange.URL, path string, result interface
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
HeaderResponse: resultHeader,
|
||||
}
|
||||
return b.SendPayload(context.Background(), &requestItem)
|
||||
return b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) { return item, nil })
|
||||
}
|
||||
|
||||
// SendAuthHTTPRequest sends an authenticated request
|
||||
@@ -369,46 +369,50 @@ func (b *Bittrex) SendAuthHTTPRequest(ep exchange.URL, method, action string, pa
|
||||
return err
|
||||
}
|
||||
|
||||
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
newRequest := func() (*request.Item, error) {
|
||||
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
path := common.EncodeURLValues(action, params)
|
||||
|
||||
path := common.EncodeURLValues(action, params)
|
||||
|
||||
var body io.Reader
|
||||
var hmac, payload []byte
|
||||
var contentHash string
|
||||
if data == nil {
|
||||
payload = []byte("")
|
||||
} else {
|
||||
var err error
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
var body io.Reader
|
||||
var hmac, payload []byte
|
||||
var contentHash string
|
||||
if data == nil {
|
||||
payload = []byte("")
|
||||
} else {
|
||||
var err error
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
contentHash = crypto.HexEncodeToString(crypto.GetSHA512(payload))
|
||||
sigPayload := ts + endpoint + path + method + contentHash
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA512, []byte(sigPayload), []byte(b.API.Credentials.Secret))
|
||||
body = bytes.NewBuffer(payload)
|
||||
contentHash = crypto.HexEncodeToString(crypto.GetSHA512(payload))
|
||||
sigPayload := ts + endpoint + path + method + contentHash
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA512, []byte(sigPayload), []byte(b.API.Credentials.Secret))
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Api-Key"] = b.API.Credentials.Key
|
||||
headers["Api-Timestamp"] = ts
|
||||
headers["Api-Content-Hash"] = contentHash
|
||||
headers["Api-Signature"] = crypto.HexEncodeToString(hmac)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["Accept"] = "application/json"
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
HeaderResponse: resultHeader,
|
||||
})
|
||||
headers := make(map[string]string)
|
||||
headers["Api-Key"] = b.API.Credentials.Key
|
||||
headers["Api-Timestamp"] = ts
|
||||
headers["Api-Content-Hash"] = contentHash
|
||||
headers["Api-Signature"] = crypto.HexEncodeToString(hmac)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["Accept"] = "application/json"
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
HeaderResponse: resultHeader,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return b.SendPayload(context.Background(), request.Unset, newRequest)
|
||||
}
|
||||
|
||||
// GetFee returns an estimate of fee based on type of transaction
|
||||
|
||||
@@ -132,13 +132,16 @@ func (b *Bittrex) WsSignalRHandshake(result interface{}) error {
|
||||
return err
|
||||
}
|
||||
path := "/negotiate?connectionData=[{name:\"c3\"}]&clientProtocol=1.5"
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}
|
||||
return b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -683,13 +683,16 @@ func (b *BTCMarkets) CancelBatch(ids []string) (BatchCancelResponse, error) {
|
||||
|
||||
// SendHTTPRequest sends an unauthenticated HTTP request
|
||||
func (b *BTCMarkets) SendHTTPRequest(path string, result interface{}) error {
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}
|
||||
return b.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -699,52 +702,51 @@ func (b *BTCMarkets) SendAuthenticatedRequest(method, path string, data, result
|
||||
return fmt.Errorf("%s %w", b.Name, exchange.ErrAuthenticatedRequestWithoutCredentialsSet)
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
strTime := strconv.FormatInt(now.UTC().UnixNano()/1000000, 10)
|
||||
newRequest := func() (*request.Item, error) {
|
||||
now := time.Now()
|
||||
strTime := strconv.FormatInt(now.UTC().UnixNano()/1000000, 10)
|
||||
|
||||
var body io.Reader
|
||||
var payload, hmac []byte
|
||||
switch data.(type) {
|
||||
case map[string]interface{}, []interface{}:
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
var body io.Reader
|
||||
var payload, hmac []byte
|
||||
switch data.(type) {
|
||||
case map[string]interface{}, []interface{}:
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
strMsg := method + btcMarketsAPIVersion + path + strTime + string(payload)
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(strMsg), []byte(b.API.Credentials.Secret))
|
||||
default:
|
||||
strArray := strings.Split(path, "?")
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(method+btcMarketsAPIVersion+strArray[0]+strTime),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
strMsg := method + btcMarketsAPIVersion + path + strTime + string(payload)
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(strMsg), []byte(b.API.Credentials.Secret))
|
||||
default:
|
||||
strArray := strings.Split(path, "?")
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(method+btcMarketsAPIVersion+strArray[0]+strTime),
|
||||
[]byte(b.API.Credentials.Secret))
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Accept"] = "application/json"
|
||||
headers["Accept-Charset"] = "UTF-8"
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["BM-AUTH-APIKEY"] = b.API.Credentials.Key
|
||||
headers["BM-AUTH-TIMESTAMP"] = strTime
|
||||
headers["BM-AUTH-SIGNATURE"] = crypto.Base64Encode(hmac)
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: btcMarketsAPIURL + btcMarketsAPIVersion + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Accept"] = "application/json"
|
||||
headers["Accept-Charset"] = "UTF-8"
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["BM-AUTH-APIKEY"] = b.API.Credentials.Key
|
||||
headers["BM-AUTH-TIMESTAMP"] = strTime
|
||||
headers["BM-AUTH-SIGNATURE"] = crypto.Base64Encode(hmac)
|
||||
|
||||
// The timestamp included with an authenticated request must be within +/- 30 seconds of the server timestamp
|
||||
ctx, cancel := context.WithDeadline(context.Background(), now.Add(30*time.Second))
|
||||
defer cancel()
|
||||
return b.SendPayload(ctx, &request.Item{
|
||||
Method: method,
|
||||
Path: btcMarketsAPIURL + btcMarketsAPIVersion + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: false,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: f,
|
||||
})
|
||||
return b.SendPayload(context.Background(), f, newRequest)
|
||||
}
|
||||
|
||||
// GetFee returns an estimate of fee based on type of transaction
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
// BTSE is the overarching type across this package
|
||||
@@ -438,14 +437,16 @@ func (b *BTSE) SendHTTPRequest(ep exchange.URL, method, endpoint string, result
|
||||
if !spotEndpoint {
|
||||
p = btseFuturesPath + btseFuturesAPIPath
|
||||
}
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: ePoint + p + endpoint,
|
||||
Result: result,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: f,
|
||||
}
|
||||
return b.SendPayload(context.Background(), f, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -460,66 +461,64 @@ func (b *BTSE) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint st
|
||||
return err
|
||||
}
|
||||
|
||||
// The concatenation is done this way because BTSE expect endpoint+nonce or endpoint+nonce+body
|
||||
// when signing the data but the full path of the request is /spot/api/v3.2/<endpoint>
|
||||
// its messy but it works and supports futures as well
|
||||
host := ePoint
|
||||
if isSpot {
|
||||
host += btseSPOTPath + btseSPOTAPIPath + endpoint
|
||||
endpoint = btseSPOTAPIPath + endpoint
|
||||
} else {
|
||||
host += btseFuturesPath + btseFuturesAPIPath
|
||||
endpoint += btseFuturesAPIPath
|
||||
}
|
||||
var hmac []byte
|
||||
var body io.Reader
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
|
||||
headers := map[string]string{
|
||||
"btse-api": b.API.Credentials.Key,
|
||||
"btse-nonce": nonce,
|
||||
}
|
||||
if req != nil {
|
||||
reqPayload, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
newRequest := func() (*request.Item, error) {
|
||||
// The concatenation is done this way because BTSE expect endpoint+nonce or endpoint+nonce+body
|
||||
// when signing the data but the full path of the request is /spot/api/v3.2/<endpoint>
|
||||
// its messy but it works and supports futures as well
|
||||
host := ePoint
|
||||
var expandedEndpoint string
|
||||
if isSpot {
|
||||
host += btseSPOTPath + btseSPOTAPIPath + endpoint
|
||||
expandedEndpoint = btseSPOTAPIPath + endpoint
|
||||
} else {
|
||||
host += btseFuturesPath + btseFuturesAPIPath
|
||||
expandedEndpoint = endpoint + btseFuturesAPIPath
|
||||
}
|
||||
body = bytes.NewBuffer(reqPayload)
|
||||
hmac = crypto.GetHMAC(
|
||||
crypto.HashSHA512_384,
|
||||
[]byte((endpoint + nonce + string(reqPayload))),
|
||||
[]byte(b.API.Credentials.Secret),
|
||||
)
|
||||
headers["Content-Type"] = "application/json"
|
||||
} else {
|
||||
hmac = crypto.GetHMAC(
|
||||
crypto.HashSHA512_384,
|
||||
[]byte((endpoint + nonce)),
|
||||
[]byte(b.API.Credentials.Secret),
|
||||
)
|
||||
if len(values) > 0 {
|
||||
host += "?" + values.Encode()
|
||||
|
||||
var hmac []byte
|
||||
var body io.Reader
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
|
||||
headers := map[string]string{
|
||||
"btse-api": b.API.Credentials.Key,
|
||||
"btse-nonce": nonce,
|
||||
}
|
||||
}
|
||||
headers["btse-sign"] = crypto.HexEncodeToString(hmac)
|
||||
if req != nil {
|
||||
reqPayload, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewBuffer(reqPayload)
|
||||
hmac = crypto.GetHMAC(
|
||||
crypto.HashSHA512_384,
|
||||
[]byte((expandedEndpoint + nonce + string(reqPayload))),
|
||||
[]byte(b.API.Credentials.Secret),
|
||||
)
|
||||
headers["Content-Type"] = "application/json"
|
||||
} else {
|
||||
hmac = crypto.GetHMAC(
|
||||
crypto.HashSHA512_384,
|
||||
[]byte((expandedEndpoint + nonce)),
|
||||
[]byte(b.API.Credentials.Secret),
|
||||
)
|
||||
if len(values) > 0 {
|
||||
host += "?" + values.Encode()
|
||||
}
|
||||
}
|
||||
headers["btse-sign"] = crypto.HexEncodeToString(hmac)
|
||||
|
||||
if b.Verbose {
|
||||
log.Debugf(log.ExchangeSys,
|
||||
"%s Sending %s request to URL %s",
|
||||
b.Name, method, endpoint)
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: host,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return b.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: host,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: b.Verbose,
|
||||
HTTPDebugging: b.HTTPDebugging,
|
||||
HTTPRecording: b.HTTPRecording,
|
||||
Endpoint: f,
|
||||
})
|
||||
return b.SendPayload(context.Background(), f, newRequest)
|
||||
}
|
||||
|
||||
// GetFee returns an estimate of fee based on type of transaction
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -686,13 +685,18 @@ func (c *CoinbasePro) SendHTTPRequest(ep exchange.URL, path string, result inter
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: c.Verbose,
|
||||
HTTPDebugging: c.HTTPDebugging,
|
||||
HTTPRecording: c.HTTPRecording,
|
||||
}
|
||||
|
||||
return c.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -705,44 +709,40 @@ func (c *CoinbasePro) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
payload := []byte("")
|
||||
|
||||
if params != nil {
|
||||
payload, err = json.Marshal(params)
|
||||
if err != nil {
|
||||
return errors.New("sendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
newRequest := func() (*request.Item, error) {
|
||||
payload := []byte("")
|
||||
if params != nil {
|
||||
payload, err = json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Request JSON: %s\n", payload)
|
||||
}
|
||||
now := time.Now()
|
||||
n := strconv.FormatInt(now.Unix(), 10)
|
||||
message := n + method + "/" + path + string(payload)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(message), []byte(c.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["CB-ACCESS-SIGN"] = crypto.Base64Encode(hmac)
|
||||
headers["CB-ACCESS-TIMESTAMP"] = n
|
||||
headers["CB-ACCESS-KEY"] = c.API.Credentials.Key
|
||||
headers["CB-ACCESS-PASSPHRASE"] = c.API.Credentials.ClientID
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(payload),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: c.Verbose,
|
||||
HTTPDebugging: c.HTTPDebugging,
|
||||
HTTPRecording: c.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
n := strconv.FormatInt(now.Unix(), 10)
|
||||
message := n + method + "/" + path + string(payload)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(message), []byte(c.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["CB-ACCESS-SIGN"] = crypto.Base64Encode(hmac)
|
||||
headers["CB-ACCESS-TIMESTAMP"] = n
|
||||
headers["CB-ACCESS-KEY"] = c.API.Credentials.Key
|
||||
headers["CB-ACCESS-PASSPHRASE"] = c.API.Credentials.ClientID
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
// Timestamp must be within 30 seconds of the api service time
|
||||
ctx, cancel := context.WithDeadline(context.Background(), now.Add(30*time.Second))
|
||||
defer cancel()
|
||||
return c.SendPayload(ctx, &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(payload),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: c.Verbose,
|
||||
HTTPDebugging: c.HTTPDebugging,
|
||||
HTTPRecording: c.HTTPRecording,
|
||||
})
|
||||
return c.SendPayload(context.Background(), request.Unset, newRequest)
|
||||
}
|
||||
|
||||
// GetFee returns an estimate of fee based on type of transaction
|
||||
|
||||
@@ -1088,24 +1088,26 @@ func (c *Coinbene) SendHTTPRequest(ep exchange.URL, path string, f request.Endpo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var resp json.RawMessage
|
||||
errCap := struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
|
||||
if err := c.SendPayload(context.Background(), &request.Item{
|
||||
var resp json.RawMessage
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: &resp,
|
||||
Verbose: c.Verbose,
|
||||
HTTPDebugging: c.HTTPDebugging,
|
||||
HTTPRecording: c.HTTPRecording,
|
||||
Endpoint: f,
|
||||
}
|
||||
if err := c.SendPayload(context.Background(), f, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errCap := struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
if err := json.Unmarshal(resp, &errCap); err == nil {
|
||||
if errCap.Code != 200 && errCap.Message != "" {
|
||||
return errors.New(errCap.Message)
|
||||
@@ -1128,76 +1130,77 @@ func (c *Coinbene) SendAuthHTTPRequest(ep exchange.URL, method, path, epPath str
|
||||
if isSwap {
|
||||
authPath = coinbeneSwapAuthPath
|
||||
}
|
||||
now := time.Now()
|
||||
timestamp := now.UTC().Format("2006-01-02T15:04:05.999Z")
|
||||
var finalBody io.Reader
|
||||
var preSign string
|
||||
switch {
|
||||
case params != nil && method == http.MethodGet:
|
||||
p, ok := params.(url.Values)
|
||||
if !ok {
|
||||
return fmt.Errorf("params is not of type url.Values")
|
||||
}
|
||||
preSign = timestamp + method + authPath + epPath + "?" + p.Encode()
|
||||
path = common.EncodeURLValues(path, p)
|
||||
case params != nil:
|
||||
var i interface{}
|
||||
switch p := params.(type) {
|
||||
case url.Values:
|
||||
m := make(map[string]string)
|
||||
for k, v := range p {
|
||||
m[k] = strings.Join(v, "")
|
||||
}
|
||||
i = m
|
||||
default:
|
||||
i = p
|
||||
}
|
||||
tempBody, err := json.Marshal(i)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
finalBody = bytes.NewBufferString(string(tempBody))
|
||||
preSign = timestamp + method + authPath + epPath + string(tempBody)
|
||||
default:
|
||||
preSign = timestamp + method + authPath + epPath
|
||||
}
|
||||
tempSign := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(preSign),
|
||||
[]byte(c.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["ACCESS-KEY"] = c.API.Credentials.Key
|
||||
headers["ACCESS-SIGN"] = crypto.HexEncodeToString(tempSign)
|
||||
headers["ACCESS-TIMESTAMP"] = timestamp
|
||||
|
||||
var resp json.RawMessage
|
||||
newRequest := func() (*request.Item, error) {
|
||||
timestamp := time.Now().UTC().Format("2006-01-02T15:04:05.999Z")
|
||||
var finalBody io.Reader
|
||||
var preSign string
|
||||
var fullPath = path
|
||||
switch {
|
||||
case params != nil && method == http.MethodGet:
|
||||
p, ok := params.(url.Values)
|
||||
if !ok {
|
||||
return nil, errors.New("params is not of type url.Values")
|
||||
}
|
||||
preSign = timestamp + method + authPath + epPath + "?" + p.Encode()
|
||||
fullPath = common.EncodeURLValues(path, p)
|
||||
case params != nil:
|
||||
var i interface{}
|
||||
switch p := params.(type) {
|
||||
case url.Values:
|
||||
m := make(map[string]string)
|
||||
for k, v := range p {
|
||||
m[k] = strings.Join(v, "")
|
||||
}
|
||||
i = m
|
||||
default:
|
||||
i = p
|
||||
}
|
||||
tempBody, err2 := json.Marshal(i)
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
finalBody = bytes.NewBufferString(string(tempBody))
|
||||
preSign = timestamp + method + authPath + epPath + string(tempBody)
|
||||
default:
|
||||
preSign = timestamp + method + authPath + epPath
|
||||
}
|
||||
tempSign := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(preSign),
|
||||
[]byte(c.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["ACCESS-KEY"] = c.API.Credentials.Key
|
||||
headers["ACCESS-SIGN"] = crypto.HexEncodeToString(tempSign)
|
||||
headers["ACCESS-TIMESTAMP"] = timestamp
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + fullPath,
|
||||
Headers: headers,
|
||||
Body: finalBody,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Verbose: c.Verbose,
|
||||
HTTPDebugging: c.HTTPDebugging,
|
||||
HTTPRecording: c.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if err := c.SendPayload(context.Background(), f, newRequest); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errCap := struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
|
||||
// Expiry of timestamp doesn't appear to be documented, so making a reasonable assumption
|
||||
ctx, cancel := context.WithDeadline(context.Background(), now.Add(15*time.Second))
|
||||
defer cancel()
|
||||
if err := c.SendPayload(ctx, &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: finalBody,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Verbose: c.Verbose,
|
||||
HTTPDebugging: c.HTTPDebugging,
|
||||
HTTPRecording: c.HTTPRecording,
|
||||
Endpoint: f,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(resp, &errCap); err == nil {
|
||||
if errCap.Code != 200 && errCap.Message != "" {
|
||||
return errors.New(errCap.Message)
|
||||
}
|
||||
if err := json.Unmarshal(resp, &errCap); err == nil &&
|
||||
errCap.Code != 200 &&
|
||||
errCap.Message != "" {
|
||||
return errors.New(errCap.Message)
|
||||
}
|
||||
return json.Unmarshal(resp, result)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -275,38 +274,37 @@ func (c *COINUT) SendHTTPRequest(ep exchange.URL, apiRequest string, params map[
|
||||
params = map[string]interface{}{}
|
||||
}
|
||||
|
||||
params["nonce"] = getNonce()
|
||||
params["request"] = apiRequest
|
||||
|
||||
payload, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return errors.New("sendHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
|
||||
if c.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Request JSON: %s", payload)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
if authenticated {
|
||||
headers["X-USER"] = c.API.Credentials.ClientID
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, payload, []byte(c.API.Credentials.Key))
|
||||
headers["X-SIGNATURE"] = crypto.HexEncodeToString(hmac)
|
||||
}
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
var rawMsg json.RawMessage
|
||||
err = c.SendPayload(context.Background(), &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: endpoint,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(payload),
|
||||
Result: &rawMsg,
|
||||
AuthRequest: authenticated,
|
||||
NonceEnabled: true,
|
||||
Verbose: c.Verbose,
|
||||
HTTPDebugging: c.HTTPDebugging,
|
||||
HTTPRecording: c.HTTPRecording,
|
||||
err = c.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
params["nonce"] = getNonce()
|
||||
params["request"] = apiRequest
|
||||
|
||||
var payload []byte
|
||||
payload, err = json.Marshal(params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
if authenticated {
|
||||
headers["X-USER"] = c.API.Credentials.ClientID
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, payload, []byte(c.API.Credentials.Key))
|
||||
headers["X-SIGNATURE"] = crypto.HexEncodeToString(hmac)
|
||||
}
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
return &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: endpoint,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(payload),
|
||||
Result: &rawMsg,
|
||||
AuthRequest: authenticated,
|
||||
NonceEnabled: true,
|
||||
Verbose: c.Verbose,
|
||||
HTTPDebugging: c.HTTPDebugging,
|
||||
HTTPRecording: c.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -305,13 +304,17 @@ func (e *EXMO) SendHTTPRequest(endpoint exchange.URL, path string, result interf
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: urlPath + path,
|
||||
Result: result,
|
||||
Verbose: e.Verbose,
|
||||
HTTPDebugging: e.HTTPDebugging,
|
||||
HTTPRecording: e.HTTPRecording,
|
||||
}
|
||||
return e.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -326,39 +329,34 @@ func (e *EXMO) SendAuthenticatedHTTPRequest(epath exchange.URL, method, endpoint
|
||||
return err
|
||||
}
|
||||
|
||||
n := e.Requester.GetNonce(true).String()
|
||||
vals.Set("nonce", n)
|
||||
path := urlPath + fmt.Sprintf("/v%s/%s", exmoAPIVersion, endpoint)
|
||||
|
||||
payload := vals.Encode()
|
||||
hash := crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(payload),
|
||||
[]byte(e.API.Credentials.Secret))
|
||||
return e.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
n := e.Requester.GetNonce(true).String()
|
||||
vals.Set("nonce", n)
|
||||
|
||||
if e.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Sending %s request to %s with params %s\n",
|
||||
method,
|
||||
endpoint,
|
||||
payload)
|
||||
}
|
||||
payload := vals.Encode()
|
||||
hash := crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(payload),
|
||||
[]byte(e.API.Credentials.Secret))
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Key"] = e.API.Credentials.Key
|
||||
headers["Sign"] = crypto.HexEncodeToString(hash)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
headers := make(map[string]string)
|
||||
headers["Key"] = e.API.Credentials.Key
|
||||
headers["Sign"] = crypto.HexEncodeToString(hash)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
path := fmt.Sprintf("/v%s/%s", exmoAPIVersion, endpoint)
|
||||
|
||||
return e.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: urlPath + path,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(payload),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: e.Verbose,
|
||||
HTTPDebugging: e.HTTPDebugging,
|
||||
HTTPRecording: e.HTTPRecording,
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(payload),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: e.Verbose,
|
||||
HTTPDebugging: e.HTTPDebugging,
|
||||
HTTPRecording: e.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -337,13 +337,16 @@ func (f *FTX) SendHTTPRequest(ep exchange.URL, path string, result interface{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: f.Verbose,
|
||||
HTTPDebugging: f.HTTPDebugging,
|
||||
HTTPRecording: f.HTTPRecording,
|
||||
}
|
||||
return f.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1139,40 +1142,45 @@ func (f *FTX) SendAuthHTTPRequest(ep exchange.URL, method, path string, data, re
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
var body io.Reader
|
||||
var hmac, payload []byte
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
newRequest := func() (*request.Item, error) {
|
||||
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
var body io.Reader
|
||||
var hmac, payload []byte
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
sigPayload := ts + method + "/api" + path + string(payload)
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
} else {
|
||||
sigPayload := ts + method + "/api" + path
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
sigPayload := ts + method + "/api" + path + string(payload)
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
} else {
|
||||
sigPayload := ts + method + "/api" + path
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["FTX-KEY"] = f.API.Credentials.Key
|
||||
headers["FTX-SIGN"] = crypto.HexEncodeToString(hmac)
|
||||
headers["FTX-TS"] = ts
|
||||
if f.API.Credentials.Subaccount != "" {
|
||||
headers["FTX-SUBACCOUNT"] = url.QueryEscape(f.API.Credentials.Subaccount)
|
||||
}
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: f.Verbose,
|
||||
HTTPDebugging: f.HTTPDebugging,
|
||||
HTTPRecording: f.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
headers := make(map[string]string)
|
||||
headers["FTX-KEY"] = f.API.Credentials.Key
|
||||
headers["FTX-SIGN"] = crypto.HexEncodeToString(hmac)
|
||||
headers["FTX-TS"] = ts
|
||||
if f.API.Credentials.Subaccount != "" {
|
||||
headers["FTX-SUBACCOUNT"] = url.QueryEscape(f.API.Credentials.Subaccount)
|
||||
}
|
||||
headers["Content-Type"] = "application/json"
|
||||
return f.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: f.Verbose,
|
||||
HTTPDebugging: f.HTTPDebugging,
|
||||
HTTPRecording: f.HTTPRecording,
|
||||
})
|
||||
return f.SendPayload(context.Background(), request.Unset, newRequest)
|
||||
}
|
||||
|
||||
// GetFee returns an estimate of fee based on type of transaction
|
||||
|
||||
@@ -320,13 +320,16 @@ func (g *Gateio) SendHTTPRequest(ep exchange.URL, path string, result interface{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return g.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: g.Verbose,
|
||||
HTTPDebugging: g.HTTPDebugging,
|
||||
HTTPRecording: g.HTTPRecording,
|
||||
}
|
||||
return g.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -421,16 +424,19 @@ func (g *Gateio) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint,
|
||||
urlPath := fmt.Sprintf("%s/%s/%s", ePoint, gateioAPIVersion, endpoint)
|
||||
|
||||
var intermidiary json.RawMessage
|
||||
err = g.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: urlPath,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(param),
|
||||
Result: &intermidiary,
|
||||
AuthRequest: true,
|
||||
Verbose: g.Verbose,
|
||||
HTTPDebugging: g.HTTPDebugging,
|
||||
HTTPRecording: g.HTTPRecording,
|
||||
}
|
||||
err = g.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
item.Body = strings.NewReader(param)
|
||||
return item, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -351,13 +350,18 @@ func (g *Gemini) SendHTTPRequest(ep exchange.URL, path string, result interface{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return g.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: g.Verbose,
|
||||
HTTPDebugging: g.HTTPDebugging,
|
||||
HTTPRecording: g.HTTPRecording,
|
||||
}
|
||||
|
||||
return g.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -373,45 +377,42 @@ func (g *Gemini) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path stri
|
||||
return err
|
||||
}
|
||||
|
||||
req := make(map[string]interface{})
|
||||
req["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
|
||||
req["nonce"] = g.Requester.GetNonce(true).String()
|
||||
return g.SendPayload(context.Background(), request.Auth, func() (*request.Item, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["request"] = fmt.Sprintf("/v%s/%s", geminiAPIVersion, path)
|
||||
req["nonce"] = g.Requester.GetNonce(true).String()
|
||||
|
||||
for key, value := range params {
|
||||
req[key] = value
|
||||
}
|
||||
for key, value := range params {
|
||||
req[key] = value
|
||||
}
|
||||
|
||||
PayloadJSON, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return errors.New("sendAuthenticatedHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
PayloadJSON, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Request JSON: %s", PayloadJSON)
|
||||
}
|
||||
PayloadBase64 := crypto.Base64Encode(PayloadJSON)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512_384, []byte(PayloadBase64), []byte(g.API.Credentials.Secret))
|
||||
|
||||
PayloadBase64 := crypto.Base64Encode(PayloadJSON)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512_384, []byte(PayloadBase64), []byte(g.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Length"] = "0"
|
||||
headers["Content-Type"] = "text/plain"
|
||||
headers["X-GEMINI-APIKEY"] = g.API.Credentials.Key
|
||||
headers["X-GEMINI-PAYLOAD"] = PayloadBase64
|
||||
headers["X-GEMINI-SIGNATURE"] = crypto.HexEncodeToString(hmac)
|
||||
headers["Cache-Control"] = "no-cache"
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Length"] = "0"
|
||||
headers["Content-Type"] = "text/plain"
|
||||
headers["X-GEMINI-APIKEY"] = g.API.Credentials.Key
|
||||
headers["X-GEMINI-PAYLOAD"] = PayloadBase64
|
||||
headers["X-GEMINI-SIGNATURE"] = crypto.HexEncodeToString(hmac)
|
||||
headers["Cache-Control"] = "no-cache"
|
||||
|
||||
return g.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + "/v1/" + path,
|
||||
Headers: headers,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: g.Verbose,
|
||||
HTTPDebugging: g.HTTPDebugging,
|
||||
HTTPRecording: g.HTTPRecording,
|
||||
Endpoint: request.Auth,
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + "/v1/" + path,
|
||||
Headers: headers,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: g.Verbose,
|
||||
HTTPDebugging: g.HTTPDebugging,
|
||||
HTTPRecording: g.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -525,14 +525,18 @@ func (h *HitBTC) SendHTTPRequest(ep exchange.URL, path string, result interface{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: h.Verbose,
|
||||
HTTPDebugging: h.HTTPDebugging,
|
||||
HTTPRecording: h.HTTPRecording,
|
||||
Endpoint: marketRequests,
|
||||
}
|
||||
|
||||
return h.SendPayload(context.Background(), marketRequests, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -550,17 +554,20 @@ func (h *HitBTC) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint
|
||||
|
||||
path := fmt.Sprintf("%s/%s", ePoint, endpoint)
|
||||
|
||||
return h.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBufferString(values.Encode()),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: h.Verbose,
|
||||
HTTPDebugging: h.HTTPDebugging,
|
||||
HTTPRecording: h.HTTPRecording,
|
||||
Endpoint: f,
|
||||
}
|
||||
|
||||
return h.SendPayload(context.Background(), f, func() (*request.Item, error) {
|
||||
item.Body = bytes.NewBufferString(values.Encode())
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -795,18 +795,24 @@ func (h *HUOBI) SendHTTPRequest(ep exchange.URL, path string, result interface{}
|
||||
return err
|
||||
}
|
||||
var tempResp json.RawMessage
|
||||
var errCap errorCapture
|
||||
err = h.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: &tempResp,
|
||||
Verbose: h.Verbose,
|
||||
HTTPDebugging: h.HTTPDebugging,
|
||||
HTTPRecording: h.HTTPRecording,
|
||||
}
|
||||
|
||||
err = h.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errCap errorCapture
|
||||
if err := json.Unmarshal(tempResp, &errCap); err == nil {
|
||||
if errCap.Code != 200 && errCap.ErrMsg != "" {
|
||||
return errors.New(errCap.ErrMsg)
|
||||
@@ -828,56 +834,57 @@ func (h *HUOBI) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoint s
|
||||
values = url.Values{}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
values.Set("AccessKeyId", h.API.Credentials.Key)
|
||||
values.Set("SignatureMethod", "HmacSHA256")
|
||||
values.Set("SignatureVersion", "2")
|
||||
values.Set("Timestamp", now.UTC().Format("2006-01-02T15:04:05"))
|
||||
|
||||
if isVersion2API {
|
||||
endpoint = "/v" + huobiAPIVersion2 + "/" + endpoint
|
||||
} else {
|
||||
endpoint = "/v" + huobiAPIVersion + "/" + endpoint
|
||||
}
|
||||
|
||||
payload := fmt.Sprintf("%s\napi.huobi.pro\n%s\n%s",
|
||||
method, endpoint, values.Encode())
|
||||
|
||||
headers := make(map[string]string)
|
||||
|
||||
if method == http.MethodGet {
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
} else {
|
||||
headers["Content-Type"] = "application/json"
|
||||
}
|
||||
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(payload), []byte(h.API.Credentials.Secret))
|
||||
values.Set("Signature", crypto.Base64Encode(hmac))
|
||||
urlPath := ePoint + common.EncodeURLValues(endpoint, values)
|
||||
|
||||
var body []byte
|
||||
if data != nil {
|
||||
body, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Time difference between your timestamp and standard should be less than 1 minute.
|
||||
ctx, cancel := context.WithDeadline(context.Background(), now.Add(time.Minute))
|
||||
defer cancel()
|
||||
interim := json.RawMessage{}
|
||||
err = h.SendPayload(ctx, &request.Item{
|
||||
Method: method,
|
||||
Path: urlPath,
|
||||
Headers: headers,
|
||||
Body: bytes.NewReader(body),
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
Verbose: h.Verbose,
|
||||
HTTPDebugging: h.HTTPDebugging,
|
||||
HTTPRecording: h.HTTPRecording,
|
||||
})
|
||||
newRequest := func() (*request.Item, error) {
|
||||
now := time.Now()
|
||||
values.Set("AccessKeyId", h.API.Credentials.Key)
|
||||
values.Set("SignatureMethod", "HmacSHA256")
|
||||
values.Set("SignatureVersion", "2")
|
||||
values.Set("Timestamp", now.UTC().Format("2006-01-02T15:04:05"))
|
||||
|
||||
if isVersion2API {
|
||||
endpoint = "/v" + huobiAPIVersion2 + "/" + endpoint
|
||||
} else {
|
||||
endpoint = "/v" + huobiAPIVersion + "/" + endpoint
|
||||
}
|
||||
|
||||
payload := fmt.Sprintf("%s\napi.huobi.pro\n%s\n%s",
|
||||
method, endpoint, values.Encode())
|
||||
|
||||
headers := make(map[string]string)
|
||||
|
||||
if method == http.MethodGet {
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
} else {
|
||||
headers["Content-Type"] = "application/json"
|
||||
}
|
||||
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(payload), []byte(h.API.Credentials.Secret))
|
||||
values.Set("Signature", crypto.Base64Encode(hmac))
|
||||
urlPath := ePoint + common.EncodeURLValues(endpoint, values)
|
||||
|
||||
var body []byte
|
||||
if data != nil {
|
||||
body, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: urlPath,
|
||||
Headers: headers,
|
||||
Body: bytes.NewReader(body),
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
Verbose: h.Verbose,
|
||||
HTTPDebugging: h.HTTPDebugging,
|
||||
HTTPRecording: h.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = h.SendPayload(context.Background(), request.Unset, newRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1118,50 +1118,56 @@ func (h *HUOBI) FuturesAuthenticatedHTTPRequest(ep exchange.URL, method, endpoin
|
||||
if values == nil {
|
||||
values = url.Values{}
|
||||
}
|
||||
now := time.Now()
|
||||
values.Set("AccessKeyId", h.API.Credentials.Key)
|
||||
values.Set("SignatureMethod", "HmacSHA256")
|
||||
values.Set("SignatureVersion", "2")
|
||||
values.Set("Timestamp", now.UTC().Format("2006-01-02T15:04:05"))
|
||||
sigPath := fmt.Sprintf("%s\napi.hbdm.com\n/%s\n%s",
|
||||
method, endpoint, values.Encode())
|
||||
headers := make(map[string]string)
|
||||
if method == http.MethodGet {
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
} else {
|
||||
headers["Content-Type"] = "application/json"
|
||||
}
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(sigPath), []byte(h.API.Credentials.Secret))
|
||||
sigValues := url.Values{}
|
||||
sigValues.Add("Signature", crypto.Base64Encode(hmac))
|
||||
urlPath :=
|
||||
common.EncodeURLValues(ePoint+endpoint, values) + "&" + sigValues.Encode()
|
||||
var body io.Reader
|
||||
var payload []byte
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
}
|
||||
|
||||
var tempResp json.RawMessage
|
||||
var errCap errorCapture
|
||||
ctx, cancel := context.WithDeadline(context.Background(), now.Add(15*time.Second))
|
||||
defer cancel()
|
||||
if err := h.SendPayload(ctx, &request.Item{
|
||||
Method: method,
|
||||
Path: urlPath,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: &tempResp,
|
||||
AuthRequest: true,
|
||||
Verbose: h.Verbose,
|
||||
HTTPDebugging: h.HTTPDebugging,
|
||||
HTTPRecording: h.HTTPRecording,
|
||||
}); err != nil {
|
||||
newRequest := func() (*request.Item, error) {
|
||||
now := time.Now()
|
||||
values.Set("AccessKeyId", h.API.Credentials.Key)
|
||||
values.Set("SignatureMethod", "HmacSHA256")
|
||||
values.Set("SignatureVersion", "2")
|
||||
values.Set("Timestamp", now.UTC().Format("2006-01-02T15:04:05"))
|
||||
sigPath := fmt.Sprintf("%s\napi.hbdm.com\n/%s\n%s",
|
||||
method, endpoint, values.Encode())
|
||||
headers := make(map[string]string)
|
||||
if method == http.MethodGet {
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
} else {
|
||||
headers["Content-Type"] = "application/json"
|
||||
}
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(sigPath), []byte(h.API.Credentials.Secret))
|
||||
sigValues := url.Values{}
|
||||
sigValues.Add("Signature", crypto.Base64Encode(hmac))
|
||||
urlPath :=
|
||||
common.EncodeURLValues(ePoint+endpoint, values) + "&" + sigValues.Encode()
|
||||
var body io.Reader
|
||||
var payload []byte
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
}
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: urlPath,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: &tempResp,
|
||||
AuthRequest: true,
|
||||
Verbose: h.Verbose,
|
||||
HTTPDebugging: h.HTTPDebugging,
|
||||
HTTPRecording: h.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = h.SendPayload(context.Background(), request.Unset, newRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errCap errorCapture
|
||||
if err := json.Unmarshal(tempResp, &errCap); err == nil {
|
||||
if errCap.Code != 200 && errCap.ErrMsg != "" {
|
||||
return errors.New(errCap.ErrMsg)
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -283,13 +282,18 @@ func (i *ItBit) SendHTTPRequest(ep exchange.URL, path string, result interface{}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return i.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: i.Verbose,
|
||||
HTTPDebugging: i.HTTPDebugging,
|
||||
HTTPRecording: i.HTTPRecording,
|
||||
}
|
||||
|
||||
return i.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -310,59 +314,56 @@ func (i *ItBit) SendAuthenticatedHTTPRequest(ep exchange.URL, method, path strin
|
||||
}
|
||||
|
||||
PayloadJSON := []byte("")
|
||||
|
||||
if params != nil {
|
||||
PayloadJSON, err = json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if i.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Request JSON: %s\n", PayloadJSON)
|
||||
}
|
||||
}
|
||||
|
||||
n := i.Requester.GetNonce(true).String()
|
||||
timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
message, err := json.Marshal([]string{method, urlPath, string(PayloadJSON), n, timestamp})
|
||||
var intermediary json.RawMessage
|
||||
err = i.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
n := i.Requester.GetNonce(true).String()
|
||||
timestamp := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
|
||||
var message []byte
|
||||
message, err = json.Marshal([]string{method, urlPath, string(PayloadJSON), n, timestamp})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash := crypto.GetSHA256([]byte(n + string(message)))
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512, []byte(urlPath+string(hash)), []byte(i.API.Credentials.Secret))
|
||||
signature := crypto.Base64Encode(hmac)
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Authorization"] = i.API.Credentials.ClientID + ":" + signature
|
||||
headers["X-Auth-Timestamp"] = timestamp
|
||||
headers["X-Auth-Nonce"] = n
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: urlPath,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(PayloadJSON),
|
||||
Result: &intermediary,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: i.Verbose,
|
||||
HTTPDebugging: i.HTTPDebugging,
|
||||
HTTPRecording: i.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hash := crypto.GetSHA256([]byte(n + string(message)))
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512, []byte(urlPath+string(hash)), []byte(i.API.Credentials.Secret))
|
||||
signature := crypto.Base64Encode(hmac)
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Authorization"] = i.API.Credentials.ClientID + ":" + signature
|
||||
headers["X-Auth-Timestamp"] = timestamp
|
||||
headers["X-Auth-Nonce"] = n
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
var intermediary json.RawMessage
|
||||
|
||||
errCheck := struct {
|
||||
Code int `json:"code"`
|
||||
Description string `json:"description"`
|
||||
RequestID string `json:"requestId"`
|
||||
}{}
|
||||
|
||||
err = i.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: urlPath,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(PayloadJSON),
|
||||
Result: &intermediary,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: i.Verbose,
|
||||
HTTPDebugging: i.HTTPDebugging,
|
||||
HTTPRecording: i.HTTPRecording,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(intermediary, &errCheck)
|
||||
if err == nil {
|
||||
if errCheck.Code != 0 || errCheck.Description != "" {
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
@@ -958,13 +957,18 @@ func (k *Kraken) SendHTTPRequest(ep exchange.URL, path string, result interface{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return k.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: k.Verbose,
|
||||
HTTPDebugging: k.HTTPDebugging,
|
||||
HTTPRecording: k.HTTPRecording,
|
||||
}
|
||||
|
||||
return k.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -979,43 +983,38 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(ep exchange.URL, method string, pa
|
||||
}
|
||||
path := fmt.Sprintf("/%s/private/%s", krakenAPIVersion, method)
|
||||
|
||||
params.Set("nonce", k.Requester.GetNonce(true).String())
|
||||
encoded := params.Encode()
|
||||
shasum := crypto.GetSHA256([]byte(params.Get("nonce") + encoded))
|
||||
signature := crypto.Base64Encode(crypto.GetHMAC(crypto.HashSHA512,
|
||||
append([]byte(path), shasum...), []byte(k.API.Credentials.Secret)))
|
||||
|
||||
if k.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Sending POST request to %s, path: %s, params: %s",
|
||||
endpoint,
|
||||
path,
|
||||
encoded)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["API-Key"] = k.API.Credentials.Key
|
||||
headers["API-Sign"] = signature
|
||||
|
||||
interim := json.RawMessage{}
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
|
||||
defer cancel()
|
||||
err = k.SendPayload(ctx, &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(encoded),
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: k.Verbose,
|
||||
HTTPDebugging: k.HTTPDebugging,
|
||||
HTTPRecording: k.HTTPRecording,
|
||||
err = k.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
nonce := k.Requester.GetNonce(true).String()
|
||||
params.Set("nonce", nonce)
|
||||
encoded := params.Encode()
|
||||
shasum := crypto.GetSHA256([]byte(nonce + encoded))
|
||||
signature := crypto.Base64Encode(crypto.GetHMAC(crypto.HashSHA512,
|
||||
append([]byte(path), shasum...),
|
||||
[]byte(k.API.Credentials.Secret)))
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["API-Key"] = k.API.Credentials.Key
|
||||
headers["API-Sign"] = signature
|
||||
|
||||
return &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(encoded),
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: k.Verbose,
|
||||
HTTPDebugging: k.HTTPDebugging,
|
||||
HTTPRecording: k.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var errCap SpotAuthError
|
||||
if err := json.Unmarshal(interim, &errCap); err == nil {
|
||||
if err = json.Unmarshal(interim, &errCap); err == nil {
|
||||
if len(errCap.Error) != 0 {
|
||||
return errors.New(errCap.Error[0])
|
||||
}
|
||||
|
||||
@@ -264,40 +264,45 @@ func (k *Kraken) SendFuturesAuthRequest(method, path string, postData url.Values
|
||||
if postData == nil {
|
||||
postData = url.Values{}
|
||||
}
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
reqData := ""
|
||||
if len(data) > 0 {
|
||||
temp, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
postData.Add("json", string(temp))
|
||||
reqData = "json=" + string(temp)
|
||||
}
|
||||
sig := k.signFuturesRequest(path, nonce, reqData)
|
||||
headers := map[string]string{
|
||||
"APIKey": k.API.Credentials.Key,
|
||||
"Authent": sig,
|
||||
"Nonce": nonce,
|
||||
}
|
||||
|
||||
interim := json.RawMessage{}
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute))
|
||||
defer cancel()
|
||||
err := k.SendPayload(ctx, &request.Item{
|
||||
Method: method,
|
||||
Path: futuresURL + common.EncodeURLValues(path, postData),
|
||||
Headers: headers,
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
Verbose: k.Verbose,
|
||||
HTTPDebugging: k.HTTPDebugging,
|
||||
HTTPRecording: k.HTTPRecording,
|
||||
})
|
||||
newRequest := func() (*request.Item, error) {
|
||||
nonce := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
reqData := ""
|
||||
if len(data) > 0 {
|
||||
temp, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
postData.Set("json", string(temp))
|
||||
reqData = "json=" + string(temp)
|
||||
}
|
||||
sig := k.signFuturesRequest(path, nonce, reqData)
|
||||
headers := map[string]string{
|
||||
"APIKey": k.API.Credentials.Key,
|
||||
"Authent": sig,
|
||||
"Nonce": nonce,
|
||||
}
|
||||
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: futuresURL + common.EncodeURLValues(path, postData),
|
||||
Headers: headers,
|
||||
Result: &interim,
|
||||
AuthRequest: true,
|
||||
Verbose: k.Verbose,
|
||||
HTTPDebugging: k.HTTPDebugging,
|
||||
HTTPRecording: k.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
err := k.SendPayload(context.Background(), request.Unset, newRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var errCap AuthErrorData
|
||||
if err := json.Unmarshal(interim, &errCap); err == nil {
|
||||
if err = json.Unmarshal(interim, &errCap); err == nil {
|
||||
if errCap.Result != "success" && errCap.Error != "" {
|
||||
return errors.New(errCap.Error)
|
||||
}
|
||||
|
||||
@@ -504,13 +504,18 @@ func (l *Lbank) SendHTTPRequest(ep exchange.URL, path string, result interface{}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: l.Verbose,
|
||||
HTTPDebugging: l.HTTPDebugging,
|
||||
HTTPRecording: l.HTTPRecording,
|
||||
}
|
||||
|
||||
return l.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -574,15 +579,19 @@ func (l *Lbank) SendAuthHTTPRequest(method, endpoint string, vals url.Values, re
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
return l.SendPayload(context.Background(), &request.Item{
|
||||
item := &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBufferString(payload),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: l.Verbose,
|
||||
HTTPDebugging: l.HTTPDebugging,
|
||||
HTTPRecording: l.HTTPRecording,
|
||||
}
|
||||
|
||||
return l.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
item.Body = bytes.NewBufferString(payload)
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -730,14 +729,18 @@ func (l *LocalBitcoins) SendHTTPRequest(endpoint exchange.URL, path string, resu
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return l.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: ePoint + path,
|
||||
Result: result,
|
||||
Verbose: l.Verbose,
|
||||
HTTPDebugging: l.HTTPDebugging,
|
||||
HTTPRecording: l.HTTPRecording,
|
||||
Endpoint: ep,
|
||||
}
|
||||
|
||||
return l.SendPayload(context.Background(), ep, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -751,43 +754,36 @@ func (l *LocalBitcoins) SendAuthenticatedHTTPRequest(ep exchange.URL, method, pa
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n := l.Requester.GetNonce(true).String()
|
||||
|
||||
path = "/api/" + path
|
||||
encoded := params.Encode()
|
||||
message := n + l.API.Credentials.Key + path + encoded
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(message), []byte(l.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["Apiauth-Key"] = l.API.Credentials.Key
|
||||
headers["Apiauth-Nonce"] = n
|
||||
headers["Apiauth-Signature"] = strings.ToUpper(crypto.HexEncodeToString(hmac))
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
return l.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
n := l.Requester.GetNonce(true).String()
|
||||
|
||||
if l.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "%s Sending `%s` request to `%s`, path: `%s`, params: `%s`.",
|
||||
l.Name,
|
||||
method,
|
||||
endpoint,
|
||||
path,
|
||||
encoded,
|
||||
)
|
||||
}
|
||||
fullPath := "/api/" + path
|
||||
encoded := params.Encode()
|
||||
message := n + l.API.Credentials.Key + fullPath + encoded
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256, []byte(message), []byte(l.API.Credentials.Secret))
|
||||
headers := make(map[string]string)
|
||||
headers["Apiauth-Key"] = l.API.Credentials.Key
|
||||
headers["Apiauth-Nonce"] = n
|
||||
headers["Apiauth-Signature"] = strings.ToUpper(crypto.HexEncodeToString(hmac))
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
if method == http.MethodGet && len(encoded) > 0 {
|
||||
path += "?" + encoded
|
||||
}
|
||||
if method == http.MethodGet && len(encoded) > 0 {
|
||||
fullPath += "?" + encoded
|
||||
}
|
||||
|
||||
return l.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBufferString(encoded),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: l.Verbose,
|
||||
HTTPDebugging: l.HTTPDebugging,
|
||||
HTTPRecording: l.HTTPRecording,
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: endpoint + fullPath,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBufferString(encoded),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: l.Verbose,
|
||||
HTTPDebugging: l.HTTPDebugging,
|
||||
HTTPRecording: l.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -11,13 +11,6 @@ type Nonce struct {
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
// Inc increments the nonce value
|
||||
func (n *Nonce) Inc() {
|
||||
n.m.Lock()
|
||||
n.n++
|
||||
n.m.Unlock()
|
||||
}
|
||||
|
||||
// Get retrives the nonce value
|
||||
func (n *Nonce) Get() Value {
|
||||
n.m.Lock()
|
||||
@@ -27,8 +20,10 @@ func (n *Nonce) Get() Value {
|
||||
|
||||
// GetInc increments and returns the value of the nonce
|
||||
func (n *Nonce) GetInc() Value {
|
||||
n.Inc()
|
||||
return n.Get()
|
||||
n.m.Lock()
|
||||
defer n.m.Unlock()
|
||||
n.n++
|
||||
return Value(n.n)
|
||||
}
|
||||
|
||||
// Set sets the nonce value
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
package nonce
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestInc(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(1)
|
||||
nonce.Inc()
|
||||
expected := Value(2)
|
||||
result := nonce.Get()
|
||||
if result != expected {
|
||||
t.Errorf("Expected %d got %d", expected, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(112321313)
|
||||
@@ -65,12 +54,13 @@ func TestNonceConcurrency(t *testing.T) {
|
||||
var nonce Nonce
|
||||
nonce.Set(12312)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1000)
|
||||
for i := 0; i < 1000; i++ {
|
||||
go nonce.Inc()
|
||||
go func() { nonce.GetInc(); wg.Done() }()
|
||||
}
|
||||
|
||||
// Allow sufficient time for all routines to finish
|
||||
time.Sleep(time.Second)
|
||||
wg.Wait()
|
||||
|
||||
result := nonce.Get()
|
||||
expected := Value(12312 + 1000)
|
||||
|
||||
@@ -576,66 +576,59 @@ func (o *OKGroup) SendHTTPRequest(ep exchange.URL, httpMethod, requestType, requ
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now()
|
||||
utcTime := now.UTC().Format(time.RFC3339)
|
||||
payload := []byte("")
|
||||
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return errors.New("sendHTTPRequest: Unable to JSON request")
|
||||
}
|
||||
|
||||
if o.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Request JSON: %s\n", payload)
|
||||
}
|
||||
}
|
||||
|
||||
path := endpoint + requestType + o.APIVersion + requestPath
|
||||
if o.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Sending %v request to %s \n", requestType, path)
|
||||
}
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
if authenticated {
|
||||
signPath := fmt.Sprintf("/%v%v%v%v", OKGroupAPIPath,
|
||||
requestType, o.APIVersion, requestPath)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(utcTime+httpMethod+signPath+string(payload)),
|
||||
[]byte(o.API.Credentials.Secret))
|
||||
headers["OK-ACCESS-KEY"] = o.API.Credentials.Key
|
||||
headers["OK-ACCESS-SIGN"] = crypto.Base64Encode(hmac)
|
||||
headers["OK-ACCESS-TIMESTAMP"] = utcTime
|
||||
headers["OK-ACCESS-PASSPHRASE"] = o.API.Credentials.ClientID
|
||||
}
|
||||
|
||||
// Requests that have a 30+ second difference between the timestamp and the API service time will be considered expired or rejected
|
||||
ctx, cancel := context.WithDeadline(context.Background(), now.Add(30*time.Second))
|
||||
defer cancel()
|
||||
var intermediary json.RawMessage
|
||||
newRequest := func() (*request.Item, error) {
|
||||
now := time.Now()
|
||||
utcTime := now.UTC().Format(time.RFC3339)
|
||||
payload := []byte("")
|
||||
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
path := endpoint + requestType + o.APIVersion + requestPath
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/json"
|
||||
if authenticated {
|
||||
signPath := fmt.Sprintf("/%v%v%v%v", OKGroupAPIPath,
|
||||
requestType, o.APIVersion, requestPath)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA256,
|
||||
[]byte(utcTime+httpMethod+signPath+string(payload)),
|
||||
[]byte(o.API.Credentials.Secret))
|
||||
headers["OK-ACCESS-KEY"] = o.API.Credentials.Key
|
||||
headers["OK-ACCESS-SIGN"] = crypto.Base64Encode(hmac)
|
||||
headers["OK-ACCESS-TIMESTAMP"] = utcTime
|
||||
headers["OK-ACCESS-PASSPHRASE"] = o.API.Credentials.ClientID
|
||||
}
|
||||
|
||||
return &request.Item{
|
||||
Method: strings.ToUpper(httpMethod),
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(payload),
|
||||
Result: &intermediary,
|
||||
AuthRequest: authenticated,
|
||||
Verbose: o.Verbose,
|
||||
HTTPDebugging: o.HTTPDebugging,
|
||||
HTTPRecording: o.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = o.SendPayload(context.Background(), request.Unset, newRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type errCapFormat struct {
|
||||
Error int64 `json:"error_code,omitempty"`
|
||||
ErrorMessage string `json:"error_message,omitempty"`
|
||||
Result bool `json:"result,string,omitempty"`
|
||||
}
|
||||
|
||||
errCap := errCapFormat{}
|
||||
errCap.Result = true
|
||||
err = o.SendPayload(ctx, &request.Item{
|
||||
Method: strings.ToUpper(httpMethod),
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBuffer(payload),
|
||||
Result: &intermediary,
|
||||
AuthRequest: authenticated,
|
||||
Verbose: o.Verbose,
|
||||
HTTPDebugging: o.HTTPDebugging,
|
||||
HTTPRecording: o.HTTPRecording,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errCap := errCapFormat{Result: true}
|
||||
|
||||
err = json.Unmarshal(intermediary, &errCap)
|
||||
if err == nil {
|
||||
|
||||
@@ -834,13 +834,18 @@ func (p *Poloniex) SendHTTPRequest(ep exchange.URL, path string, result interfac
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: p.Verbose,
|
||||
HTTPDebugging: p.HTTPDebugging,
|
||||
HTTPRecording: p.HTTPRecording,
|
||||
}
|
||||
|
||||
return p.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -853,30 +858,31 @@ func (p *Poloniex) SendAuthenticatedHTTPRequest(ep exchange.URL, method, endpoin
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
headers["Key"] = p.API.Credentials.Key
|
||||
values.Set("nonce", strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
values.Set("command", endpoint)
|
||||
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(values.Encode()),
|
||||
[]byte(p.API.Credentials.Secret))
|
||||
return p.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
headers := make(map[string]string)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
headers["Key"] = p.API.Credentials.Key
|
||||
values.Set("nonce", strconv.FormatInt(time.Now().UnixNano(), 10))
|
||||
values.Set("command", endpoint)
|
||||
|
||||
headers["Sign"] = crypto.HexEncodeToString(hmac)
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512,
|
||||
[]byte(values.Encode()),
|
||||
[]byte(p.API.Credentials.Secret))
|
||||
headers["Sign"] = crypto.HexEncodeToString(hmac)
|
||||
|
||||
path := fmt.Sprintf("%s/%s", ePoint, poloniexAPITradingEndpoint)
|
||||
|
||||
return p.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBufferString(values.Encode()),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: p.Verbose,
|
||||
HTTPDebugging: p.HTTPDebugging,
|
||||
HTTPRecording: p.HTTPRecording,
|
||||
path := fmt.Sprintf("%s/%s", ePoint, poloniexAPITradingEndpoint)
|
||||
return &request.Item{
|
||||
Method: method,
|
||||
Path: path,
|
||||
Headers: headers,
|
||||
Body: bytes.NewBufferString(values.Encode()),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: p.Verbose,
|
||||
HTTPDebugging: p.HTTPDebugging,
|
||||
HTTPRecording: p.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
@@ -20,6 +19,17 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
var (
|
||||
errRequestSystemIsNil = errors.New("request system is nil")
|
||||
errMaxRequestJobs = errors.New("max request jobs reached")
|
||||
errRequestFunctionIsNil = errors.New("request function is nil")
|
||||
errServiceNameUnset = errors.New("service name unset")
|
||||
errRequestItemNil = errors.New("request item is nil")
|
||||
errInvalidPath = errors.New("invalid path")
|
||||
errHeaderResponseMapIsNil = errors.New("header response map is nil")
|
||||
errFailedToRetryRequest = errors.New("failed to retry request")
|
||||
)
|
||||
|
||||
// New returns a new Requester
|
||||
func New(name string, httpRequester *http.Client, opts ...RequesterOption) *Requester {
|
||||
r := &Requester{
|
||||
@@ -39,15 +49,47 @@ func New(name string, httpRequester *http.Client, opts ...RequesterOption) *Requ
|
||||
}
|
||||
|
||||
// SendPayload handles sending HTTP/HTTPS requests
|
||||
func (r *Requester) SendPayload(ctx context.Context, i *Item) error {
|
||||
func (r *Requester) SendPayload(ctx context.Context, ep EndpointLimit, newRequest Generate) error {
|
||||
if r == nil {
|
||||
return errRequestSystemIsNil
|
||||
}
|
||||
|
||||
defer r.timedLock.UnlockIfLocked()
|
||||
|
||||
if newRequest == nil {
|
||||
return errRequestFunctionIsNil
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&r.jobs) >= MaxRequestJobs {
|
||||
return errMaxRequestJobs
|
||||
}
|
||||
|
||||
atomic.AddInt32(&r.jobs, 1)
|
||||
err := r.doRequest(ctx, ep, newRequest)
|
||||
atomic.AddInt32(&r.jobs, -1)
|
||||
return err
|
||||
}
|
||||
|
||||
// validateRequest validates the requester item fields
|
||||
func (i *Item) validateRequest(ctx context.Context, r *Requester) (*http.Request, error) {
|
||||
if i == nil {
|
||||
return nil, errRequestItemNil
|
||||
}
|
||||
|
||||
if i.Path == "" {
|
||||
return nil, errInvalidPath
|
||||
}
|
||||
|
||||
if i.HeaderResponse != nil && *i.HeaderResponse == nil {
|
||||
return nil, errHeaderResponseMapIsNil
|
||||
}
|
||||
|
||||
if !i.NonceEnabled {
|
||||
r.timedLock.LockForDuration()
|
||||
}
|
||||
|
||||
req, err := i.validateRequest(ctx, r)
|
||||
req, err := http.NewRequestWithContext(ctx, i.Method, i.Path, i.Body)
|
||||
if err != nil {
|
||||
r.timedLock.UnlockIfLocked()
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if i.HTTPDebugging {
|
||||
@@ -56,43 +98,6 @@ func (r *Requester) SendPayload(ctx context.Context, i *Item) error {
|
||||
log.Debugf(log.RequestSys, "DumpRequest:\n%s", dump)
|
||||
}
|
||||
|
||||
if atomic.LoadInt32(&r.jobs) >= MaxRequestJobs {
|
||||
r.timedLock.UnlockIfLocked()
|
||||
return errors.New("max request jobs reached")
|
||||
}
|
||||
|
||||
atomic.AddInt32(&r.jobs, 1)
|
||||
err = r.doRequest(req, i)
|
||||
atomic.AddInt32(&r.jobs, -1)
|
||||
r.timedLock.UnlockIfLocked()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// validateRequest validates the requester item fields
|
||||
func (i *Item) validateRequest(ctx context.Context, r *Requester) (*http.Request, error) {
|
||||
if r == nil || r.Name == "" {
|
||||
return nil, errors.New("not initialised, SetDefaults() called before making request?")
|
||||
}
|
||||
|
||||
if i == nil {
|
||||
return nil, errors.New("request item cannot be nil")
|
||||
}
|
||||
|
||||
if i.Path == "" {
|
||||
return nil, errors.New("invalid path")
|
||||
}
|
||||
|
||||
if i.HeaderResponse != nil {
|
||||
if *i.HeaderResponse == nil {
|
||||
return nil, errors.New("header response is nil")
|
||||
}
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, i.Method, i.Path, i.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range i.Headers {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
@@ -105,43 +110,35 @@ func (i *Item) validateRequest(ctx context.Context, r *Requester) (*http.Request
|
||||
}
|
||||
|
||||
// DoRequest performs a HTTP/HTTPS request with the supplied params
|
||||
func (r *Requester) doRequest(req *http.Request, p *Item) error {
|
||||
if p == nil {
|
||||
return errors.New("request item cannot be nil")
|
||||
}
|
||||
if p.Verbose {
|
||||
log.Debugf(log.RequestSys,
|
||||
"%s request path: %s",
|
||||
r.Name,
|
||||
p.Path)
|
||||
|
||||
for k, d := range req.Header {
|
||||
log.Debugf(log.RequestSys,
|
||||
"%s request header [%s]: %s",
|
||||
r.Name,
|
||||
k,
|
||||
d)
|
||||
}
|
||||
log.Debugf(log.RequestSys,
|
||||
"%s request type: %s",
|
||||
r.Name,
|
||||
req.Method)
|
||||
|
||||
if p.Body != nil {
|
||||
log.Debugf(log.RequestSys,
|
||||
"%s request body: %v",
|
||||
r.Name,
|
||||
p.Body)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Requester) doRequest(ctx context.Context, endpoint EndpointLimit, newRequest Generate) error {
|
||||
for attempt := 1; ; attempt++ {
|
||||
// Initiate a rate limit reservation and sleep on requested endpoint
|
||||
err := r.InitiateRateLimit(p.Endpoint)
|
||||
err := r.InitiateRateLimit(endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := newRequest()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := p.validateRequest(ctx, r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if p.Verbose {
|
||||
log.Debugf(log.RequestSys, "%s attempt %d request path: %s", r.Name, attempt, p.Path)
|
||||
for k, d := range req.Header {
|
||||
log.Debugf(log.RequestSys, "%s request header [%s]: %s", r.Name, k, d)
|
||||
}
|
||||
log.Debugf(log.RequestSys, "%s request type: %s", r.Name, p.Method)
|
||||
if p.Body != nil {
|
||||
log.Debugf(log.RequestSys, "%s request body: %v", r.Name, p.Body)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := r.HTTPClient.Do(req)
|
||||
if retry, checkErr := r.retryPolicy(resp, err); checkErr != nil {
|
||||
return checkErr
|
||||
@@ -151,18 +148,11 @@ func (r *Requester) doRequest(req *http.Request, p *Item) error {
|
||||
r.drainBody(resp.Body)
|
||||
}
|
||||
|
||||
// Can't currently regenerate nonce and signatures with fresh values for retries, so for now, we must not retry
|
||||
if p.NonceEnabled {
|
||||
if timeoutErr, ok := err.(net.Error); !ok || !timeoutErr.Timeout() {
|
||||
return fmt.Errorf("unable to retry request using nonce, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if attempt > r.maxRetries {
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retry request, err: %v", err)
|
||||
return fmt.Errorf("%w, err: %v", errFailedToRetryRequest, err)
|
||||
}
|
||||
return fmt.Errorf("failed to retry request, status: %s", resp.Status)
|
||||
return fmt.Errorf("%w, status: %s", errFailedToRetryRequest, resp.Status)
|
||||
}
|
||||
|
||||
after := RetryAfter(resp, time.Now())
|
||||
@@ -262,8 +252,7 @@ func (r *Requester) GetNonce(isNano bool) nonce.Value {
|
||||
}
|
||||
return r.Nonce.Get()
|
||||
}
|
||||
r.Nonce.Inc()
|
||||
return r.Nonce.Get()
|
||||
return r.Nonce.GetInc()
|
||||
}
|
||||
|
||||
// GetNonceMilli returns a nonce for requests. This locks and enforces concurrent
|
||||
@@ -274,8 +263,7 @@ func (r *Requester) GetNonceMilli() nonce.Value {
|
||||
r.Nonce.Set(time.Now().UnixNano() / int64(time.Millisecond))
|
||||
return r.Nonce.Get()
|
||||
}
|
||||
r.Nonce.Inc()
|
||||
return r.Nonce.Get()
|
||||
return r.Nonce.GetInc()
|
||||
}
|
||||
|
||||
// SetProxy sets a proxy address to the client transport
|
||||
|
||||
@@ -178,6 +178,8 @@ type GlobalLimitTest struct {
|
||||
UnAuth *rate.Limiter
|
||||
}
|
||||
|
||||
var errEndpointLimitNotFound = errors.New("endpoint limit not found")
|
||||
|
||||
func (g *GlobalLimitTest) Limit(e EndpointLimit) error {
|
||||
switch e {
|
||||
case Auth:
|
||||
@@ -193,7 +195,9 @@ func (g *GlobalLimitTest) Limit(e EndpointLimit) error {
|
||||
time.Sleep(g.UnAuth.Reserve().Delay())
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("cannot execute functionality: %d not found", e)
|
||||
return fmt.Errorf("cannot execute functionality: %d %w",
|
||||
e,
|
||||
errEndpointLimitNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,81 +207,92 @@ var globalshell = GlobalLimitTest{
|
||||
|
||||
func TestDoRequest(t *testing.T) {
|
||||
t.Parallel()
|
||||
r := New("test",
|
||||
new(http.Client),
|
||||
WithLimiter(&globalshell))
|
||||
r := New("test", new(http.Client), WithLimiter(&globalshell))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err := r.SendPayload(ctx, &Item{})
|
||||
if err == nil {
|
||||
t.Fatal(unexpected)
|
||||
err := (*Requester)(nil).SendPayload(ctx, Unset, nil)
|
||||
if !errors.Is(errRequestSystemIsNil, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errRequestSystemIsNil, err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "invalid path") {
|
||||
t.Fatal(err)
|
||||
err = r.SendPayload(ctx, Unset, nil)
|
||||
if !errors.Is(errRequestFunctionIsNil, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errRequestFunctionIsNil, err)
|
||||
}
|
||||
|
||||
err = r.SendPayload(ctx, &Item{Method: http.MethodGet})
|
||||
if err == nil {
|
||||
t.Fatal(unexpected)
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) { return nil, nil })
|
||||
if !errors.Is(errRequestItemNil, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errRequestItemNil, err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "invalid path") {
|
||||
t.Fatal(err)
|
||||
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) { return &Item{}, nil })
|
||||
if !errors.Is(errInvalidPath, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errInvalidPath, err)
|
||||
}
|
||||
|
||||
var nilHeader http.Header
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Path: testURL,
|
||||
HeaderResponse: &nilHeader,
|
||||
}, nil
|
||||
})
|
||||
if !errors.Is(errHeaderResponseMapIsNil, err) {
|
||||
t.Fatalf("expected: %v but received: %v", errHeaderResponseMapIsNil, err)
|
||||
}
|
||||
|
||||
// Invalid/missing endpoint limit
|
||||
err = r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
err = r.SendPayload(ctx, Unset, func() (*Item, error) {
|
||||
return &Item{
|
||||
Path: testURL,
|
||||
}, nil
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal(unexpected)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "cannot execute functionality") {
|
||||
t.Fatal(err)
|
||||
if !errors.Is(err, errEndpointLimitNotFound) {
|
||||
t.Fatalf("expected: %v but received: %v", errEndpointLimitNotFound, err)
|
||||
}
|
||||
|
||||
// force debug
|
||||
err = r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
HTTPDebugging: true,
|
||||
Verbose: true,
|
||||
// Force debug
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Path: testURL,
|
||||
Headers: map[string]string{
|
||||
"test": "supertest",
|
||||
},
|
||||
Body: strings.NewReader("test"),
|
||||
HTTPDebugging: true,
|
||||
Verbose: true,
|
||||
}, nil
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal(unexpected)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: %v but expected: %v", err, nil)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "cannot execute functionality") {
|
||||
t.Fatal(err)
|
||||
|
||||
// Fail new request call
|
||||
newError := errors.New("request item failure")
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return nil, newError
|
||||
})
|
||||
if !errors.Is(err, newError) {
|
||||
t.Fatalf("received: %v but expected: %v", err, newError)
|
||||
}
|
||||
|
||||
// max request job ceiling
|
||||
r.jobs = MaxRequestJobs
|
||||
err = r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Endpoint: UnAuth,
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return &Item{Path: testURL}, nil
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal(unexpected)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "max request jobs reached") {
|
||||
t.Fatal(err)
|
||||
if !errors.Is(err, errMaxRequestJobs) {
|
||||
t.Fatalf("received: %v but expected: %v", err, errMaxRequestJobs)
|
||||
}
|
||||
// reset jobs
|
||||
r.jobs = 0
|
||||
|
||||
// timeout checker
|
||||
r.HTTPClient.Timeout = time.Millisecond * 50
|
||||
err = r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/timeout",
|
||||
Endpoint: UnAuth,
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return &Item{Path: testURL + "/timeout"}, nil
|
||||
})
|
||||
if err == nil {
|
||||
t.Fatal(unexpected)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "failed to retry request") {
|
||||
t.Fatal(err)
|
||||
if !errors.Is(err, errFailedToRetryRequest) {
|
||||
t.Fatalf("received: %v but expected: %v", err, errFailedToRetryRequest)
|
||||
}
|
||||
// reset timeout
|
||||
r.HTTPClient.Timeout = 0
|
||||
@@ -289,18 +304,16 @@ func TestDoRequest(t *testing.T) {
|
||||
|
||||
// Check header contents
|
||||
var passback = http.Header{}
|
||||
err = r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
Endpoint: UnAuth,
|
||||
HeaderResponse: &passback,
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
HeaderResponse: &passback,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !resp.Response {
|
||||
t.Fatal(unexpected)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: %v but expected: %v", err, nil)
|
||||
}
|
||||
|
||||
if passback.Get("Content-Length") != "17" {
|
||||
@@ -315,17 +328,19 @@ func TestDoRequest(t *testing.T) {
|
||||
var respErr struct {
|
||||
Error bool `json:"error"`
|
||||
}
|
||||
err = r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &respErr,
|
||||
Endpoint: UnAuth,
|
||||
err = r.SendPayload(ctx, UnAuth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &respErr,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
if !errors.Is(err, nil) {
|
||||
t.Fatalf("received: %v but expected: %v", err, nil)
|
||||
}
|
||||
if !resp.Response {
|
||||
t.Fatal(unexpected)
|
||||
|
||||
if respErr.Error {
|
||||
t.Fatal("unexpected value")
|
||||
}
|
||||
|
||||
// Check client side rate limit
|
||||
@@ -337,12 +352,13 @@ func TestDoRequest(t *testing.T) {
|
||||
var resp struct {
|
||||
Response bool `json:"response"`
|
||||
}
|
||||
payloadError := r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/rate",
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Endpoint: Auth,
|
||||
payloadError := r.SendPayload(ctx, Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/rate",
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
}, nil
|
||||
})
|
||||
wg.Done()
|
||||
if payloadError != nil {
|
||||
@@ -378,12 +394,13 @@ func TestDoRequest_Retries(t *testing.T) {
|
||||
var resp struct {
|
||||
Response bool `json:"response"`
|
||||
}
|
||||
payloadError := r.SendPayload(context.Background(), &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/rate-retry",
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Endpoint: Auth,
|
||||
payloadError := r.SendPayload(context.Background(), Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/rate-retry",
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
}, nil
|
||||
})
|
||||
if payloadError != nil {
|
||||
atomic.StoreInt32(&failed, 1)
|
||||
@@ -409,31 +426,36 @@ func TestDoRequest_RetryNonRecoverable(t *testing.T) {
|
||||
return 0
|
||||
}
|
||||
r := New("test", new(http.Client), WithBackoff(backoff))
|
||||
payloadError := r.SendPayload(context.Background(), &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/always-retry",
|
||||
err := r.SendPayload(context.Background(), Unset, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/always-retry",
|
||||
}, nil
|
||||
})
|
||||
if payloadError == nil {
|
||||
t.Fatal("expected an error")
|
||||
if !errors.Is(err, errFailedToRetryRequest) {
|
||||
t.Fatalf("received: %v but expected: %v", err, errFailedToRetryRequest)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoRequest_NotRetryable(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
notRetryErr := errors.New("not retryable")
|
||||
retry := func(resp *http.Response, err error) (bool, error) {
|
||||
return false, errors.New("not retryable")
|
||||
return false, notRetryErr
|
||||
}
|
||||
backoff := func(n int) time.Duration {
|
||||
return time.Duration(n) * time.Millisecond
|
||||
}
|
||||
r := New("test", new(http.Client), WithRetryPolicy(retry), WithBackoff(backoff))
|
||||
payloadError := r.SendPayload(context.Background(), &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/always-retry",
|
||||
err := r.SendPayload(context.Background(), Unset, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL + "/always-retry",
|
||||
}, nil
|
||||
})
|
||||
if payloadError == nil {
|
||||
t.Fatal("expected an error")
|
||||
if !errors.Is(err, notRetryErr) {
|
||||
t.Fatalf("received: %v but expected: %v", err, notRetryErr)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,8 +527,8 @@ func TestBasicLimiter(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
tn := time.Now()
|
||||
_ = r.SendPayload(ctx, &i)
|
||||
_ = r.SendPayload(ctx, &i)
|
||||
_ = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil })
|
||||
_ = r.SendPayload(ctx, Unset, func() (*Item, error) { return &i, nil })
|
||||
if time.Since(tn) < time.Second {
|
||||
t.Error("rate limit issues")
|
||||
}
|
||||
@@ -519,12 +541,13 @@ func TestEnableDisableRateLimit(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
var resp interface{}
|
||||
err := r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Endpoint: Auth,
|
||||
err := r.SendPayload(ctx, Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -540,12 +563,13 @@ func TestEnableDisableRateLimit(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Endpoint: Auth,
|
||||
err = r.SendPayload(ctx, Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -564,12 +588,13 @@ func TestEnableDisableRateLimit(t *testing.T) {
|
||||
ti := time.NewTicker(time.Second)
|
||||
c := make(chan struct{})
|
||||
go func(c chan struct{}) {
|
||||
err = r.SendPayload(ctx, &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
Endpoint: Auth,
|
||||
err = r.SendPayload(ctx, Auth, func() (*Item, error) {
|
||||
return &Item{
|
||||
Method: http.MethodGet,
|
||||
Path: testURL,
|
||||
Result: &resp,
|
||||
AuthRequest: true,
|
||||
}, nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -56,7 +56,6 @@ type Item struct {
|
||||
// HeaderResponse for inspection of header contents package side useful for
|
||||
// pagination
|
||||
HeaderResponse *http.Header
|
||||
Endpoint EndpointLimit
|
||||
}
|
||||
|
||||
// Backoff determines how long to wait between request attempts.
|
||||
@@ -67,3 +66,9 @@ type RetryPolicy func(resp *http.Response, err error) (bool, error)
|
||||
|
||||
// RequesterOption is a function option that can be applied to configure a Requester when creating it.
|
||||
type RequesterOption func(*Requester)
|
||||
|
||||
// Generate defines a closure for functionality outside of the requester to
|
||||
// to generate new *http.Request on every attempt. This minimizes the chance of
|
||||
// being outside of receive window if application rate limiting reduces outbound
|
||||
// requests.
|
||||
type Generate func() (*Item, error)
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -269,13 +268,18 @@ func (y *Yobit) SendHTTPRequest(ep exchange.URL, path string, result interface{}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return y.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: y.Verbose,
|
||||
HTTPDebugging: y.HTTPDebugging,
|
||||
HTTPRecording: y.HTTPRecording,
|
||||
}
|
||||
|
||||
return y.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -292,37 +296,32 @@ func (y *Yobit) SendAuthenticatedHTTPRequest(ep exchange.URL, path string, param
|
||||
params = url.Values{}
|
||||
}
|
||||
|
||||
n := y.Requester.GetNonce(false).String()
|
||||
return y.SendPayload(context.Background(), request.Unset, func() (*request.Item, error) {
|
||||
n := y.Requester.GetNonce(false).String()
|
||||
|
||||
params.Set("nonce", n)
|
||||
params.Set("method", path)
|
||||
params.Set("nonce", n)
|
||||
params.Set("method", path)
|
||||
|
||||
encoded := params.Encode()
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512, []byte(encoded), []byte(y.API.Credentials.Secret))
|
||||
encoded := params.Encode()
|
||||
hmac := crypto.GetHMAC(crypto.HashSHA512, []byte(encoded), []byte(y.API.Credentials.Secret))
|
||||
|
||||
if y.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "Sending POST request to %s calling path %s with params %s\n",
|
||||
endpoint,
|
||||
path,
|
||||
encoded)
|
||||
}
|
||||
headers := make(map[string]string)
|
||||
headers["Key"] = y.API.Credentials.Key
|
||||
headers["Sign"] = crypto.HexEncodeToString(hmac)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
headers := make(map[string]string)
|
||||
headers["Key"] = y.API.Credentials.Key
|
||||
headers["Sign"] = crypto.HexEncodeToString(hmac)
|
||||
headers["Content-Type"] = "application/x-www-form-urlencoded"
|
||||
|
||||
return y.SendPayload(context.Background(), &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: endpoint,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(encoded),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: y.Verbose,
|
||||
HTTPDebugging: y.HTTPDebugging,
|
||||
HTTPRecording: y.HTTPRecording,
|
||||
return &request.Item{
|
||||
Method: http.MethodPost,
|
||||
Path: endpoint,
|
||||
Headers: headers,
|
||||
Body: strings.NewReader(encoded),
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
NonceEnabled: true,
|
||||
Verbose: y.Verbose,
|
||||
HTTPDebugging: y.HTTPDebugging,
|
||||
HTTPRecording: y.HTTPRecording,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common/convert"
|
||||
@@ -19,8 +18,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
zbTradeURL = "http://api.zb.live"
|
||||
zbMarketURL = "https://trade.zb.live/api"
|
||||
zbTradeURL = "http://api.zb.land"
|
||||
zbMarketURL = "https://trade.zb.land/api"
|
||||
zbAPIVersion = "v1"
|
||||
zbData = "data"
|
||||
zbAccountInfo = "getAccountInfo"
|
||||
@@ -286,14 +285,18 @@ func (z *ZB) SendHTTPRequest(ep exchange.URL, path string, result interface{}, f
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return z.SendPayload(context.Background(), &request.Item{
|
||||
|
||||
item := &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: endpoint + path,
|
||||
Result: result,
|
||||
Verbose: z.Verbose,
|
||||
HTTPDebugging: z.HTTPDebugging,
|
||||
HTTPRecording: z.HTTPRecording,
|
||||
Endpoint: f,
|
||||
}
|
||||
|
||||
return z.SendPayload(context.Background(), f, func() (*request.Item, error) {
|
||||
return item, nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -312,40 +315,38 @@ func (z *ZB) SendAuthenticatedHTTPRequest(ep exchange.URL, httpMethod string, pa
|
||||
[]byte(params.Encode()),
|
||||
[]byte(crypto.Sha1ToHex(z.API.Credentials.Secret)))
|
||||
|
||||
now := time.Now()
|
||||
params.Set("reqTime", fmt.Sprintf("%d", convert.UnixMillis(now)))
|
||||
params.Set("sign", fmt.Sprintf("%x", hmac))
|
||||
|
||||
urlPath := fmt.Sprintf("%s/%s?%s",
|
||||
endpoint,
|
||||
params.Get("method"),
|
||||
params.Encode())
|
||||
|
||||
var intermediary json.RawMessage
|
||||
newRequest := func() (*request.Item, error) {
|
||||
now := time.Now()
|
||||
params.Set("reqTime", fmt.Sprintf("%d", convert.UnixMillis(now)))
|
||||
params.Set("sign", fmt.Sprintf("%x", hmac))
|
||||
|
||||
urlPath := fmt.Sprintf("%s/%s?%s",
|
||||
endpoint,
|
||||
params.Get("method"),
|
||||
params.Encode())
|
||||
|
||||
return &request.Item{
|
||||
Method: httpMethod,
|
||||
Path: urlPath,
|
||||
Result: &intermediary,
|
||||
AuthRequest: true,
|
||||
Verbose: z.Verbose,
|
||||
HTTPDebugging: z.HTTPDebugging,
|
||||
HTTPRecording: z.HTTPRecording,
|
||||
}, nil
|
||||
}
|
||||
|
||||
err = z.SendPayload(context.Background(), f, newRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
errCap := struct {
|
||||
Code int64 `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}{}
|
||||
|
||||
// Expiry of timestamp doesn't appear to be documented, so making a reasonable assumption
|
||||
ctx, cancel := context.WithDeadline(context.Background(), now.Add(15*time.Second))
|
||||
defer cancel()
|
||||
err = z.SendPayload(ctx, &request.Item{
|
||||
Method: httpMethod,
|
||||
Path: urlPath,
|
||||
Body: strings.NewReader(""),
|
||||
Result: &intermediary,
|
||||
AuthRequest: true,
|
||||
Verbose: z.Verbose,
|
||||
HTTPDebugging: z.HTTPDebugging,
|
||||
HTTPRecording: z.HTTPRecording,
|
||||
Endpoint: f,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(intermediary, &errCap)
|
||||
if err == nil {
|
||||
if errCap.Code > 1000 {
|
||||
|
||||
Reference in New Issue
Block a user