2024-04-11 13:35:17 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"dero-swap/cfg"
|
2024-04-20 21:21:02 +01:00
|
|
|
"dero-swap/clients"
|
2024-04-11 13:35:17 +01:00
|
|
|
"dero-swap/coin"
|
|
|
|
"dero-swap/dero"
|
|
|
|
"dero-swap/monero"
|
|
|
|
"encoding/json"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func GetMarket(pair string, provider int) (prices Swap_Price) {
|
|
|
|
|
|
|
|
resp, err := http.Get(pair)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Market: HTTP Get: %v\n", err)
|
|
|
|
return Swap_Price{}
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Market: Read Body: %v\n", err)
|
|
|
|
return Swap_Price{}
|
|
|
|
}
|
|
|
|
|
|
|
|
var market Price_Provider
|
|
|
|
|
|
|
|
if err := json.Unmarshal(body, &market); err != nil {
|
|
|
|
log.Printf("Market: Cannot unmarshal data: %v\n", err)
|
|
|
|
return Swap_Price{}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch provider {
|
|
|
|
case TO:
|
|
|
|
prices.Ask, err = strconv.ParseFloat(market.Ask, 64)
|
|
|
|
prices.Bid, err = strconv.ParseFloat(market.Bid, 64)
|
|
|
|
prices.Median, err = strconv.ParseFloat(market.Price, 64)
|
|
|
|
case XEGGEX:
|
|
|
|
prices.Ask, err = strconv.ParseFloat(market.BestAsk, 64)
|
|
|
|
prices.Bid, err = strconv.ParseFloat(market.BestBid, 64)
|
|
|
|
prices.Median, err = strconv.ParseFloat(market.LastPrice, 64)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Market: Cannot convert string: %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get Pair values
|
|
|
|
// TODO: configurable fees; clean-up
|
|
|
|
func GetPrice(pair string) (bid float64, ask float64) {
|
|
|
|
|
|
|
|
var base, base_usd Swap_Price
|
|
|
|
var atomicUnits uint = 8
|
|
|
|
var simple bool
|
|
|
|
|
|
|
|
switch pair {
|
|
|
|
case coin.BTCDERO, coin.DEROBTC:
|
|
|
|
simple = true
|
|
|
|
for i := range DERO_BTC {
|
|
|
|
if base = GetMarket(DERO_BTC[i], i); base.Ask > 0 && base.Bid > 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case coin.LTCDERO, coin.DEROLTC:
|
|
|
|
for i := range LTC_USDT {
|
|
|
|
if base = GetMarket(LTC_USDT[i], i); base.Ask > 0 && base.Bid > 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case coin.XMRDERO, coin.DEROXMR:
|
|
|
|
atomicUnits = 12
|
|
|
|
for i := range XMR_USDT {
|
|
|
|
if base = GetMarket(XMR_USDT[i], i); base.Ask > 0 && base.Bid > 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case coin.ARRRDERO, coin.DEROARRR:
|
|
|
|
base = GetMarket(ARRR_USDT, TO)
|
|
|
|
}
|
|
|
|
|
|
|
|
if base.Ask == 0 || base.Bid == 0 {
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
|
|
|
|
if simple {
|
|
|
|
bid = base.Bid - (base.Bid * cfg.SwapFees.Swap.Bid / 100)
|
|
|
|
bid = coin.RoundFloat(bid, atomicUnits)
|
|
|
|
ask = base.Ask + (base.Ask * cfg.SwapFees.Swap.Ask / 100)
|
|
|
|
ask = coin.RoundFloat(ask, atomicUnits)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range DERO_USDT {
|
|
|
|
if base_usd = GetMarket(DERO_USDT[i], i); base_usd.Ask > 0 && base_usd.Bid > 0 {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if base_usd.Ask == 0 || base_usd.Bid == 0 {
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
|
|
|
|
bid = base_usd.Bid - (base_usd.Bid * cfg.SwapFees.Swap.Bid / 100)
|
|
|
|
ask = base_usd.Ask + (base_usd.Ask * cfg.SwapFees.Swap.Ask / 100)
|
|
|
|
|
|
|
|
bid = coin.RoundFloat(bid/base.Bid, atomicUnits)
|
|
|
|
ask = coin.RoundFloat(ask/base.Ask, atomicUnits)
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: simplify
|
|
|
|
func UpdateMarkets() {
|
|
|
|
|
|
|
|
var btc, ltc, xmr, arrr float64
|
|
|
|
var derobtc, deroltc, deroxmr, deroarrr float64
|
|
|
|
|
|
|
|
for p := range coin.SimplePairs {
|
|
|
|
switch p {
|
|
|
|
case coin.XMRDERO, coin.DEROXMR:
|
|
|
|
deroxmr, xmr = GetPrice(p)
|
|
|
|
case coin.ARRRDERO, coin.DEROARRR:
|
|
|
|
deroarrr, arrr = GetPrice(p)
|
|
|
|
case coin.LTCDERO, coin.DEROLTC:
|
|
|
|
deroltc, ltc = GetPrice(p)
|
|
|
|
case coin.BTCDERO, coin.DEROBTC:
|
|
|
|
derobtc, btc = GetPrice(p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// sometimes TradeOgre's BID/ASK values are swapped
|
|
|
|
if derobtc > 0 && btc > 0 && derobtc > btc {
|
|
|
|
swap := btc
|
|
|
|
btc = derobtc
|
|
|
|
derobtc = swap
|
|
|
|
}
|
|
|
|
if deroltc > 0 && ltc > 0 && deroltc > ltc {
|
|
|
|
swap := ltc
|
|
|
|
ltc = deroltc
|
|
|
|
deroltc = swap
|
|
|
|
}
|
|
|
|
if deroarrr > 0 && arrr > 0 && deroarrr > arrr {
|
|
|
|
swap := arrr
|
|
|
|
arrr = deroarrr
|
|
|
|
deroarrr = swap
|
|
|
|
}
|
|
|
|
if deroxmr > 0 && xmr > 0 && deroxmr > xmr {
|
|
|
|
swap := xmr
|
|
|
|
xmr = deroxmr
|
|
|
|
deroxmr = swap
|
|
|
|
}
|
|
|
|
|
|
|
|
mk.Lock()
|
|
|
|
defer mk.Unlock()
|
|
|
|
|
|
|
|
// TODO: simplify
|
|
|
|
if btc > 0 {
|
|
|
|
mk.Pairs.BTC = btc
|
|
|
|
mk.Update[coin.BTCDERO] = time.Now().UnixMilli()
|
|
|
|
IsPairAvailable[coin.BTCDERO] = true
|
|
|
|
} else {
|
|
|
|
t := time.UnixMilli(mk.Update[coin.BTCDERO])
|
|
|
|
if time.Since(t) > time.Minute*2 {
|
|
|
|
IsPairAvailable[coin.BTCDERO] = false
|
|
|
|
log.Println("BTC->DERO disabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ltc > 0 {
|
|
|
|
mk.Pairs.LTC = ltc
|
|
|
|
mk.Update[coin.LTCDERO] = time.Now().UnixMilli()
|
|
|
|
IsPairAvailable[coin.LTCDERO] = true
|
|
|
|
} else {
|
|
|
|
t := time.UnixMilli(mk.Update[coin.LTCDERO])
|
|
|
|
if time.Since(t) > time.Minute*2 {
|
|
|
|
IsPairAvailable[coin.LTCDERO] = false
|
|
|
|
log.Println("LTC->DERO disabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if arrr > 0 {
|
|
|
|
mk.Pairs.ARRR = arrr
|
|
|
|
mk.Update[coin.ARRRDERO] = time.Now().UnixMilli()
|
|
|
|
IsPairAvailable[coin.ARRRDERO] = true
|
|
|
|
} else {
|
|
|
|
t := time.UnixMilli(mk.Update[coin.ARRRDERO])
|
|
|
|
if time.Since(t) > time.Minute*2 {
|
|
|
|
IsPairAvailable[coin.ARRRDERO] = false
|
|
|
|
log.Println("ARRR->DERO disabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if xmr > 0 {
|
|
|
|
mk.Pairs.XMR = xmr
|
|
|
|
mk.Update[coin.XMRDERO] = time.Now().UnixMilli()
|
|
|
|
IsPairAvailable[coin.XMRDERO] = true
|
|
|
|
} else {
|
|
|
|
t := time.UnixMilli(mk.Update[coin.XMRDERO])
|
|
|
|
if time.Since(t) > time.Minute*2 {
|
|
|
|
IsPairAvailable[coin.XMRDERO] = false
|
|
|
|
log.Println("XMR->DERO disabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if deroltc > 0 {
|
|
|
|
mk.Pairs.DEROLTC = deroltc
|
|
|
|
mk.Update[coin.DEROLTC] = time.Now().UnixMilli()
|
|
|
|
IsPairAvailable[coin.DEROLTC] = true
|
|
|
|
} else {
|
|
|
|
t := time.UnixMilli(mk.Update[coin.DEROLTC])
|
|
|
|
if time.Since(t) > time.Minute*2 {
|
|
|
|
IsPairAvailable[coin.DEROLTC] = false
|
|
|
|
log.Println("DERO->LTC disabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if derobtc > 0 {
|
|
|
|
mk.Pairs.DEROBTC = derobtc
|
|
|
|
mk.Update[coin.DEROBTC] = time.Now().UnixMilli()
|
|
|
|
IsPairAvailable[coin.DEROBTC] = true
|
|
|
|
} else {
|
|
|
|
t := time.UnixMilli(mk.Update[coin.DEROBTC])
|
|
|
|
if time.Since(t) > time.Minute*2 {
|
|
|
|
IsPairAvailable[coin.DEROBTC] = false
|
|
|
|
log.Println("DERO->BTC disabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if deroarrr > 0 {
|
|
|
|
mk.Pairs.DEROARRR = deroarrr
|
|
|
|
mk.Update[coin.DEROARRR] = time.Now().UnixMilli()
|
|
|
|
IsPairAvailable[coin.DEROARRR] = true
|
|
|
|
} else {
|
|
|
|
t := time.UnixMilli(mk.Update[coin.DEROARRR])
|
|
|
|
if time.Since(t) > time.Minute*2 {
|
|
|
|
IsPairAvailable[coin.DEROARRR] = false
|
|
|
|
log.Println("DERO->ARRR disabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if deroxmr > 0 {
|
|
|
|
mk.Pairs.DEROXMR = deroxmr
|
|
|
|
mk.Update[coin.DEROXMR] = time.Now().UnixMilli()
|
|
|
|
IsPairAvailable[coin.DEROXMR] = true
|
|
|
|
} else {
|
|
|
|
t := time.UnixMilli(mk.Update[coin.DEROXMR])
|
|
|
|
if time.Since(t) > time.Minute*2 {
|
|
|
|
IsPairAvailable[coin.DEROXMR] = false
|
|
|
|
log.Println("DERO->XMR disabled")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var data bytes.Buffer
|
|
|
|
var out WS_Message
|
|
|
|
|
|
|
|
out.Method = "market"
|
|
|
|
out.Result = mk.Pairs
|
|
|
|
encoder := json.NewEncoder(&data)
|
|
|
|
|
|
|
|
err := encoder.Encode(out)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Market: %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
SendWSData(nil, data.Bytes())
|
|
|
|
|
|
|
|
data.Reset()
|
|
|
|
out.Method = "balance"
|
|
|
|
out.Result = UpdatePool()
|
|
|
|
err = encoder.Encode(out)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Balance: %v\n", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
SendWSData(nil, data.Bytes())
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: reduce function calls
|
|
|
|
func UpdatePool() Swap_Balance {
|
|
|
|
|
|
|
|
lock.Lock()
|
|
|
|
defer lock.Unlock()
|
|
|
|
|
|
|
|
var balance Swap_Balance
|
|
|
|
|
|
|
|
balance.Dero = dero.GetBalance()
|
|
|
|
|
|
|
|
for p := range coin.SimplePairs {
|
|
|
|
switch p {
|
|
|
|
case coin.XMRDERO, coin.DEROXMR:
|
|
|
|
balance.XMR = monero.GetBalance()
|
|
|
|
case coin.ARRRDERO, coin.DEROARRR:
|
|
|
|
balance.ARRR = coin.XTCGetBalance(p)
|
|
|
|
case coin.LTCDERO, coin.DEROLTC:
|
|
|
|
balance.LTC = coin.XTCGetBalance(p)
|
|
|
|
case coin.BTCDERO, coin.DEROBTC:
|
|
|
|
balance.BTC = coin.XTCGetBalance(p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-20 21:21:02 +01:00
|
|
|
balance.External = clients.GetExternalBalances()
|
|
|
|
|
2024-04-11 13:35:17 +01:00
|
|
|
return balance
|
|
|
|
}
|