mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-19 23:16:48 +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:
@@ -46,6 +46,7 @@ const (
|
||||
getBalances = "/balances"
|
||||
getBalance = "/balances/%s"
|
||||
getDepositAddress = "/addresses/%s"
|
||||
depositAddresses = "/addresses/"
|
||||
getAllOpenOrders = "/orders/open"
|
||||
getOpenOrders = "/orders/open?marketSymbol=%s"
|
||||
getOrder = "/orders/%s"
|
||||
@@ -235,12 +236,26 @@ func (b *Bittrex) GetAccountBalanceByCurrency(ctx context.Context, currency stri
|
||||
return resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(getBalance, currency), nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetCryptoDepositAddresses is used to retrieve all deposit addresses
|
||||
func (b *Bittrex) GetCryptoDepositAddresses(ctx context.Context) ([]AddressData, error) {
|
||||
var resp []AddressData
|
||||
return resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, depositAddresses, nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// GetCryptoDepositAddress is used to retrieve an address for a specific currency
|
||||
func (b *Bittrex) GetCryptoDepositAddress(ctx context.Context, currency string) (AddressData, error) {
|
||||
var resp AddressData
|
||||
return resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodGet, fmt.Sprintf(getDepositAddress, currency), nil, nil, &resp, nil)
|
||||
}
|
||||
|
||||
// ProvisionNewDepositAddress provisions a new deposit address for a specific currency
|
||||
func (b *Bittrex) ProvisionNewDepositAddress(ctx context.Context, currency string) (*ProvisionNewAddressData, error) {
|
||||
req := make(map[string]interface{}, 1)
|
||||
req["currencySymbol"] = currency
|
||||
var resp ProvisionNewAddressData
|
||||
return &resp, b.SendAuthHTTPRequest(ctx, exchange.RestSpot, http.MethodPost, depositAddresses, nil, req, &resp, nil)
|
||||
}
|
||||
|
||||
// Withdraw is used to withdraw funds from your account.
|
||||
func (b *Bittrex) Withdraw(ctx context.Context, currency, paymentID, address string, quantity float64) (WithdrawalData, error) {
|
||||
req := make(map[string]interface{})
|
||||
|
||||
@@ -266,6 +266,30 @@ func TestGetOpenWithdrawals(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCryptoDepositAddresses(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("api keys not set")
|
||||
}
|
||||
_, err := b.GetCryptoDepositAddresses(context.Background())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisionNewDepositAddress(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if !areTestAPIKeysSet() {
|
||||
t.Skip("api keys not set")
|
||||
}
|
||||
_, err := b.ProvisionNewDepositAddress(context.Background(), currency.XRP.String())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetClosedDeposits(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -570,6 +594,7 @@ func TestModifyOrder(t *testing.T) {
|
||||
func WithdrawCryptocurrencyFunds(t *testing.T) {
|
||||
t.Helper()
|
||||
withdrawCryptoRequest := withdraw.Request{
|
||||
Exchange: b.Name,
|
||||
Amount: -1,
|
||||
Currency: currency.BTC,
|
||||
Description: "WITHDRAW IT ALL",
|
||||
@@ -621,12 +646,12 @@ func TestWithdrawInternationalBank(t *testing.T) {
|
||||
|
||||
func TestGetDepositAddress(t *testing.T) {
|
||||
if areTestAPIKeysSet() {
|
||||
_, err := b.GetDepositAddress(context.Background(), currency.BTC, "")
|
||||
_, err := b.GetDepositAddress(context.Background(), currency.XRP, "", "")
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
} else {
|
||||
_, err := b.GetDepositAddress(context.Background(), currency.BTC, "")
|
||||
_, err := b.GetDepositAddress(context.Background(), currency.BTC, "", "")
|
||||
if err == nil {
|
||||
t.Error("error cannot be nil")
|
||||
}
|
||||
|
||||
@@ -127,6 +127,13 @@ type AddressData struct {
|
||||
CryptoAddressTag string `json:"cryptoAddressTag"`
|
||||
}
|
||||
|
||||
// ProvisionNewAddressData holds the provision deposit data
|
||||
// Status is REQUESTED
|
||||
type ProvisionNewAddressData struct {
|
||||
Status string `json:"status"`
|
||||
CurrencySymbol string `json:"currencySymbol"`
|
||||
}
|
||||
|
||||
// CurrencyData holds currency data
|
||||
// Status is ONLINE or OFFLINE
|
||||
type CurrencyData struct {
|
||||
|
||||
@@ -15,6 +15,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"
|
||||
@@ -697,16 +698,16 @@ func (b *Bittrex) ConstructOrderDetail(orderData *OrderData) (order.Detail, erro
|
||||
}
|
||||
|
||||
// GetDepositAddress returns a deposit address for a specified currency
|
||||
func (b *Bittrex) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _ string) (string, error) {
|
||||
func (b *Bittrex) GetDepositAddress(ctx context.Context, cryptocurrency currency.Code, _, _ string) (*deposit.Address, error) {
|
||||
depositAddr, err := b.GetCryptoDepositAddress(ctx, cryptocurrency.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if depositAddr.Status != "PROVISIONED" {
|
||||
return "", errors.New("no deposit address found for currency" + cryptocurrency.String())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return depositAddr.CryptoAddress, nil
|
||||
return &deposit.Address{
|
||||
Address: depositAddr.CryptoAddress,
|
||||
Tag: depositAddr.CryptoAddressTag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
|
||||
|
||||
@@ -232,23 +232,6 @@ func (b *Bittrex) processJob(p currency.Pair) error {
|
||||
return b.applyBufferUpdate(p)
|
||||
}
|
||||
|
||||
// flushAndCleanup flushes orderbook and clean local cache
|
||||
func (b *Bittrex) flushAndCleanup(p currency.Pair) {
|
||||
errClean := b.Websocket.Orderbook.FlushOrderbook(p, asset.Spot)
|
||||
if errClean != nil {
|
||||
log.Errorf(log.WebsocketMgr,
|
||||
"%s flushing websocket error: %v",
|
||||
b.Name,
|
||||
errClean)
|
||||
}
|
||||
errClean = b.obm.cleanup(p)
|
||||
if errClean != nil {
|
||||
log.Errorf(log.WebsocketMgr, "%s cleanup websocket error: %v",
|
||||
b.Name,
|
||||
errClean)
|
||||
}
|
||||
}
|
||||
|
||||
// stageWsUpdate stages websocket update to roll through updates that need to
|
||||
// be applied to a fetched orderbook via REST.
|
||||
func (o *orderbookManager) stageWsUpdate(u *OrderbookUpdateMessage, pair currency.Pair, a asset.Item) error {
|
||||
@@ -310,25 +293,6 @@ func (o *orderbookManager) stopFetchingBook(pair currency.Pair) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// stopNeedsFetchingBook completes the book fetching initiation.
|
||||
func (o *orderbookManager) stopNeedsFetchingBook(pair currency.Pair) error {
|
||||
o.Lock()
|
||||
defer o.Unlock()
|
||||
state, ok := o.state[pair.Base][pair.Quote][asset.Spot]
|
||||
if !ok {
|
||||
return fmt.Errorf("could not match pair %s and asset type %s in hash table",
|
||||
pair,
|
||||
asset.Spot)
|
||||
}
|
||||
if !state.needsFetchingBook {
|
||||
return fmt.Errorf("needs fetching book already set to false for %s %s",
|
||||
pair,
|
||||
asset.Spot)
|
||||
}
|
||||
state.needsFetchingBook = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// setNeedsFetchingBook completes the book fetching initiation.
|
||||
func (o *orderbookManager) setNeedsFetchingBook(pair currency.Pair) error {
|
||||
o.Lock()
|
||||
@@ -368,25 +332,6 @@ func (o *orderbookManager) handleFetchingBook(pair currency.Pair) (fetching, nee
|
||||
return false, false, nil
|
||||
}
|
||||
|
||||
// completeInitialSync sets if an asset type has completed its initial sync
|
||||
func (o *orderbookManager) completeInitialSync(pair currency.Pair) error {
|
||||
o.Lock()
|
||||
defer o.Unlock()
|
||||
state, ok := o.state[pair.Base][pair.Quote][asset.Spot]
|
||||
if !ok {
|
||||
return fmt.Errorf("complete initial sync cannot match currency pair %s asset type %s",
|
||||
pair,
|
||||
asset.Spot)
|
||||
}
|
||||
if !state.initialSync {
|
||||
return fmt.Errorf("initital sync already set to false for %s %s",
|
||||
pair,
|
||||
asset.Spot)
|
||||
}
|
||||
state.initialSync = false
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkIsInitialSync checks status if the book is Initial Sync being via the REST
|
||||
// protocol.
|
||||
func (o *orderbookManager) checkIsInitialSync(pair currency.Pair) (bool, error) {
|
||||
@@ -488,31 +433,3 @@ func (u *update) validate(updt *OrderbookUpdateMessage, recent *orderbook.Base)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// cleanup cleans up buffer and reset fetch and init
|
||||
func (o *orderbookManager) cleanup(pair currency.Pair) error {
|
||||
o.Lock()
|
||||
state, ok := o.state[pair.Base][pair.Quote][asset.Spot]
|
||||
if !ok {
|
||||
o.Unlock()
|
||||
return fmt.Errorf("cleanup cannot match %s %s to hash table",
|
||||
pair,
|
||||
asset.Spot)
|
||||
}
|
||||
|
||||
bufferEmpty:
|
||||
for {
|
||||
select {
|
||||
case <-state.buffer:
|
||||
// bleed and discard buffer
|
||||
default:
|
||||
break bufferEmpty
|
||||
}
|
||||
}
|
||||
o.Unlock()
|
||||
// disable rest orderbook synchronisation
|
||||
_ = o.stopFetchingBook(pair)
|
||||
_ = o.completeInitialSync(pair)
|
||||
_ = o.stopNeedsFetchingBook(pair)
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user