mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-23 15:10:15 +00:00
* update acccount ratelimits and added missing endpoints * completed mapping missing trade accoung REST endpoints and Rate Limit * added orderbook trading missing REST endpoints * Added few missing endpoints and unit tests * Completed grid trading and signal bot trading with unit tests * Added Recurring Buy endpoints and corresponding unit tests * Added copy trading endpoints and unit tests * added newly added block trading and spread endpoints * completed mapping spread endpoints * Added new endpoints and unit tests * Added round 1: Okx types and converts update. * Update endpoints handling and types update * Removed constants, updated unit tests, and updated endpoint methods * Slight endpoint and unit test update * Added spread and other websocket endpoints and update * completed Spread WS Orderbook handler * Added missing spread channels and handlers * Adding Bussinss websocket and missing subscriptions, update unit tests, and endpoints * Added spread endpoints to wrapper and unit tests update * Added missing websocket subscriptions and copy trading endpoints * Added missing endpoints and re-organize business websocket handlers * Docs update * Endpoints code updates * types, unit test and endpoints update * Minor unit tests update * spelling fix * fix unit test issues * Updating unit tests error handling * Updating unit tests error handling * Unit tests assertion handling update * Unit tests update * Resolve linter issues * linter issues fix * Orderbook unit test error fix * Minor fixes * Change on test handling and types * Updating unit tests and cleanups * Fix unit test issues * Add ratelimit test and update unit tests and methods * Update method parameters and error declarations * Added lending endpoints, unit tests, and update endpoint methods and error declarations * Update ratelimiters, add missing lending and trading endpoints and unit tests * Update websocket authentication and subscription handling * Minor update to unit test and types * Types, error handling, and other minor updates * Update unit tests and endpoint functions * error declarations update and unit tests * Overall update on unit endpoint, unit tests, and types * Adding review fixes * Update on endpoints, order types, and unit tests * Update unit tests and endpoint functions * Update on endpoint and update missing parameters * Implemented and tested newly added trading endpoints * endpoints update and unit tests * Update missing endpoints and update overall code * added and testing funding and fiat related endpoints * Update on convert and fiat related endpoints * linter fix, types, endpoints, and unit tests update * linter issues fix * revert changes on tempos * Fix Panic and update websocket auth calls handling * config linter issue fix * Fix panic issues and update unit tests * Fix race condition, TestAllExchangeWrappers unit test issues * Fix withdrawal manager test issue * Rename ClosePositionForContractrID --> ClosePositionForContractID * Rename ClosePositionForContractrID --> ClosePositionForContractID * Fix error * endpoints update and fix unit test issues * added unit tests, updated comments, and code sections * revert change in defaultSubscriptions * few types comments update * Minor changes * remove lending endpoints * rm mistakenly added code * fix unit test * minor unit test fix * Adding offline error tests, update endpoints function, config update * Update unit test coverage for offline error handliing * Updating wrapper algo order coverage, endpoint calls, and unit tests * Updating wrapper trade functions to accomodate algo orders * update wrapper unit tests * Fix wrapper order functions offline errors handling * Tested and updated wrapper order functions * Address review comments * update order unit tests, and okx endpoint functions * finalize affected order, endpoint, and margin endpoints * Slight change on margin unit test * fix margin unit test issues * Minor change on unit test * updates on contract settlement and future contract wrapper function * add test coverage for contract functions and minor fix on wrapper * Overall update and unit testing * codespell, unit tests, type declaration and naming, and code-structure updates * margin types value and validation function fix * Update tests and helper funcs * Improve test coverage * helper functions and unit tests update * Fix margin unit test * Minor review updates * minor fix on if statement * Update helper functions * error handling and functions naming update * update comment * minor error return fixes * minor unit test fix * Minor fix on spread websocket orders handling * codespell fix * skip orderbook depth with incomplete price * skip orderbook depth with incomplete price
426 lines
13 KiB
Go
426 lines
13 KiB
Go
package withdraw
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"time"
|
|
|
|
"github.com/gofrs/uuid"
|
|
"github.com/thrasher-corp/gocryptotrader/common"
|
|
"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"
|
|
)
|
|
|
|
// 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.Errorln(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.Errorln(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.Errorln(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.Errorln(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 {
|
|
x := len(columns)
|
|
if limit > 0 {
|
|
x++
|
|
}
|
|
queries := make([]qm.QueryMod, 0, x)
|
|
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, common.ErrNoResults
|
|
}
|
|
return resp, nil
|
|
}
|