Files
gocryptotrader/engine/withdraw.go
Ryan O'Hara-Reid 4e828a8124 (Exchanges) Introduce validation method and small updates (#565)
* Remove pointer reference

* Fix portfolio withdraw tests

* Add nil protection in validator method to reduce prospective panics and for future outbound checking

* Updated tests

* ch order var to not ref package

* rm comparison

* Add order ID validation check

* Add exchange name validation check

* Add in test details

* fix tests

* fix linter issues

* linter issues strikes again

* linter rabbit hole

* Addr nitterinos

* Add validation variadic interface to define sets of functionality check POC

* didn't want to add an amount other than 0, didn't want to add address to exchange withdraw, didn't want to whitlist, can change if need be

* add coverage

* Add validation method options for exchange wrappers and abstracted validation into its own package

* Add validation code for structs in exchange template generation

* remove extra validation call as this is done in wrapper

* fix niterinos for examplerinos

* Add template to documentation tool and regenerated documentation

* Addr niticles

* Fix tests due to validation update

* Add more validation checks for modify/submit orders

* update tests

* fix more tests

* Add asset type to submit variable in tests and rpc call. Regen funcs.

* Add field to modify struct in tests

* applied field asset to cancel struct across project

* fix woopsy
2020-10-02 13:36:01 +10:00

214 lines
7.0 KiB
Go

package engine
import (
"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 performs validation and submits a new withdraw request to
// exchange
func SubmitWithdrawal(req *withdraw.Request) (*withdraw.Response, error) {
if req == nil {
return nil, withdraw.ErrRequestCannotBeNil
}
exch := Bot.GetExchangeByName(req.Exchange)
if exch == nil {
return nil, ErrExchangeNotFound
}
resp := &withdraw.Response{
Exchange: withdraw.ExchangeResponse{
Name: req.Exchange,
},
RequestDetails: *req,
}
var err error
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 {
var ret *withdraw.ExchangeResponse
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.ID,
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 != (withdraw.FiatRequest{}) {
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 != (withdraw.FiatRequest{}) {
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},
}
}