mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-21 23:16:49 +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:
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -521,53 +522,64 @@ func getCryptocurrencyWithdrawalFee(c currency.Code) float64 {
|
||||
}
|
||||
|
||||
// WithdrawCrypto withdraws cryptocurrency to your selected wallet
|
||||
func (g *Gateio) WithdrawCrypto(ctx context.Context, currency, address string, amount float64) (*withdraw.ExchangeResponse, error) {
|
||||
type response struct {
|
||||
func (g *Gateio) WithdrawCrypto(ctx context.Context, curr, address, memo, chain string, amount float64) (*withdraw.ExchangeResponse, error) {
|
||||
if curr == "" || address == "" || amount <= 0 {
|
||||
return nil, errors.New("currency, address and amount must be set")
|
||||
}
|
||||
|
||||
resp := struct {
|
||||
Result bool `json:"result"`
|
||||
Message string `json:"message"`
|
||||
Code int `json:"code"`
|
||||
}{}
|
||||
|
||||
vals := url.Values{}
|
||||
vals.Set("currency", strings.ToUpper(curr))
|
||||
vals.Set("amount", strconv.FormatFloat(amount, 'f', -1, 64))
|
||||
|
||||
// Transaction MEMO has to be entered after the address separated by a space
|
||||
if memo != "" {
|
||||
address += " " + memo
|
||||
}
|
||||
vals.Set("address", address)
|
||||
|
||||
if chain != "" {
|
||||
vals.Set("chain", strings.ToUpper(chain))
|
||||
}
|
||||
|
||||
var result response
|
||||
params := fmt.Sprintf("currency=%v&amount=%v&address=%v",
|
||||
currency,
|
||||
address,
|
||||
amount,
|
||||
)
|
||||
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, gateioWithdraw, params, &result)
|
||||
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, gateioWithdraw, vals.Encode(), &resp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !result.Result {
|
||||
return nil, fmt.Errorf("code:%d message:%s", result.Code, result.Message)
|
||||
if !resp.Result {
|
||||
return nil, fmt.Errorf("code:%d message:%s", resp.Code, resp.Message)
|
||||
}
|
||||
|
||||
return &withdraw.ExchangeResponse{
|
||||
Status: result.Message,
|
||||
Status: resp.Message,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetCryptoDepositAddress returns a deposit address for a cryptocurrency
|
||||
func (g *Gateio) GetCryptoDepositAddress(ctx context.Context, currency string) (string, error) {
|
||||
type response struct {
|
||||
Result bool `json:"result,string"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Address string `json:"addr"`
|
||||
}
|
||||
|
||||
var result response
|
||||
func (g *Gateio) GetCryptoDepositAddress(ctx context.Context, curr string) (*DepositAddr, error) {
|
||||
var result DepositAddr
|
||||
params := fmt.Sprintf("currency=%s",
|
||||
currency)
|
||||
curr)
|
||||
|
||||
err := g.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, gateioDepositAddress, params, &result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !result.Result {
|
||||
return "", fmt.Errorf("code:%d message:%s", result.Code, result.Message)
|
||||
return nil, fmt.Errorf("code:%d message:%s", result.Code, result.Message)
|
||||
}
|
||||
|
||||
return result.Address, nil
|
||||
// For memo/payment ID currencies
|
||||
if strings.Contains(result.Address, " ") {
|
||||
split := strings.Split(result.Address, " ")
|
||||
result.Address = split[0]
|
||||
result.Tag = split[1]
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
@@ -423,6 +423,7 @@ func TestModifyOrder(t *testing.T) {
|
||||
|
||||
func TestWithdraw(t *testing.T) {
|
||||
withdrawCryptoRequest := withdraw.Request{
|
||||
Exchange: g.Name,
|
||||
Amount: -1,
|
||||
Currency: currency.BTC,
|
||||
Description: "WITHDRAW IT ALL",
|
||||
@@ -472,12 +473,12 @@ func TestWithdrawInternationalBank(t *testing.T) {
|
||||
|
||||
func TestGetDepositAddress(t *testing.T) {
|
||||
if areTestAPIKeysSet() {
|
||||
_, err := g.GetDepositAddress(context.Background(), currency.ETC, "")
|
||||
_, err := g.GetDepositAddress(context.Background(), currency.USDT, "", "TRX")
|
||||
if err != nil {
|
||||
t.Error("Test Fail - GetDepositAddress error", err)
|
||||
}
|
||||
} else {
|
||||
_, err := g.GetDepositAddress(context.Background(), currency.ETC, "")
|
||||
_, err := g.GetDepositAddress(context.Background(), currency.ETC, "", "")
|
||||
if err == nil {
|
||||
t.Error("Test Fail - GetDepositAddress error cannot be nil")
|
||||
}
|
||||
@@ -867,3 +868,25 @@ func TestUpdateTickers(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCryptoDepositAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("api keys not set")
|
||||
}
|
||||
_, err := g.GetCryptoDepositAddress(context.Background(), currency.USDT.String())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAvailableTransferTrains(t *testing.T) {
|
||||
t.Parallel()
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("api keys not set")
|
||||
}
|
||||
_, err := g.GetAvailableTransferChains(context.Background(), currency.USDT)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -532,3 +532,19 @@ type wsOrderbook struct {
|
||||
Bids [][]string `json:"bids"`
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
// DepositAddr stores the deposit address info
|
||||
type DepositAddr struct {
|
||||
Result bool `json:"result,string"`
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Address string `json:"addr"`
|
||||
Tag string
|
||||
MultichainAddresses []struct {
|
||||
Chain string `json:"chain"`
|
||||
Address string `json:"address"`
|
||||
PaymentID string `json:"payment_id"`
|
||||
PaymentName string `json:"payment_name"`
|
||||
ObtainFailed uint8 `json:"obtain_failed"`
|
||||
} `json:"multichain_addresses"`
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -72,23 +73,25 @@ func (g *Gateio) 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,
|
||||
CancelOrders: true,
|
||||
CancelOrder: true,
|
||||
SubmitOrder: true,
|
||||
UserTradeHistory: true,
|
||||
CryptoDeposit: true,
|
||||
CryptoWithdrawal: true,
|
||||
TradeFee: true,
|
||||
CryptoWithdrawalFee: true,
|
||||
TickerBatching: true,
|
||||
TickerFetching: true,
|
||||
KlineFetching: true,
|
||||
TradeFetching: true,
|
||||
OrderbookFetching: true,
|
||||
AutoPairUpdates: true,
|
||||
AccountInfo: true,
|
||||
GetOrder: true,
|
||||
GetOrders: true,
|
||||
CancelOrders: true,
|
||||
CancelOrder: true,
|
||||
SubmitOrder: true,
|
||||
UserTradeHistory: true,
|
||||
CryptoDeposit: true,
|
||||
CryptoWithdrawal: true,
|
||||
TradeFee: true,
|
||||
CryptoWithdrawalFee: true,
|
||||
MultiChainDeposits: true,
|
||||
MultiChainWithdrawals: true,
|
||||
},
|
||||
WebsocketCapabilities: protocol.Features{
|
||||
TickerFetching: true,
|
||||
@@ -626,17 +629,32 @@ func (g *Gateio) GetOrderInfo(ctx context.Context, orderID string, pair currency
|
||||
}
|
||||
|
||||
// GetDepositAddress returns a deposit address for a specified currency
|
||||
func (g *Gateio) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _ string) (string, error) {
|
||||
func (g *Gateio) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _, chain string) (*deposit.Address, error) {
|
||||
addr, err := g.GetCryptoDepositAddress(ctx, cryptocurrency.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if addr == gateioGenerateAddress {
|
||||
return "",
|
||||
if addr.Address == gateioGenerateAddress {
|
||||
return nil,
|
||||
errors.New("new deposit address is being generated, please retry again shortly")
|
||||
}
|
||||
return addr, nil
|
||||
|
||||
if chain != "" {
|
||||
for x := range addr.MultichainAddresses {
|
||||
if strings.EqualFold(addr.MultichainAddresses[x].Chain, chain) {
|
||||
return &deposit.Address{
|
||||
Address: addr.MultichainAddresses[x].Address,
|
||||
Tag: addr.MultichainAddresses[x].PaymentName,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("network %s not found", chain)
|
||||
}
|
||||
return &deposit.Address{
|
||||
Address: addr.Address,
|
||||
Tag: addr.Tag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
|
||||
@@ -648,7 +666,10 @@ func (g *Gateio) WithdrawCryptocurrencyFunds(ctx context.Context, withdrawReques
|
||||
return g.WithdrawCrypto(ctx,
|
||||
withdrawRequest.Currency.String(),
|
||||
withdrawRequest.Crypto.Address,
|
||||
withdrawRequest.Amount)
|
||||
withdrawRequest.Crypto.AddressTag,
|
||||
withdrawRequest.Crypto.Chain,
|
||||
withdrawRequest.Amount,
|
||||
)
|
||||
}
|
||||
|
||||
// WithdrawFiatFunds returns a withdrawal ID when a
|
||||
@@ -865,3 +886,18 @@ func (g *Gateio) GetHistoricCandles(ctx context.Context, pair currency.Pair, a a
|
||||
func (g *Gateio) GetHistoricCandlesExtended(ctx context.Context, pair currency.Pair, a asset.Item, start, end time.Time, interval kline.Interval) (kline.Item, error) {
|
||||
return g.GetHistoricCandles(ctx, pair, a, start, end, interval)
|
||||
}
|
||||
|
||||
// GetAvailableTransferChains returns the available transfer blockchains for the specific
|
||||
// cryptocurrency
|
||||
func (g *Gateio) GetAvailableTransferChains(ctx context.Context, cryptocurrency currency.Code) ([]string, error) {
|
||||
chains, err := g.GetCryptoDepositAddress(ctx, cryptocurrency.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var availableChains []string
|
||||
for x := range chains.MultichainAddresses {
|
||||
availableChains = append(availableChains, chains.MultichainAddresses[x].Chain)
|
||||
}
|
||||
return availableChains, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user