mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
Withdraw additional functionality (validation/submission/tracking) (#409)
* reworked request struct and exchange response started work on validation system * removed import cycle until work around * Added intial withdraw support via CLI added * Added Crypto command to gctcli * moved var declartion to single line * Test updates for binance and anx * All exchange tests have been updated test coverage added to validate * First pass at adding withdrawl select from database * started adding basic lru cache system * Added basic LRU cache including Add Get Remove Contains ContainsOrAdd Clear * wording changes on comments * removed exported var's in strut as they are not required * Added README * README updates * corrected ID on commands * rm line :D * merged in origin/cache * linter fixes (gofmt) * Added basic cache lookup to events * swapped to mutex over rwmutex updated comments * unexported getNewest & getOldest * unexported getNewest & getOldest * Updated comments and cited references in source * updated comments * WIP * Migrated exchange WithdrawFiat wrapper to new struct response * Migrated exchange WithdrawFiat wrapper to new struct response * started work on bank management * Added exchange level banking details back with migration to banking package * Removed broken tests for now * Added validation to bank accounts * removed duplicate bank details from withdraw struct * Test coverage increased * gofmt * merged upstream/master with clean up * First pass at adding command line linking to gctcli * added validation for crypto address, added gctcli support to retreive previous withdrawal requests * general cleanup * general cleanup * reordered imports * Increased test coverage moved to database sublogger * Pass incorrect currency no longer return error from c.CheckBankAccountConfig * remove TestMain() for now as other tests in this package will need to be reworked * Happy little race car * Reverted to upstream tests * Added test covarege for validation method, corrected response on cli protobuf * query clean up and removal of duplicated code * cleaned up queries into singlem ethod increased test coverage * Migrated international fund withdraw to new exchange response and added cache size override * Migrated international fund withdraw to new exchange response and added cache size override * Extended gctcli commands * lowered default cache to 25 * small code clean up * added get by date method * add returned error * cli commands cleaing return error on nil results to fix out of bounds * merged write & read helpers into one for test coverage and increased engine/withdraw test coverage * gofmt * Added test coverage for valid ID * removed unused param * converted to use timestamp package from protobuf * gofmt * use built in RFC3339 timestamp * remove setting of CreatedAt & UpdatedAt and allow ORm to take care of it * also use ptype on byid * code flow improvements * remove inverse conditional check and linters run * removed test data * removed comment * removed comment * also write failures to database for auditing * converted to use default time for start & end * Default to time.Now() minus 30 days * Default to time.Now() minus 30 days * small code clean up * fixed missing semicolon on migrations, code clean up * updated sqlite migrations * Added additonal check for exchange level bank account if global is not found * case sensativity fix for currency names * use correct compare * test coverage fixed * removed space * return pointer to banking.Account * return pointer to banking.Account * added else check back to validate() * Added empty string as default to migration over NULL due to retrivial of data
This commit is contained in:
@@ -20,6 +20,7 @@ import (
|
||||
gctscript "github.com/thrasher-corp/gocryptotrader/gctscript/vm"
|
||||
gctlog "github.com/thrasher-corp/gocryptotrader/log"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
"github.com/thrasher-corp/gocryptotrader/utils"
|
||||
)
|
||||
|
||||
@@ -121,6 +122,7 @@ func ValidateSettings(b *Engine, s *Settings) {
|
||||
b.Settings.MaxVirtualMachines = s.MaxVirtualMachines
|
||||
b.Settings.EnableDispatcher = s.EnableDispatcher
|
||||
b.Settings.EnablePortfolioManager = s.EnablePortfolioManager
|
||||
b.Settings.WithdrawCacheSize = s.WithdrawCacheSize
|
||||
if b.Settings.EnablePortfolioManager {
|
||||
if b.Settings.PortfolioManagerDelay != time.Duration(0) && s.PortfolioManagerDelay > 0 {
|
||||
b.Settings.PortfolioManagerDelay = s.PortfolioManagerDelay
|
||||
@@ -161,6 +163,10 @@ func ValidateSettings(b *Engine, s *Settings) {
|
||||
gctscript.GCTScriptConfig.MaxVirtualMachines = uint8(s.MaxVirtualMachines)
|
||||
}
|
||||
|
||||
if flagSet["withdrawcachesize"] {
|
||||
withdraw.CacheSize = s.WithdrawCacheSize
|
||||
}
|
||||
|
||||
b.Settings.EnableCommsRelayer = s.EnableCommsRelayer
|
||||
b.Settings.EnableEventManager = s.EnableEventManager
|
||||
|
||||
@@ -287,6 +293,8 @@ func PrintSettings(s *Settings) {
|
||||
gctlog.Debugf(gctlog.Global, "- GCTSCRIPT SETTINGS: ")
|
||||
gctlog.Debugf(gctlog.Global, "\t Enable GCTScript manager: %v", s.EnableGCTScriptManager)
|
||||
gctlog.Debugf(gctlog.Global, "\t GCTScript max virtual machines: %v", s.MaxVirtualMachines)
|
||||
gctlog.Debugf(gctlog.Global, "- WITHDRAW SETTINGS: ")
|
||||
gctlog.Debugf(gctlog.Global, "\t Withdraw Cache size: %v", s.WithdrawCacheSize)
|
||||
gctlog.Debugf(gctlog.Global, "- COMMON SETTINGS:")
|
||||
gctlog.Debugf(gctlog.Global, "\t Global HTTP timeout: %v", s.GlobalHTTPTimeout)
|
||||
gctlog.Debugf(gctlog.Global, "\t Global HTTP user agent: %v", s.GlobalHTTPUserAgent)
|
||||
|
||||
@@ -78,6 +78,9 @@ type Settings struct {
|
||||
|
||||
// GCTscript settings
|
||||
MaxVirtualMachines uint
|
||||
|
||||
// Withdraw settings
|
||||
WithdrawCacheSize uint64
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/stats"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/withdraw"
|
||||
"github.com/thrasher-corp/gocryptotrader/gctscript/vm"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio"
|
||||
@@ -690,20 +689,6 @@ func GetExchangeCryptocurrencyDepositAddresses() map[string]map[string]string {
|
||||
return result
|
||||
}
|
||||
|
||||
// WithdrawCryptocurrencyFundsByExchange withdraws the desired cryptocurrency and amount to a desired cryptocurrency address
|
||||
func WithdrawCryptocurrencyFundsByExchange(exchName string, req *withdraw.CryptoRequest) (string, error) {
|
||||
if req == nil {
|
||||
return "", errors.New("crypto withdraw request param is nil")
|
||||
}
|
||||
|
||||
exch := GetExchangeByName(exchName)
|
||||
if exch == nil {
|
||||
return "", ErrExchangeNotFound
|
||||
}
|
||||
|
||||
return exch.WithdrawCryptocurrencyFunds(req)
|
||||
}
|
||||
|
||||
// FormatCurrency is a method that formats and returns a currency pair
|
||||
// based on the user currency display preferences
|
||||
func FormatCurrency(p currency.Pair) currency.Pair {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
grpcauth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
|
||||
grpcruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
@@ -21,6 +22,7 @@ import (
|
||||
"github.com/thrasher-corp/gocryptotrader/common/file"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/file/archive"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/database"
|
||||
"github.com/thrasher-corp/gocryptotrader/database/models/postgres"
|
||||
"github.com/thrasher-corp/gocryptotrader/database/models/sqlite3"
|
||||
"github.com/thrasher-corp/gocryptotrader/database/repository/audit"
|
||||
@@ -34,6 +36,8 @@ import (
|
||||
gctscript "github.com/thrasher-corp/gocryptotrader/gctscript/vm"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/banking"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
"github.com/thrasher-corp/gocryptotrader/utils"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
@@ -637,6 +641,9 @@ func (s *RPCServer) GetPortfolioSummary(ctx context.Context, r *gctrpc.GetPortfo
|
||||
// AddPortfolioAddress adds an address to the portfolio manager
|
||||
func (s *RPCServer) AddPortfolioAddress(ctx context.Context, r *gctrpc.AddPortfolioAddressRequest) (*gctrpc.AddPortfolioAddressResponse, error) {
|
||||
err := Bot.Portfolio.AddAddress(r.Address, r.Description, currency.NewCode(r.CoinType), r.Balance)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &gctrpc.AddPortfolioAddressResponse{}, err
|
||||
}
|
||||
|
||||
@@ -924,13 +931,172 @@ func (s *RPCServer) GetCryptocurrencyDepositAddress(ctx context.Context, r *gctr
|
||||
|
||||
// WithdrawCryptocurrencyFunds withdraws cryptocurrency funds specified by
|
||||
// exchange
|
||||
func (s *RPCServer) WithdrawCryptocurrencyFunds(ctx context.Context, r *gctrpc.WithdrawCurrencyRequest) (*gctrpc.WithdrawResponse, error) {
|
||||
return &gctrpc.WithdrawResponse{}, common.ErrNotYetImplemented
|
||||
func (s *RPCServer) WithdrawCryptocurrencyFunds(ctx context.Context, r *gctrpc.WithdrawCryptoRequest) (*gctrpc.WithdrawResponse, error) {
|
||||
exch := GetExchangeByName(r.Exchange)
|
||||
if exch == nil {
|
||||
return nil, errors.New("exchange is not loaded/doesn't exist")
|
||||
}
|
||||
|
||||
request := &withdraw.Request{
|
||||
Amount: r.Amount,
|
||||
Currency: currency.NewCode(strings.ToUpper(r.Currency)),
|
||||
Type: withdraw.Crypto,
|
||||
Description: r.Description,
|
||||
Crypto: &withdraw.CryptoRequest{
|
||||
Address: r.Address,
|
||||
AddressTag: r.AddressTag,
|
||||
FeeAmount: r.Fee,
|
||||
},
|
||||
}
|
||||
|
||||
resp, err := SubmitWithdrawal(r.Exchange, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gctrpc.WithdrawResponse{
|
||||
Id: resp.ID.String(),
|
||||
Status: resp.Exchange.Status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithdrawFiatFunds withdraws fiat funds specified by exchange
|
||||
func (s *RPCServer) WithdrawFiatFunds(ctx context.Context, r *gctrpc.WithdrawCurrencyRequest) (*gctrpc.WithdrawResponse, error) {
|
||||
return &gctrpc.WithdrawResponse{}, common.ErrNotYetImplemented
|
||||
func (s *RPCServer) WithdrawFiatFunds(ctx context.Context, r *gctrpc.WithdrawFiatRequest) (*gctrpc.WithdrawResponse, error) {
|
||||
exch := GetExchangeByName(r.Exchange)
|
||||
if exch == nil {
|
||||
return nil, errors.New("exchange is not loaded/doesn't exist")
|
||||
}
|
||||
|
||||
var bankAccount *banking.Account
|
||||
|
||||
bankAccount, err := banking.GetBankAccountByID(r.BankAccountId)
|
||||
if err != nil {
|
||||
bankAccount, err = exch.GetBase().GetExchangeBankAccounts(r.BankAccountId, r.Currency)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
request := &withdraw.Request{
|
||||
Amount: r.Amount,
|
||||
Currency: currency.NewCode(strings.ToUpper(r.Currency)),
|
||||
Type: withdraw.Fiat,
|
||||
Description: r.Description,
|
||||
Fiat: &withdraw.FiatRequest{
|
||||
Bank: bankAccount,
|
||||
},
|
||||
}
|
||||
resp, err := SubmitWithdrawal(r.Exchange, request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &gctrpc.WithdrawResponse{
|
||||
Id: resp.ID.String(),
|
||||
Status: resp.Exchange.Status,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// WithdrawalEventByID returns previous withdrawal request details
|
||||
func (s *RPCServer) WithdrawalEventByID(ctx context.Context, r *gctrpc.WithdrawalEventByIDRequest) (*gctrpc.WithdrawalEventByIDResponse, error) {
|
||||
if !Bot.Config.Database.Enabled {
|
||||
return nil, database.ErrDatabaseSupportDisabled
|
||||
}
|
||||
v, err := WithdrawalEventByID(r.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &gctrpc.WithdrawalEventByIDResponse{
|
||||
Event: &gctrpc.WithdrawalEventResponse{
|
||||
Id: v.ID.String(),
|
||||
Exchange: &gctrpc.WithdrawlExchangeEvent{
|
||||
Name: v.Exchange.Name,
|
||||
Id: v.Exchange.Name,
|
||||
Status: v.Exchange.Status,
|
||||
},
|
||||
Request: &gctrpc.WithdrawalRequestEvent{
|
||||
Currency: v.RequestDetails.Currency.String(),
|
||||
Description: v.RequestDetails.Description,
|
||||
Amount: v.RequestDetails.Amount,
|
||||
Type: int32(v.RequestDetails.Type),
|
||||
},
|
||||
},
|
||||
}
|
||||
createdAtPtype, err := ptypes.TimestampProto(v.CreatedAt)
|
||||
if err != nil {
|
||||
log.Errorf(log.Global, "failed to convert time: %v", err)
|
||||
}
|
||||
resp.Event.CreatedAt = createdAtPtype
|
||||
|
||||
updatedAtPtype, err := ptypes.TimestampProto(v.UpdatedAt)
|
||||
if err != nil {
|
||||
log.Errorf(log.Global, "failed to convert time: %v", err)
|
||||
}
|
||||
resp.Event.UpdatedAt = updatedAtPtype
|
||||
|
||||
if v.RequestDetails.Type == withdraw.Crypto {
|
||||
resp.Event.Request.Crypto = new(gctrpc.CryptoWithdrawalEvent)
|
||||
resp.Event.Request.Crypto = &gctrpc.CryptoWithdrawalEvent{
|
||||
Address: v.RequestDetails.Crypto.Address,
|
||||
AddressTag: v.RequestDetails.Crypto.AddressTag,
|
||||
Fee: v.RequestDetails.Crypto.FeeAmount,
|
||||
}
|
||||
} else if v.RequestDetails.Type == withdraw.Fiat {
|
||||
if v.RequestDetails.Fiat != nil {
|
||||
resp.Event.Request.Fiat = new(gctrpc.FiatWithdrawalEvent)
|
||||
resp.Event.Request.Fiat = &gctrpc.FiatWithdrawalEvent{
|
||||
BankName: v.RequestDetails.Fiat.Bank.BankName,
|
||||
AccountName: v.RequestDetails.Fiat.Bank.AccountName,
|
||||
AccountNumber: v.RequestDetails.Fiat.Bank.AccountNumber,
|
||||
Bsb: v.RequestDetails.Fiat.Bank.BSBNumber,
|
||||
Swift: v.RequestDetails.Fiat.Bank.SWIFTCode,
|
||||
Iban: v.RequestDetails.Fiat.Bank.IBAN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// WithdrawalEventsByExchange returns previous withdrawal request details by exchange
|
||||
func (s *RPCServer) WithdrawalEventsByExchange(ctx context.Context, r *gctrpc.WithdrawalEventsByExchangeRequest) (*gctrpc.WithdrawalEventsByExchangeResponse, error) {
|
||||
if !Bot.Config.Database.Enabled {
|
||||
return nil, database.ErrDatabaseSupportDisabled
|
||||
}
|
||||
if r.Id == "" {
|
||||
ret, err := WithdrawalEventByExchange(r.Exchange, int(r.Limit))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseMultipleEvents(ret), nil
|
||||
}
|
||||
|
||||
ret, err := WithdrawalEventByExchangeID(r.Exchange, r.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parseSingleEvents(ret), nil
|
||||
}
|
||||
|
||||
// WithdrawalEventsByDate returns previous withdrawal request details by exchange
|
||||
func (s *RPCServer) WithdrawalEventsByDate(ctx context.Context, r *gctrpc.WithdrawalEventsByDateRequest) (*gctrpc.WithdrawalEventsByExchangeResponse, error) {
|
||||
UTCStartTime, err := time.Parse(audit.TableTimeFormat, r.Start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
UTCSEndTime, err := time.Parse(audit.TableTimeFormat, r.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret, err := WithdrawEventByDate(r.Exchange, UTCStartTime, UTCSEndTime, int(r.Limit))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseMultipleEvents(ret), nil
|
||||
}
|
||||
|
||||
// GetLoggerDetails returns a loggers details
|
||||
|
||||
222
engine/withdraw.go
Normal file
222
engine/withdraw.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
withdrawDataStore "github.com/thrasher-corp/gocryptotrader/database/repository/withdraw"
|
||||
"github.com/thrasher-corp/gocryptotrader/gctrpc"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
|
||||
const (
|
||||
// ErrWithdrawRequestNotFound message to display when no record is found
|
||||
ErrWithdrawRequestNotFound = "%v not found"
|
||||
// ErrRequestCannotbeNil message to display when request is nil
|
||||
ErrRequestCannotbeNil = "request cannot be nil"
|
||||
// StatusError const for for "error" string
|
||||
StatusError = "error"
|
||||
)
|
||||
|
||||
// SubmitWithdrawal preforms validation and submits a new withdraw request to exchange
|
||||
func SubmitWithdrawal(exchName string, req *withdraw.Request) (*withdraw.Response, error) {
|
||||
if req == nil {
|
||||
return nil, errors.New(ErrRequestCannotbeNil)
|
||||
}
|
||||
|
||||
var err error
|
||||
var ret *withdraw.ExchangeResponse
|
||||
if req.Exchange == "" {
|
||||
req.Exchange = exchName
|
||||
}
|
||||
|
||||
err = withdraw.Validate(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exch := GetExchangeByName(exchName)
|
||||
if exch == nil {
|
||||
return nil, ErrExchangeNotFound
|
||||
}
|
||||
|
||||
resp := &withdraw.Response{
|
||||
Exchange: &withdraw.ExchangeResponse{
|
||||
Name: exchName,
|
||||
},
|
||||
RequestDetails: req,
|
||||
}
|
||||
|
||||
if Bot.Settings.EnableDryRun {
|
||||
log.Warnln(log.Global, "Dry run enabled, no withdrawal request will be submitted or have an event created")
|
||||
resp.ID = withdraw.DryRunID
|
||||
resp.Exchange.Status = "dryrun"
|
||||
resp.Exchange.ID = withdraw.DryRunID.String()
|
||||
} else {
|
||||
if req.Type == withdraw.Fiat {
|
||||
ret, err = exch.WithdrawFiatFunds(req)
|
||||
if err != nil {
|
||||
resp.Exchange.ID = StatusError
|
||||
resp.Exchange.Status = err.Error()
|
||||
} else {
|
||||
resp.Exchange.Status = ret.Status
|
||||
resp.Exchange.ID = ret.ID
|
||||
}
|
||||
} else if req.Type == withdraw.Crypto {
|
||||
ret, err = exch.WithdrawCryptocurrencyFunds(req)
|
||||
if err != nil {
|
||||
resp.Exchange.ID = StatusError
|
||||
resp.Exchange.Status = err.Error()
|
||||
} else {
|
||||
resp.Exchange.Status = ret.Status
|
||||
resp.Exchange.ID = ret.ID
|
||||
}
|
||||
}
|
||||
withdrawDataStore.Event(resp)
|
||||
}
|
||||
if err == nil {
|
||||
withdraw.Cache.Add(resp.ID, resp)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// WithdrawalEventByID returns a withdrawal request by ID
|
||||
func WithdrawalEventByID(id string) (*withdraw.Response, error) {
|
||||
v := withdraw.Cache.Get(id)
|
||||
if v != nil {
|
||||
return v.(*withdraw.Response), nil
|
||||
}
|
||||
|
||||
l, err := withdrawDataStore.GetEventByUUID(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(ErrWithdrawRequestNotFound, id)
|
||||
}
|
||||
withdraw.Cache.Add(id, l)
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// WithdrawalEventByExchange returns a withdrawal request by ID
|
||||
func WithdrawalEventByExchange(exchange string, limit int) ([]*withdraw.Response, error) {
|
||||
return withdrawDataStore.GetEventsByExchange(exchange, limit)
|
||||
}
|
||||
|
||||
// WithdrawEventByDate returns a withdrawal request by ID
|
||||
func WithdrawEventByDate(exchange string, start, end time.Time, limit int) ([]*withdraw.Response, error) {
|
||||
return withdrawDataStore.GetEventsByDate(exchange, start, end, limit)
|
||||
}
|
||||
|
||||
// WithdrawalEventByExchangeID returns a withdrawal request by Exchange ID
|
||||
func WithdrawalEventByExchangeID(exchange, id string) (*withdraw.Response, error) {
|
||||
return withdrawDataStore.GetEventByExchangeID(exchange, id)
|
||||
}
|
||||
|
||||
func parseMultipleEvents(ret []*withdraw.Response) *gctrpc.WithdrawalEventsByExchangeResponse {
|
||||
v := &gctrpc.WithdrawalEventsByExchangeResponse{}
|
||||
for x := range ret {
|
||||
tempEvent := &gctrpc.WithdrawalEventResponse{
|
||||
Id: ret[x].ID.String(),
|
||||
Exchange: &gctrpc.WithdrawlExchangeEvent{
|
||||
Name: ret[x].Exchange.Name,
|
||||
Id: ret[x].Exchange.Name,
|
||||
Status: ret[x].Exchange.Status,
|
||||
},
|
||||
Request: &gctrpc.WithdrawalRequestEvent{
|
||||
Currency: ret[x].RequestDetails.Currency.String(),
|
||||
Description: ret[x].RequestDetails.Description,
|
||||
Amount: ret[x].RequestDetails.Amount,
|
||||
Type: int32(ret[x].RequestDetails.Type),
|
||||
},
|
||||
}
|
||||
|
||||
createdAtPtype, err := ptypes.TimestampProto(ret[x].CreatedAt)
|
||||
if err != nil {
|
||||
log.Errorf(log.Global, "failed to convert time: %v", err)
|
||||
}
|
||||
tempEvent.CreatedAt = createdAtPtype
|
||||
|
||||
updatedAtPtype, err := ptypes.TimestampProto(ret[x].UpdatedAt)
|
||||
if err != nil {
|
||||
log.Errorf(log.Global, "failed to convert time: %v", err)
|
||||
}
|
||||
tempEvent.UpdatedAt = updatedAtPtype
|
||||
|
||||
if ret[x].RequestDetails.Type == withdraw.Crypto {
|
||||
tempEvent.Request.Crypto = new(gctrpc.CryptoWithdrawalEvent)
|
||||
tempEvent.Request.Crypto = &gctrpc.CryptoWithdrawalEvent{
|
||||
Address: ret[x].RequestDetails.Crypto.Address,
|
||||
AddressTag: ret[x].RequestDetails.Crypto.AddressTag,
|
||||
Fee: ret[x].RequestDetails.Crypto.FeeAmount,
|
||||
}
|
||||
} else if ret[x].RequestDetails.Type == withdraw.Fiat {
|
||||
if ret[x].RequestDetails.Fiat != nil {
|
||||
tempEvent.Request.Fiat = new(gctrpc.FiatWithdrawalEvent)
|
||||
tempEvent.Request.Fiat = &gctrpc.FiatWithdrawalEvent{
|
||||
BankName: ret[x].RequestDetails.Fiat.Bank.BankName,
|
||||
AccountName: ret[x].RequestDetails.Fiat.Bank.AccountName,
|
||||
AccountNumber: ret[x].RequestDetails.Fiat.Bank.AccountNumber,
|
||||
Bsb: ret[x].RequestDetails.Fiat.Bank.BSBNumber,
|
||||
Swift: ret[x].RequestDetails.Fiat.Bank.SWIFTCode,
|
||||
Iban: ret[x].RequestDetails.Fiat.Bank.IBAN,
|
||||
}
|
||||
}
|
||||
}
|
||||
v.Event = append(v.Event, tempEvent)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func parseSingleEvents(ret *withdraw.Response) *gctrpc.WithdrawalEventsByExchangeResponse {
|
||||
tempEvent := &gctrpc.WithdrawalEventResponse{
|
||||
Id: ret.ID.String(),
|
||||
Exchange: &gctrpc.WithdrawlExchangeEvent{
|
||||
Name: ret.Exchange.Name,
|
||||
Id: ret.Exchange.Name,
|
||||
Status: ret.Exchange.Status,
|
||||
},
|
||||
Request: &gctrpc.WithdrawalRequestEvent{
|
||||
Currency: ret.RequestDetails.Currency.String(),
|
||||
Description: ret.RequestDetails.Description,
|
||||
Amount: ret.RequestDetails.Amount,
|
||||
Type: int32(ret.RequestDetails.Type),
|
||||
},
|
||||
}
|
||||
createdAtPtype, err := ptypes.TimestampProto(ret.CreatedAt)
|
||||
if err != nil {
|
||||
log.Errorf(log.Global, "failed to convert time: %v", err)
|
||||
}
|
||||
tempEvent.CreatedAt = createdAtPtype
|
||||
|
||||
updatedAtPtype, err := ptypes.TimestampProto(ret.UpdatedAt)
|
||||
if err != nil {
|
||||
log.Errorf(log.Global, "failed to convert time: %v", err)
|
||||
}
|
||||
tempEvent.UpdatedAt = updatedAtPtype
|
||||
|
||||
if ret.RequestDetails.Type == withdraw.Crypto {
|
||||
tempEvent.Request.Crypto = new(gctrpc.CryptoWithdrawalEvent)
|
||||
tempEvent.Request.Crypto = &gctrpc.CryptoWithdrawalEvent{
|
||||
Address: ret.RequestDetails.Crypto.Address,
|
||||
AddressTag: ret.RequestDetails.Crypto.AddressTag,
|
||||
Fee: ret.RequestDetails.Crypto.FeeAmount,
|
||||
}
|
||||
} else if ret.RequestDetails.Type == withdraw.Fiat {
|
||||
if ret.RequestDetails.Fiat != nil {
|
||||
tempEvent.Request.Fiat = new(gctrpc.FiatWithdrawalEvent)
|
||||
tempEvent.Request.Fiat = &gctrpc.FiatWithdrawalEvent{
|
||||
BankName: ret.RequestDetails.Fiat.Bank.BankName,
|
||||
AccountName: ret.RequestDetails.Fiat.Bank.AccountName,
|
||||
AccountNumber: ret.RequestDetails.Fiat.Bank.AccountNumber,
|
||||
Bsb: ret.RequestDetails.Fiat.Bank.BSBNumber,
|
||||
Swift: ret.RequestDetails.Fiat.Bank.SWIFTCode,
|
||||
Iban: ret.RequestDetails.Fiat.Bank.IBAN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &gctrpc.WithdrawalEventsByExchangeResponse{
|
||||
Event: []*gctrpc.WithdrawalEventResponse{tempEvent},
|
||||
}
|
||||
}
|
||||
191
engine/withdraw_test.go
Normal file
191
engine/withdraw_test.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/banking"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
|
||||
const (
|
||||
exchangeName = "BTC Markets"
|
||||
bankAccountID = "test-bank-01"
|
||||
)
|
||||
|
||||
var (
|
||||
settings = Settings{
|
||||
ConfigFile: filepath.Join("..", "testdata", "configtest.json"),
|
||||
EnableDryRun: true,
|
||||
DataDir: filepath.Join("..", "testdata", "gocryptotrader"),
|
||||
Verbose: false,
|
||||
EnableGRPC: false,
|
||||
EnableDeprecatedRPC: false,
|
||||
EnableWebsocketRPC: false,
|
||||
}
|
||||
)
|
||||
|
||||
func setupEngine() (err error) {
|
||||
Bot, err = NewFromSettings(&settings)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Bot.Start()
|
||||
}
|
||||
|
||||
func cleanup() {
|
||||
err := os.RemoveAll(settings.DataDir)
|
||||
if err != nil {
|
||||
fmt.Printf("Clean up failed to remove file: %v manual removal may be required", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubmitWithdrawal(t *testing.T) {
|
||||
err := setupEngine()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
banking.Accounts = append(banking.Accounts,
|
||||
banking.Account{
|
||||
Enabled: true,
|
||||
ID: "test-bank-01",
|
||||
BankName: "Test Bank",
|
||||
BankAddress: "42 Bank Street",
|
||||
BankPostalCode: "13337",
|
||||
BankPostalCity: "Satoshiville",
|
||||
BankCountry: "Japan",
|
||||
AccountName: "Satoshi Nakamoto",
|
||||
AccountNumber: "0234",
|
||||
BSBNumber: "123456",
|
||||
SWIFTCode: "91272837",
|
||||
IBAN: "98218738671897",
|
||||
SupportedCurrencies: "AUD,USD",
|
||||
SupportedExchanges: exchangeName,
|
||||
},
|
||||
)
|
||||
|
||||
bank, err := banking.GetBankAccountByID(bankAccountID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
req := &withdraw.Request{
|
||||
Exchange: exchangeName,
|
||||
Currency: currency.AUD,
|
||||
Description: exchangeName,
|
||||
Amount: 1.0,
|
||||
Type: 1,
|
||||
Fiat: &withdraw.FiatRequest{
|
||||
Bank: bank,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = SubmitWithdrawal(exchangeName, req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = SubmitWithdrawal(exchangeName, nil)
|
||||
if err != nil {
|
||||
if err.Error() != withdraw.ErrRequestCannotBeNil.Error() {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
cleanup()
|
||||
}
|
||||
|
||||
func TestWithdrawEventByID(t *testing.T) {
|
||||
tempResp := &withdraw.Response{
|
||||
ID: withdraw.DryRunID,
|
||||
}
|
||||
_, err := WithdrawalEventByID(withdraw.DryRunID.String())
|
||||
if err != nil {
|
||||
if err.Error() != fmt.Errorf(ErrWithdrawRequestNotFound, withdraw.DryRunID.String()).Error() {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
withdraw.Cache.Add(withdraw.DryRunID.String(), tempResp)
|
||||
v, err := WithdrawalEventByID(withdraw.DryRunID.String())
|
||||
if err != nil {
|
||||
if err != fmt.Errorf(ErrWithdrawRequestNotFound, withdraw.DryRunID.String()) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if v == nil {
|
||||
t.Fatal("expected WithdrawalEventByID() to return data from cache")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawalEventByExchange(t *testing.T) {
|
||||
_, err := WithdrawalEventByExchange(exchangeName, 1)
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawEventByDate(t *testing.T) {
|
||||
_, err := WithdrawEventByDate(exchangeName, time.Now(), time.Now(), 1)
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithdrawalEventByExchangeID(t *testing.T) {
|
||||
_, err := WithdrawalEventByExchangeID(exchangeName, exchangeName)
|
||||
if err == nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseEvents(t *testing.T) {
|
||||
var testData []*withdraw.Response
|
||||
for x := 0; x < 5; x++ {
|
||||
test := fmt.Sprintf("test-%v", x)
|
||||
resp := &withdraw.Response{
|
||||
ID: withdraw.DryRunID,
|
||||
Exchange: &withdraw.ExchangeResponse{
|
||||
Name: test,
|
||||
ID: test,
|
||||
Status: test,
|
||||
},
|
||||
RequestDetails: &withdraw.Request{
|
||||
Exchange: test,
|
||||
Description: test,
|
||||
Amount: 1.0,
|
||||
},
|
||||
}
|
||||
if x%2 == 0 {
|
||||
resp.RequestDetails.Currency = currency.AUD
|
||||
resp.RequestDetails.Type = 1
|
||||
resp.RequestDetails.Fiat = new(withdraw.FiatRequest)
|
||||
resp.RequestDetails.Fiat.Bank = new(banking.Account)
|
||||
} else {
|
||||
resp.RequestDetails.Currency = currency.BTC
|
||||
resp.RequestDetails.Type = 0
|
||||
resp.RequestDetails.Crypto = new(withdraw.CryptoRequest)
|
||||
resp.RequestDetails.Crypto.Address = test
|
||||
resp.RequestDetails.Crypto.FeeAmount = 0
|
||||
resp.RequestDetails.Crypto.AddressTag = test
|
||||
}
|
||||
testData = append(testData, resp)
|
||||
}
|
||||
v := parseMultipleEvents(testData)
|
||||
if reflect.TypeOf(v).String() != "*gctrpc.WithdrawalEventsByExchangeResponse" {
|
||||
t.Fatal("expected type to be *gctrpc.WithdrawalEventsByExchangeResponse")
|
||||
}
|
||||
|
||||
v = parseSingleEvents(testData[0])
|
||||
if reflect.TypeOf(v).String() != "*gctrpc.WithdrawalEventsByExchangeResponse" {
|
||||
t.Fatal("expected type to be *gctrpc.WithdrawalEventsByExchangeResponse")
|
||||
}
|
||||
|
||||
v = parseSingleEvents(testData[1])
|
||||
if v.Event[0].Request.Type != 0 {
|
||||
t.Fatal("Expected second entry in slice to return a Request.Type of Crypto")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user