mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-30 15:10:40 +00:00
exchanges/engine: Add multichain deposit/withdrawal support (#794)
* Add exchange multichain support * Start tidying up * Add multichain transfer support for Bitfinex and fix poloniex bug * Add Coinbene multichain support * Start adjusting the deposit address manager * Fix deposit tests and further enhancements * Cleanup * Add bypass flag, expand tests plus error coverage for Huobi Adjust helpers * Address nitterinos * BFX wd changes * Address nitterinos * Minor fixes rebasing on master * Fix BFX acceptableMethods test * Add some TO-DOs for 2 tests WRT races * Fix acceptableMethods test round 2 * Address nitterinos
This commit is contained in:
@@ -1071,8 +1071,13 @@ func (k *Kraken) SendAuthenticatedHTTPRequest(ctx context.Context, ep exchange.U
|
||||
}
|
||||
var errCap SpotAuthError
|
||||
if err = json.Unmarshal(interim, &errCap); err == nil {
|
||||
if len(errCap.Error) != 0 {
|
||||
return errors.New(errCap.Error[0])
|
||||
if errCap.Error != nil {
|
||||
switch e := errCap.Error.(type) {
|
||||
case []string:
|
||||
return errors.New(e[0])
|
||||
case string:
|
||||
return errors.New(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
return json.Unmarshal(interim, result)
|
||||
@@ -1148,7 +1153,7 @@ func calculateTradingFee(currency string, feePair map[string]TradeVolumeFee, pur
|
||||
}
|
||||
|
||||
// GetCryptoDepositAddress returns a deposit address for a cryptocurrency
|
||||
func (k *Kraken) GetCryptoDepositAddress(ctx context.Context, method, code string) (string, error) {
|
||||
func (k *Kraken) GetCryptoDepositAddress(ctx context.Context, method, code string, createNew bool) ([]DepositAddress, error) {
|
||||
var resp = struct {
|
||||
Error []string `json:"error"`
|
||||
Result []DepositAddress `json:"result"`
|
||||
@@ -1158,16 +1163,19 @@ func (k *Kraken) GetCryptoDepositAddress(ctx context.Context, method, code strin
|
||||
values.Set("asset", code)
|
||||
values.Set("method", method)
|
||||
|
||||
if createNew {
|
||||
values.Set("new", "1")
|
||||
}
|
||||
|
||||
err := k.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, krakenDepositAddresses, values, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, a := range resp.Result {
|
||||
return a.Address, nil
|
||||
if len(resp.Result) == 0 {
|
||||
return nil, errors.New("no addresses returned")
|
||||
}
|
||||
|
||||
return "", errors.New("no addresses returned")
|
||||
return resp.Result, nil
|
||||
}
|
||||
|
||||
// WithdrawStatus gets the status of recent withdrawals
|
||||
|
||||
@@ -560,6 +560,18 @@ func TestGetBalance(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetTradeBalance API endpoint test
|
||||
func TestGetDepositMethods(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("no api keys set")
|
||||
}
|
||||
_, err := k.GetDepositMethods(context.Background(), "USDT")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetTradeBalance API endpoint test
|
||||
func TestGetTradeBalance(t *testing.T) {
|
||||
t.Parallel()
|
||||
@@ -1082,16 +1094,34 @@ func TestWithdrawInternationalBank(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCryptoDepositAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("API keys not set")
|
||||
}
|
||||
_, err := k.GetCryptoDepositAddress(context.Background(), "Bitcoin", "XBT", false)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if !canManipulateRealOrders {
|
||||
t.Skip("canManipulateRealOrders not set, skipping test")
|
||||
}
|
||||
_, err = k.GetCryptoDepositAddress(context.Background(), "Bitcoin", "XBT", true)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetDepositAddress wrapper test
|
||||
func TestGetDepositAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
if areTestAPIKeysSet() {
|
||||
_, err := k.GetDepositAddress(context.Background(), currency.BTC, "")
|
||||
_, err := k.GetDepositAddress(context.Background(), currency.USDT, "", "")
|
||||
if err != nil {
|
||||
t.Error("GetDepositAddress() error", err)
|
||||
}
|
||||
} else {
|
||||
_, err := k.GetDepositAddress(context.Background(), currency.BTC, "")
|
||||
_, err := k.GetDepositAddress(context.Background(), currency.BTC, "", "")
|
||||
if err == nil {
|
||||
t.Error("GetDepositAddress() error can not be nil")
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ type AuthErrorData struct {
|
||||
|
||||
// SpotAuthError stores authenticated error messages
|
||||
type SpotAuthError struct {
|
||||
Error []string `json:"error"`
|
||||
Error interface{} `json:"error"` // can be a []string or string
|
||||
}
|
||||
|
||||
// Asset holds asset information
|
||||
@@ -456,9 +456,10 @@ var WithdrawalFees = map[currency.Code]float64{
|
||||
|
||||
// DepositAddress defines a deposit address
|
||||
type DepositAddress struct {
|
||||
Address string `json:"address"`
|
||||
ExpireTime int64 `json:"expiretm,string"`
|
||||
New bool `json:"new"`
|
||||
Address string `json:"address"`
|
||||
ExpireTime interface{} `json:"expiretm"` // this is an int when new is specified
|
||||
Tag string `json:"tag"`
|
||||
New bool `json:"new"`
|
||||
}
|
||||
|
||||
// WithdrawStatusResponse defines a withdrawal status response
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
@@ -104,27 +105,29 @@ func (k *Kraken) SetDefaults() {
|
||||
REST: true,
|
||||
Websocket: true,
|
||||
RESTCapabilities: protocol.Features{
|
||||
TickerBatching: true,
|
||||
TickerFetching: true,
|
||||
KlineFetching: true,
|
||||
TradeFetching: true,
|
||||
OrderbookFetching: true,
|
||||
AutoPairUpdates: true,
|
||||
AccountInfo: true,
|
||||
GetOrder: true,
|
||||
GetOrders: true,
|
||||
CancelOrder: true,
|
||||
SubmitOrder: true,
|
||||
UserTradeHistory: true,
|
||||
CryptoDeposit: true,
|
||||
CryptoWithdrawal: true,
|
||||
FiatDeposit: true,
|
||||
FiatWithdraw: true,
|
||||
TradeFee: true,
|
||||
FiatDepositFee: true,
|
||||
FiatWithdrawalFee: true,
|
||||
CryptoDepositFee: true,
|
||||
CryptoWithdrawalFee: true,
|
||||
TickerBatching: true,
|
||||
TickerFetching: true,
|
||||
KlineFetching: true,
|
||||
TradeFetching: true,
|
||||
OrderbookFetching: true,
|
||||
AutoPairUpdates: true,
|
||||
AccountInfo: true,
|
||||
GetOrder: true,
|
||||
GetOrders: true,
|
||||
CancelOrder: true,
|
||||
SubmitOrder: true,
|
||||
UserTradeHistory: true,
|
||||
CryptoDeposit: true,
|
||||
CryptoWithdrawal: true,
|
||||
FiatDeposit: true,
|
||||
FiatWithdraw: true,
|
||||
TradeFee: true,
|
||||
FiatDepositFee: true,
|
||||
FiatWithdrawalFee: true,
|
||||
CryptoDepositFee: true,
|
||||
CryptoWithdrawalFee: true,
|
||||
MultiChainDeposits: true,
|
||||
MultiChainWithdrawals: true,
|
||||
},
|
||||
WebsocketCapabilities: protocol.Features{
|
||||
TickerFetching: true,
|
||||
@@ -975,19 +978,33 @@ func (k *Kraken) GetOrderInfo(ctx context.Context, orderID string, pair currency
|
||||
}
|
||||
|
||||
// GetDepositAddress returns a deposit address for a specified currency
|
||||
func (k *Kraken) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _ string) (string, error) {
|
||||
methods, err := k.GetDepositMethods(ctx, cryptocurrency.String())
|
||||
func (k *Kraken) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _, chain string) (*deposit.Address, error) {
|
||||
if chain == "" {
|
||||
methods, err := k.GetDepositMethods(ctx, cryptocurrency.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(methods) == 0 {
|
||||
return nil, errors.New("unable to get any deposit methods")
|
||||
}
|
||||
chain = methods[0].Method
|
||||
}
|
||||
|
||||
depositAddr, err := k.GetCryptoDepositAddress(ctx, chain, cryptocurrency.String(), false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
if strings.Contains(err.Error(), "no addresses returned") {
|
||||
depositAddr, err = k.GetCryptoDepositAddress(ctx, chain, cryptocurrency.String(), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var method string
|
||||
for _, m := range methods {
|
||||
method = m.Method
|
||||
}
|
||||
if method == "" {
|
||||
return "", errors.New("method not found")
|
||||
}
|
||||
return k.GetCryptoDepositAddress(ctx, method, cryptocurrency.String())
|
||||
return &deposit.Address{
|
||||
Address: depositAddr[0].Address,
|
||||
Tag: depositAddr[0].Tag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal
|
||||
@@ -1501,3 +1518,18 @@ func compatibleFillOrderType(fillType string) (order.Type, error) {
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetAvailableTransferChains returns the available transfer blockchains for the specific
|
||||
// cryptocurrency
|
||||
func (k *Kraken) GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error) {
|
||||
methods, err := k.GetDepositMethods(ctx, cryptocurrency.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var availableChains []string
|
||||
for x := range methods {
|
||||
availableChains = append(availableChains, methods[x].Method)
|
||||
}
|
||||
return availableChains, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user