mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-18 15:10:03 +00:00
* 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
427 lines
13 KiB
Go
427 lines
13 KiB
Go
package withdraw
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/gofrs/uuid"
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
"github.com/thrasher-corp/gocryptotrader/database"
|
|
modelPSQL "github.com/thrasher-corp/gocryptotrader/database/models/postgres"
|
|
modelSQLite "github.com/thrasher-corp/gocryptotrader/database/models/sqlite3"
|
|
"github.com/thrasher-corp/gocryptotrader/database/repository"
|
|
exchangeDB "github.com/thrasher-corp/gocryptotrader/database/repository/exchange"
|
|
"github.com/thrasher-corp/gocryptotrader/log"
|
|
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
|
"github.com/thrasher-corp/sqlboiler/boil"
|
|
"github.com/thrasher-corp/sqlboiler/queries/qm"
|
|
)
|
|
|
|
var (
|
|
// ErrNoResults is the error returned if no results are found
|
|
ErrNoResults = errors.New("no results found")
|
|
)
|
|
|
|
// Event stores Withdrawal Response details in database
|
|
func Event(res *withdraw.Response) {
|
|
if database.DB.SQL == nil {
|
|
return
|
|
}
|
|
|
|
ctx := context.Background()
|
|
ctx = boil.SkipTimestamps(ctx)
|
|
|
|
exchangeUUID, err := exchangeDB.UUIDByName(res.Exchange.Name)
|
|
if err != nil {
|
|
log.Error(log.DatabaseMgr, err)
|
|
return
|
|
}
|
|
|
|
res.Exchange.Name = exchangeUUID.String()
|
|
tx, err := database.DB.SQL.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event transaction being failed: %v", err)
|
|
return
|
|
}
|
|
|
|
if repository.GetSQLDialect() == database.DBSQLite3 {
|
|
err = addSQLiteEvent(ctx, tx, res)
|
|
} else {
|
|
err = addPSQLEvent(ctx, tx, res)
|
|
}
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event insert failed: %v", err)
|
|
err = tx.Rollback()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event Transaction rollback failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
err = tx.Commit()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event Transaction commit failed: %v", err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func addPSQLEvent(ctx context.Context, tx *sql.Tx, res *withdraw.Response) (err error) {
|
|
var tempEvent = modelPSQL.WithdrawalHistory{
|
|
ExchangeNameID: res.Exchange.Name,
|
|
ExchangeID: res.Exchange.ID,
|
|
Status: res.Exchange.Status,
|
|
Currency: res.RequestDetails.Currency.String(),
|
|
Amount: res.RequestDetails.Amount,
|
|
WithdrawType: int(res.RequestDetails.Type),
|
|
}
|
|
|
|
if res.RequestDetails.Description != "" {
|
|
tempEvent.Description.SetValid(res.RequestDetails.Description)
|
|
}
|
|
|
|
err = tempEvent.Insert(ctx, tx, boil.Infer())
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event Insert failed: %v", err)
|
|
err = tx.Rollback()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Rollback failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.RequestDetails.Type == withdraw.Fiat {
|
|
fiatEvent := &modelPSQL.WithdrawalFiat{
|
|
BankName: res.RequestDetails.Fiat.Bank.BankName,
|
|
BankAddress: res.RequestDetails.Fiat.Bank.BankAddress,
|
|
BankAccountName: res.RequestDetails.Fiat.Bank.AccountName,
|
|
BankAccountNumber: res.RequestDetails.Fiat.Bank.AccountNumber,
|
|
BSB: res.RequestDetails.Fiat.Bank.BSBNumber,
|
|
SwiftCode: res.RequestDetails.Fiat.Bank.SWIFTCode,
|
|
Iban: res.RequestDetails.Fiat.Bank.IBAN,
|
|
}
|
|
err = tempEvent.SetWithdrawalFiatWithdrawalFiats(ctx, tx, true, fiatEvent)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event Insert failed: %v", err)
|
|
err = tx.Rollback()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Rollback failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
if res.RequestDetails.Type == withdraw.Crypto {
|
|
cryptoEvent := &modelPSQL.WithdrawalCrypto{
|
|
Address: res.RequestDetails.Crypto.Address,
|
|
Fee: res.RequestDetails.Crypto.FeeAmount,
|
|
}
|
|
if res.RequestDetails.Crypto.AddressTag != "" {
|
|
cryptoEvent.AddressTag.SetValid(res.RequestDetails.Crypto.AddressTag)
|
|
}
|
|
err = tempEvent.AddWithdrawalCryptoWithdrawalCryptos(ctx, tx, true, cryptoEvent)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event Insert failed: %v", err)
|
|
err = tx.Rollback()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Rollback failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
realID, _ := uuid.FromString(tempEvent.ID)
|
|
res.ID = realID
|
|
|
|
return nil
|
|
}
|
|
|
|
func addSQLiteEvent(ctx context.Context, tx *sql.Tx, res *withdraw.Response) (err error) {
|
|
newUUID, errUUID := uuid.NewV4()
|
|
if errUUID != nil {
|
|
log.Errorf(log.DatabaseMgr, "Failed to generate UUID: %v", errUUID)
|
|
err = tx.Rollback()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Rollback failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
var tempEvent = modelSQLite.WithdrawalHistory{
|
|
ID: newUUID.String(),
|
|
ExchangeNameID: res.Exchange.Name,
|
|
ExchangeID: res.Exchange.ID,
|
|
Status: res.Exchange.Status,
|
|
Currency: res.RequestDetails.Currency.String(),
|
|
Amount: res.RequestDetails.Amount,
|
|
WithdrawType: int64(res.RequestDetails.Type),
|
|
}
|
|
|
|
if res.RequestDetails.Description != "" {
|
|
tempEvent.Description.SetValid(res.RequestDetails.Description)
|
|
}
|
|
|
|
err = tempEvent.Insert(ctx, tx, boil.Infer())
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event Insert failed: %v", err)
|
|
err = tx.Rollback()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Rollback failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
|
|
if res.RequestDetails.Type == withdraw.Fiat {
|
|
fiatEvent := &modelSQLite.WithdrawalFiat{
|
|
BankName: res.RequestDetails.Fiat.Bank.BankName,
|
|
BankAddress: res.RequestDetails.Fiat.Bank.BankAddress,
|
|
BankAccountName: res.RequestDetails.Fiat.Bank.AccountName,
|
|
BankAccountNumber: res.RequestDetails.Fiat.Bank.AccountNumber,
|
|
BSB: res.RequestDetails.Fiat.Bank.BSBNumber,
|
|
SwiftCode: res.RequestDetails.Fiat.Bank.SWIFTCode,
|
|
Iban: res.RequestDetails.Fiat.Bank.IBAN,
|
|
}
|
|
|
|
err = tempEvent.AddWithdrawalFiats(ctx, tx, true, fiatEvent)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event Insert failed: %v", err)
|
|
err = tx.Rollback()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Rollback failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
if res.RequestDetails.Type == withdraw.Crypto {
|
|
cryptoEvent := &modelSQLite.WithdrawalCrypto{
|
|
Address: res.RequestDetails.Crypto.Address,
|
|
Fee: res.RequestDetails.Crypto.FeeAmount,
|
|
}
|
|
|
|
if res.RequestDetails.Crypto.AddressTag != "" {
|
|
cryptoEvent.AddressTag.SetValid(res.RequestDetails.Crypto.AddressTag)
|
|
}
|
|
|
|
err = tempEvent.AddWithdrawalCryptos(ctx, tx, true, cryptoEvent)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Event Insert failed: %v", err)
|
|
err = tx.Rollback()
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Rollback failed: %v", err)
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
res.ID = newUUID
|
|
return nil
|
|
}
|
|
|
|
// GetEventByUUID return requested withdraw information by ID
|
|
func GetEventByUUID(id string) (*withdraw.Response, error) {
|
|
resp, err := getByColumns(generateWhereQuery([]string{"id"}, []string{id}, 1))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return resp[0], nil
|
|
}
|
|
|
|
// GetEventsByExchange returns all withdrawal requests by exchange
|
|
func GetEventsByExchange(exchange string, limit int) ([]*withdraw.Response, error) {
|
|
exch, err := exchangeDB.UUIDByName(exchange)
|
|
if err != nil {
|
|
log.Error(log.DatabaseMgr, err)
|
|
return nil, err
|
|
}
|
|
return getByColumns(generateWhereQuery([]string{"exchange_name_id"}, []string{exch.String()}, limit))
|
|
}
|
|
|
|
// GetEventByExchangeID return requested withdraw information by Exchange ID
|
|
func GetEventByExchangeID(exchange, id string) (*withdraw.Response, error) {
|
|
exch, err := exchangeDB.UUIDByName(exchange)
|
|
if err != nil {
|
|
log.Error(log.DatabaseMgr, err)
|
|
return nil, err
|
|
}
|
|
resp, err := getByColumns(generateWhereQuery([]string{"exchange_name_id", "exchange_id"}, []string{exch.String(), id}, 1))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return resp[0], err
|
|
}
|
|
|
|
// GetEventsByDate returns requested withdraw information by date range
|
|
func GetEventsByDate(exchange string, start, end time.Time, limit int) ([]*withdraw.Response, error) {
|
|
betweenQuery := generateWhereBetweenQuery("created_at", start, end, limit)
|
|
if exchange == "" {
|
|
return getByColumns(betweenQuery)
|
|
}
|
|
exch, err := exchangeDB.UUIDByName(exchange)
|
|
if err != nil {
|
|
log.Error(log.DatabaseMgr, err)
|
|
return nil, err
|
|
}
|
|
return getByColumns(append(generateWhereQuery([]string{"exchange_name_id"}, []string{exch.String()}, 0), betweenQuery...))
|
|
}
|
|
|
|
func generateWhereQuery(columns, id []string, limit int) []qm.QueryMod {
|
|
var queries []qm.QueryMod
|
|
if limit > 0 {
|
|
queries = append(queries, qm.Limit(limit))
|
|
}
|
|
for x := range columns {
|
|
queries = append(queries, qm.Where(columns[x]+"= ?", id[x]))
|
|
}
|
|
return queries
|
|
}
|
|
|
|
func generateWhereBetweenQuery(column string, start, end interface{}, limit int) []qm.QueryMod {
|
|
return []qm.QueryMod{
|
|
qm.Limit(limit),
|
|
qm.Where(column+" BETWEEN ? AND ?", start, end),
|
|
}
|
|
}
|
|
|
|
func getByColumns(q []qm.QueryMod) ([]*withdraw.Response, error) {
|
|
if database.DB.SQL == nil {
|
|
return nil, database.ErrDatabaseSupportDisabled
|
|
}
|
|
|
|
var resp []*withdraw.Response
|
|
var ctx = context.Background()
|
|
if repository.GetSQLDialect() == database.DBSQLite3 {
|
|
v, err := modelSQLite.WithdrawalHistories(q...).All(ctx, database.DB.SQL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for x := range v {
|
|
var tempResp = &withdraw.Response{}
|
|
var newUUID uuid.UUID
|
|
newUUID, err = uuid.FromString(v[x].ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tempResp.ID = newUUID
|
|
tempResp.Exchange.ID = v[x].ExchangeID
|
|
tempResp.Exchange.Status = v[x].Status
|
|
tempResp.RequestDetails = withdraw.Request{
|
|
Currency: currency.NewCode(v[x].Currency),
|
|
Description: v[x].Description.String,
|
|
Amount: v[x].Amount,
|
|
Type: withdraw.RequestType(v[x].WithdrawType),
|
|
}
|
|
|
|
exchangeName, err := v[x].ExchangeName().One(ctx, database.DB.SQL)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Unable to get exchange name")
|
|
tempUUID, errUUID := uuid.FromString(v[x].ExchangeNameID)
|
|
if errUUID != nil {
|
|
log.Errorf(log.DatabaseMgr, "invalid exchange name UUID for record %v", v[x].ID)
|
|
} else {
|
|
tempResp.Exchange.UUID = tempUUID
|
|
}
|
|
} else {
|
|
tempResp.Exchange.Name = exchangeName.Name
|
|
}
|
|
|
|
createdAtTime, err := time.Parse(time.RFC3339, v[x].CreatedAt)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "record: %v has an incorrect time format ( %v ) - defaulting to empty time: %v", tempResp.ID, v[x].CreatedAt, err)
|
|
tempResp.CreatedAt = time.Time{}
|
|
} else {
|
|
tempResp.CreatedAt = createdAtTime
|
|
}
|
|
|
|
updatedAtTime, err := time.Parse(time.RFC3339, v[x].UpdatedAt)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "record: %v has an incorrect time format ( %v ) - defaulting to empty time: %v", tempResp.ID, v[x].UpdatedAt, err)
|
|
tempResp.UpdatedAt = time.Time{}
|
|
} else {
|
|
tempResp.UpdatedAt = updatedAtTime
|
|
}
|
|
|
|
if withdraw.RequestType(v[x].WithdrawType) == withdraw.Crypto {
|
|
x, err := v[x].WithdrawalCryptos().One(ctx, database.DB.SQL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tempResp.RequestDetails.Crypto.Address = x.Address
|
|
tempResp.RequestDetails.Crypto.AddressTag = x.AddressTag.String
|
|
tempResp.RequestDetails.Crypto.FeeAmount = x.Fee
|
|
} else {
|
|
x, err := v[x].WithdrawalFiats().One(ctx, database.DB.SQL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tempResp.RequestDetails.Fiat.Bank.AccountName = x.BankAccountName
|
|
tempResp.RequestDetails.Fiat.Bank.AccountNumber = x.BankAccountNumber
|
|
tempResp.RequestDetails.Fiat.Bank.IBAN = x.Iban
|
|
tempResp.RequestDetails.Fiat.Bank.SWIFTCode = x.SwiftCode
|
|
tempResp.RequestDetails.Fiat.Bank.BSBNumber = x.BSB
|
|
}
|
|
resp = append(resp, tempResp)
|
|
}
|
|
} else {
|
|
v, err := modelPSQL.WithdrawalHistories(q...).All(ctx, database.DB.SQL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for x := range v {
|
|
var tempResp = &withdraw.Response{}
|
|
newUUID, _ := uuid.FromString(v[x].ID)
|
|
tempResp.ID = newUUID
|
|
tempResp.Exchange.ID = v[x].ExchangeID
|
|
tempResp.Exchange.Status = v[x].Status
|
|
tempResp.RequestDetails = withdraw.Request{
|
|
Currency: currency.NewCode(v[x].Currency),
|
|
Description: v[x].Description.String,
|
|
Amount: v[x].Amount,
|
|
Type: withdraw.RequestType(v[x].WithdrawType),
|
|
}
|
|
tempResp.CreatedAt = v[x].CreatedAt
|
|
tempResp.UpdatedAt = v[x].UpdatedAt
|
|
|
|
exchangeName, err := v[x].ExchangeName().One(ctx, database.DB.SQL)
|
|
if err != nil {
|
|
log.Errorf(log.DatabaseMgr, "Unable to get exchange name")
|
|
tempUUID, errUUID := uuid.FromString(v[x].ExchangeNameID)
|
|
if errUUID != nil {
|
|
log.Errorf(log.DatabaseMgr, "invalid exchange name UUID for record %v", v[x].ID)
|
|
} else {
|
|
tempResp.Exchange.UUID = tempUUID
|
|
}
|
|
} else {
|
|
tempResp.Exchange.Name = exchangeName.Name
|
|
}
|
|
|
|
if withdraw.RequestType(v[x].WithdrawType) == withdraw.Crypto {
|
|
x, err := v[x].WithdrawalCryptoWithdrawalCryptos().One(ctx, database.DB.SQL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tempResp.RequestDetails.Crypto.Address = x.Address
|
|
tempResp.RequestDetails.Crypto.AddressTag = x.AddressTag.String
|
|
tempResp.RequestDetails.Crypto.FeeAmount = x.Fee
|
|
} else if withdraw.RequestType(v[x].WithdrawType) == withdraw.Fiat {
|
|
x, err := v[x].WithdrawalFiatWithdrawalFiats().One(ctx, database.DB.SQL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tempResp.RequestDetails.Fiat.Bank.AccountName = x.BankAccountName
|
|
tempResp.RequestDetails.Fiat.Bank.AccountNumber = x.BankAccountNumber
|
|
tempResp.RequestDetails.Fiat.Bank.IBAN = x.Iban
|
|
tempResp.RequestDetails.Fiat.Bank.SWIFTCode = x.SwiftCode
|
|
tempResp.RequestDetails.Fiat.Bank.BSBNumber = x.BSB
|
|
}
|
|
resp = append(resp, tempResp)
|
|
}
|
|
}
|
|
if len(resp) == 0 {
|
|
return nil, ErrNoResults
|
|
}
|
|
return resp, nil
|
|
}
|