From 764ad4aa1b527e70b66522c0bed72641ee800f55 Mon Sep 17 00:00:00 2001 From: mmarcel Date: Sat, 20 Apr 2024 22:23:48 +0200 Subject: [PATCH] Swap Client --- README.md | 139 -------------------- cfg/config.go | 39 ++---- cfg/variables.go | 13 +- clients/clients.go | 38 +----- clients/variables.go | 2 +- coin/variables.go | 2 +- config.json | 7 ++ dero/wallet.go | 2 +- fees.json | 12 ++ go.mod | 16 ++- main.go | 10 +- price.go | 224 +++++++++------------------------ price_variables.go | 16 +-- server.go | 293 +++++++++---------------------------------- swap.go | 8 +- swap_manager.go | 6 +- 16 files changed, 186 insertions(+), 641 deletions(-) delete mode 100644 README.md create mode 100644 config.json create mode 100644 fees.json diff --git a/README.md b/README.md deleted file mode 100644 index b0c1e0c..0000000 --- a/README.md +++ /dev/null @@ -1,139 +0,0 @@ -# Dero Swaps - -## Build from source -Clone the repository to your local disk.\ -Run `go mod tidy` and compile with `go build`. - -## Configuration -There are no paramters (yet).\ -Instead, 2 config files are used.\ -Place the following two config files in the program directory. -- **config.json** -``` -{ - "listen" : (string) IP:Port to listen on, - "btc_daemon" : (string) IP:Port of Bitcoin daemon, - "btc_dir" : (string) path to Bitcoin directory, - "ltc_daemon" : (string) IP:Port of Litecoin daemon, - "ltc_dir" : (string) path to Litecoin directory, - "arrr_daemon" : (string) IP:Port of Pirate daemon, - "arrr_dir" : (string) path to Pirate directory, - "monero_wallet" : (string) IP:Port of Monero Wallet, - "dero_daemon" : (string) IP:Port of Dero Daemon, - "dero_wallet" : (string) IP:Port of Dero Wallet, - "pairs" : (string array) enabled pairs -} -``` -Example file: -``` -{ - "listen" : "192.168.177.161:10413", - "btc_daemon" : "localhost:8332", - "btc_dir" : "/mnt/bitcoin", - "ltc_daemon" : "localhost:9332", - "ltc_dir" : "/mnt/litecoin", - "arrr_daemon" : "localhost:45453", - "arrr_dir" : "/mnt/pirate", - "monero_wallet" : "localhost:18090", - "dero_daemon" : "localhost:10102", - "dero_wallet" : "localhost:10103", - "pairs" : ["btc-dero","ltc-dero","arrr-dero","dero-ltc","dero-btc","dero-arrr","xmr-dero","dero-xmr"] -} -``` -- **fees.json** -``` -{ - "withdrawal" : { - "dero-btc" : (float) Bitcoin withdrawal fee, - "dero-ltc" : (float) Litecoin withdrawal fee, - "dero-arrr" : (float) Pirate withdrawal fee, - "dero-xmr" : (float) Monero withdrawal fee - }, - "swap" : { - "bid": (float) in percent, Bid fees - "ask": (float) in percent, Ask fees - } -} -``` -Example file: -``` -{ - "withdrawal" : { - "dero-btc" : 0.00004, - "dero-ltc" : 0.0002, - "dero-arrr" : 0.001, - "dero-xmr" : 0.00006 - }, - "swap" : { - "bid": 0.75, - "ask": 0.75 - } -} -``` - -The mentioned *withdrawal* fees are recommended and can be adjusted later. -### Available Pairs -The following Pairs are supported: -- btc-dero -- ltc-dero -- xmr-dero -- arrr-dero -- dero-btc -- dero-ltc -- dero-xmr -- dero-arrr - -## Usage -The swap service starts a *websocket* server. -Bitcoin, Litecoin and Pirate swaps require a local node. -The easiest way to start is to enable **dero-xmr** and **xmr-dero** swaps. -The Monero wallet can be pointed to a public remote node. - -### Methods -- **balance** (get pool liquidity) -- **market** (get price data) -- **swap** (create a swap request) - ### JSON parameters - There are no parameters required for methods *market* and *balance*\ - - **swap** params - ``` -{ - "pair" : (string) swap pair, - "amount" : (float) swap amount (in Dero), - "dero_address" : (string) Destination wallet address -} -``` -### JSON responses -**balance** response -``` -"result": { - "dero" : (float) Dero pool, - "ltc" : (float) Litecoin pool, - "btc" : (float) Bitcoin pool, - "arrr" : (float) Pirate pool, - "xmr" : (float) Monero Pool -} -``` -**market** response -``` -"result": { - "ltcdero" : (float) LTC price for buying 1 Dero, - "btcdero" : (float) BTC price for buying 1 Dero, - "xmrdero" : (float) XMR price for buying 1 Dero, - "arrrdero" : (float) ARRR price for buying 1 Dero, - "deroltc" : (float) LTC price for selling 1 Dero - "derobtc" : (float) BTC price for selling 1 Dero, - "deroxmr" : (float) XMR price for selling 1 Dero, - "deroarrr" : (float) ARRR price for selling 1 Dero, -} -``` -**swap** response -``` - "id" : (int) Swap ID,, - "wallet" : (string) Deposit wallet - "deposit" : (float) Deposit amount, - "swap" : (float)`Payout value, only used for Dero-* swaps, - "error" : (string) error message, if present, - "request" : (Swap_Request) Swap JSON parameters -``` diff --git a/cfg/config.go b/cfg/config.go index 0f045cc..5517424 100644 --- a/cfg/config.go +++ b/cfg/config.go @@ -1,14 +1,15 @@ package cfg import ( - "dero-swap/coin" - "dero-swap/dero" - "dero-swap/monero" "encoding/base64" "encoding/json" "log" + "net/url" "os" "strings" + "swap-client/coin" + "swap-client/dero" + "swap-client/monero" "github.com/ybbus/jsonrpc/v3" ) @@ -40,11 +41,9 @@ func LoadConfig() { log.Println("Dero Wallet: No RPC authorization specified") } - monero.Monero_Wallet = jsonrpc.NewClient("http://" + Settings.Monero_Wallet + "/json_rpc") + Server_URL = url.URL{Scheme: "wss", Host: Settings.ServerAddress, Path: "/ws"} - coin.XTC_URL[coin.BTCDERO] = "http://" + Settings.BTC_Daemon - coin.XTC_URL[coin.LTCDERO] = "http://" + Settings.LTC_Daemon - coin.XTC_URL[coin.ARRRDERO] = "http://" + Settings.ARRR_Daemon + monero.Monero_Wallet = jsonrpc.NewClient("http://" + Settings.Monero_Wallet + "/json_rpc") // check if pair is "supported" for _, p := range Settings.Pairs { @@ -87,6 +86,11 @@ func LoadFees() { // basic config check func CheckConfig() bool { + if Settings.Nickname == "" { + log.Println("Please specify a nickname") + return false + } + if Settings.Dero_Daemon == "" || Settings.Dero_Wallet == "" { log.Println("Dero Daemon or Dero Wallet is not set") return false @@ -94,27 +98,6 @@ func CheckConfig() bool { for p := range coin.Pairs { switch p { - case coin.BTCDERO, coin.DEROBTC: - if Settings.BTC_Daemon == "" || Settings.BTC_Dir == "" { - log.Printf("%s pair is set, but daemon or directory is not set\n", p) - return false - } else { - coin.BTC_Dir = Settings.BTC_Dir - } - case coin.LTCDERO, coin.DEROLTC: - if Settings.LTC_Daemon == "" || Settings.LTC_Dir == "" { - log.Printf("%s pair is set, but daemon or directory is not set\n", p) - return false - } else { - coin.LTC_Dir = Settings.LTC_Dir - } - case coin.ARRRDERO, coin.DEROARRR: - if Settings.ARRR_Daemon == "" || Settings.ARRR_Dir == "" { - log.Printf("%s pair is set, but daemon or directory is not set\n", p) - return false - } else { - coin.ARRR_Dir = Settings.ARRR_Dir - } case coin.XMRDERO, coin.DEROXMR: if Settings.Monero_Wallet == "" { log.Printf("%s pair is set, but wallet is not set\n", p) diff --git a/cfg/variables.go b/cfg/variables.go index 6f9a7c0..6146967 100644 --- a/cfg/variables.go +++ b/cfg/variables.go @@ -1,13 +1,10 @@ package cfg +import "net/url" + type Config struct { - ListenAddress string `json:"listen"` - BTC_Daemon string `json:"BTC_Daemon"` - BTC_Dir string `json:"BTC_Dir"` - LTC_Daemon string `json:"LTC_Daemon"` - LTC_Dir string `json:"LTC_Dir"` - ARRR_Daemon string `json:"ARRR_Daemon"` - ARRR_Dir string `json:"ARRR_Dir"` + ServerAddress string `json:"server"` + Nickname string `json:"nickname"` Dero_Daemon string `json:"Dero_Daemon"` Dero_Wallet string `json:"dero_wallet"` Dero_Login string `json:"dero_login"` @@ -48,3 +45,5 @@ type ( var Settings Config var SwapFees Fees + +var Server_URL url.URL diff --git a/clients/clients.go b/clients/clients.go index 7f8da4e..d185d8d 100644 --- a/clients/clients.go +++ b/clients/clients.go @@ -1,24 +1,18 @@ package clients import ( - "dero-swap/coin" "log" + "swap-client/coin" "github.com/lesismal/nbio/nbhttp/websocket" ) -type Swap_External struct { - Nickname string `json:"nickname"` - Dero float64 `json:"dero"` - XMR float64 `json:"xmr,omitempty"` -} - -func IsExternalSwapAvailable(user string, pair string, amount float64) (ok bool, client *websocket.Conn) { +func IsExternalSwapAvailable(pair string, amount float64) (ok bool, client *websocket.Conn) { Clients.Range(func(key any, value any) bool { c := value.(ClientInfo) for _, p := range c.PairInfo { - if p.Pair == pair && p.Balance >= amount && user == c.Nickname { + if p.Pair == pair && p.Balance >= amount { ok = true client = key.(*websocket.Conn) return false @@ -29,7 +23,7 @@ func IsExternalSwapAvailable(user string, pair string, amount float64) (ok bool, return } -func PrepareExternalSwap(user string, pair string, amount float64) (bool, *websocket.Conn) { +func PrepareExternalSwap(pair string, amount float64) (bool, *websocket.Conn) { // only XMR swaps if pair != coin.XMRDERO && pair != coin.DEROXMR { @@ -37,7 +31,7 @@ func PrepareExternalSwap(user string, pair string, amount float64) (bool, *webso return false, nil } - ok, conn := IsExternalSwapAvailable(user, pair, amount) + ok, conn := IsExternalSwapAvailable(pair, amount) if !ok { log.Println("No 3rd party swaps available") return false, nil @@ -81,25 +75,3 @@ func (c *SwapState) GetOrigin(conn *websocket.Conn) *websocket.Conn { return c.Result[conn] } - -func GetExternalBalances() (list []Swap_External) { - - var entry Swap_External - - Clients.Range(func(key any, value any) bool { - c := value.(ClientInfo) - entry.Nickname = c.Nickname - for _, p := range c.PairInfo { - switch p.Pair { - case coin.DEROXMR: - entry.XMR = p.Balance - case coin.XMRDERO: - entry.Dero = p.Balance - } - } - list = append(list, entry) - return true - }) - - return -} diff --git a/clients/variables.go b/clients/variables.go index de60550..141e3fb 100644 --- a/clients/variables.go +++ b/clients/variables.go @@ -28,4 +28,4 @@ type SwapState struct { } var Clients sync.Map -var ActiveClients = SwapState{Client: make(map[*websocket.Conn]bool), Result: make(map[*websocket.Conn]*websocket.Conn)} +var ActiveClients = SwapState{Client: make(map[*websocket.Conn]bool)} diff --git a/coin/variables.go b/coin/variables.go index fe94289..4977c56 100644 --- a/coin/variables.go +++ b/coin/variables.go @@ -10,7 +10,7 @@ type ( Pair string `json:"pair"` Amount float64 `json:"amount"` DeroAddr string `json:"dero_address"` - Extern string `json:"extern,omitempty"` + Partner bool `json:"partner,omitempty"` } Swap_Response struct { ID int64 `json:"id"` diff --git a/config.json b/config.json new file mode 100644 index 0000000..26e122b --- /dev/null +++ b/config.json @@ -0,0 +1,7 @@ +{ + "server" : "mg25ot4aggt8dprv.myfritz.net:10413", + "monero_wallet" : "localhost:18090", + "dero_daemon" : "localhost:10102", + "dero_wallet" : "localhost:10103", + "pairs" : ["xmr-dero","dero-xmr"] +} diff --git a/dero/wallet.go b/dero/wallet.go index e44ed1b..9b951fd 100644 --- a/dero/wallet.go +++ b/dero/wallet.go @@ -5,7 +5,7 @@ import ( "log" "time" - "dero-swap/coin" + "swap-client/coin" "github.com/deroproject/derohe/cryptography/crypto" "github.com/deroproject/derohe/rpc" diff --git a/fees.json b/fees.json new file mode 100644 index 0000000..e159b97 --- /dev/null +++ b/fees.json @@ -0,0 +1,12 @@ +{ + "withdrawal" : { + "dero-btc" : 0.00004, + "dero-ltc" : 0.0002, + "dero-arrr" : 0.001, + "dero-xmr" : 0.00006 + }, + "swap" : { + "bid": 0.75, + "ask": 0.75 + } +} diff --git a/go.mod b/go.mod index 1bd7513..bfd8a4e 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ -module dero-swap +module swap-client go 1.21.5 require ( - github.com/deroproject/derohe v0.0.0-20240215152352-a5a0e6a68ada - github.com/lesismal/llib v1.1.12 - github.com/lesismal/nbio v1.5.3 + github.com/deroproject/derohe v0.0.0-20240229002921-e9df1205b660 + github.com/gorilla/websocket v1.5.1 + github.com/lesismal/nbio v1.5.6 github.com/robfig/cron/v3 v3.0.1 github.com/ybbus/jsonrpc/v3 v3.1.5 ) @@ -18,18 +18,16 @@ require ( github.com/fxamacker/cbor/v2 v2.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/lesismal/llib v1.1.13 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/satori/go.uuid v1.2.0 // indirect github.com/stretchr/testify v1.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect go.uber.org/multierr v1.10.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.19.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/main.go b/main.go index 997c10c..56fa2a1 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,8 @@ import ( "log" "os" - "dero-swap/cfg" - "dero-swap/coin" + "swap-client/cfg" + "swap-client/coin" "github.com/robfig/cron/v3" ) @@ -26,17 +26,15 @@ func main() { log.Println("Configuration error. Please check config file!") os.Exit(1) } + cfg.LoadWallets() - coin.Locked.LoadLockedBalance() - UpdateMarkets() - c := cron.New() c.AddFunc("@every 1m", UpdateMarkets) c.AddFunc("@every 2m", Delay.CheckBackoff) c.Start() go Swap_Controller() - StartServer() + StartClient(cfg.Server_URL) } diff --git a/price.go b/price.go index 90ac878..ecf4e8c 100644 --- a/price.go +++ b/price.go @@ -1,17 +1,16 @@ package main import ( - "bytes" - "dero-swap/cfg" - "dero-swap/clients" - "dero-swap/coin" - "dero-swap/dero" - "dero-swap/monero" "encoding/json" "io" "log" "net/http" "strconv" + "swap-client/cfg" + "swap-client/clients" + "swap-client/coin" + "swap-client/dero" + "swap-client/monero" "time" ) @@ -121,186 +120,83 @@ func GetPrice(pair string) (bid float64, ask float64) { // TODO: simplify func UpdateMarkets() { - var btc, ltc, xmr, arrr float64 - var derobtc, deroltc, deroxmr, deroarrr float64 + var xmr float64 + var deroxmr 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 deroxmr > 0 && xmr > 0 && deroxmr > xmr { + swap := xmr + xmr = deroxmr + deroxmr = swap + } + + mk.Lock() + defer mk.Unlock() + + // TODO: simplify + 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 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") + } } } - // 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 - } + balance := UpdatePool() - 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) + out.Method = "client" + out.Params = balance - 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()) + Connection.WriteJSON(out) } -// TODO: reduce function calls -func UpdatePool() Swap_Balance { +func UpdatePool() clients.ClientInfo { lock.Lock() defer lock.Unlock() - var balance Swap_Balance + var info clients.ClientInfo + var pair clients.PairInfo - balance.Dero = dero.GetBalance() + info.Nickname = cfg.Settings.Nickname - for p := range coin.SimplePairs { + for p := range coin.Pairs { 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) + case coin.DEROXMR: + pair.Balance = monero.GetBalance() - coin.Locked.GetLockedBalance(p) + pair.Pair = p + case coin.XMRDERO: + pair.Balance = dero.GetBalance() - coin.Locked.GetLockedBalance(p) + pair.Pair = p + default: + continue } + info.PairInfo = append(info.PairInfo, pair) } - balance.External = clients.GetExternalBalances() - - return balance + return info } diff --git a/price_variables.go b/price_variables.go index fa4cdfa..f51a75c 100644 --- a/price_variables.go +++ b/price_variables.go @@ -1,9 +1,6 @@ package main -import ( - "dero-swap/clients" - "sync" -) +import "sync" type ( Price_Provider struct { @@ -37,12 +34,11 @@ type ( DEROXMR float64 `json:"deroxmr,omitempty"` } Swap_Balance struct { - Dero float64 `json:"dero"` - LTC float64 `json:"ltc,omitempty"` - BTC float64 `json:"btc,omitempty"` - ARRR float64 `json:"arrr,omitempty"` - XMR float64 `json:"xmr,omitempty"` - External []clients.Swap_External `json:"external,omitempty"` + Dero float64 `json:"dero"` + LTC float64 `json:"ltc,omitempty"` + BTC float64 `json:"btc,omitempty"` + ARRR float64 `json:"arrr,omitempty"` + XMR float64 `json:"xmr,omitempty"` } ) diff --git a/server.go b/server.go index 37653a8..30f9de2 100644 --- a/server.go +++ b/server.go @@ -1,26 +1,19 @@ package main import ( - "bytes" - "encoding/json" + "crypto/tls" "fmt" "log" - "net/http" - "os" + "net/url" "strings" - "sync" "time" - "github.com/lesismal/llib/std/crypto/tls" - "github.com/deroproject/derohe/globals" - "github.com/lesismal/nbio/nbhttp" - "github.com/lesismal/nbio/nbhttp/websocket" + "github.com/gorilla/websocket" - "dero-swap/cfg" - "dero-swap/clients" - "dero-swap/coin" - "dero-swap/dero" + "swap-client/cfg" + "swap-client/coin" + "swap-client/dero" ) type WS_Message struct { @@ -30,7 +23,7 @@ type WS_Message struct { Result any `json:"result"` } -var WSConnections sync.Map +var Connection *websocket.Conn // swap other coins to Dero func Dero_Swap(request coin.Swap_Request) (response coin.Swap_Response) { @@ -115,242 +108,72 @@ func Reverse_Swap(request coin.Swap_Request) (response coin.Swap_Response) { return response } -func newUpgrader() *websocket.Upgrader { - u := websocket.NewUpgrader() +func StartClient(server url.URL) { - u.CheckOrigin = (func(r *http.Request) bool { - return true - }) + var err error - u.OnClose(func(c *websocket.Conn, err error) { - WSConnections.Delete(c) - clients.Clients.Delete(c) - }) + for { - u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) { + var in WS_Message - var in, out WS_Message - var send bytes.Buffer - - read := bytes.NewReader(data) - decoder := json.NewDecoder(read) - encoder := json.NewEncoder(&send) - - err := decoder.Decode(&in) + dialer := websocket.DefaultDialer + dialer.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, + } + Connection, _, err = websocket.DefaultDialer.Dial(server.String(), nil) if err != nil { - fmt.Println(err) - return + log.Println("Websocket error, re-connect in 10 seconds") + time.Sleep(time.Second * 10) + continue } - out.ID = in.ID + log.Printf("Connected to server %s\n", cfg.Settings.ServerAddress) + UpdateMarkets() - switch in.Method { - case "swap": - - var request coin.Swap_Request - var response coin.Swap_Response - - p := in.Params.(map[string]any) - out.Method = "swap" - - if d, ok := p["pair"]; ok { - request.Pair = d.(string) - } - if d, ok := p["amount"]; ok { - request.Amount = d.(float64) - } - if d, ok := p["dero_address"]; ok { - request.DeroAddr = d.(string) + for { + if err := Connection.ReadJSON(&in); err != nil { + break } - if q, ok := p["extern"]; ok && q.(string) != "" { - if ok, conn := clients.PrepareExternalSwap(q.(string), request.Pair, request.Amount); ok { - clients.ActiveClients.ChangeClientState(clients.LOCK, conn) - clients.ActiveClients.AddOrigin(conn, c) - out.Params = request - encoder.Encode(out) - SendWSData(conn, send.Bytes()) - } else { - response.Error = "External swap not possible" + var out WS_Message + + out.ID = in.ID + + switch in.Method { + case "swap": + + var request coin.Swap_Request + var response coin.Swap_Response + + p := in.Params.(map[string]any) + out.Method = "client_ok" + + if d, ok := p["pair"]; ok { + request.Pair = d.(string) + } + if d, ok := p["amount"]; ok { + request.Amount = d.(float64) + } + if d, ok := p["dero_address"]; ok { + request.DeroAddr = d.(string) + } + + switch request.Pair { + case coin.XMRDERO: + response = Dero_Swap(request) + case coin.DEROXMR: + response = Reverse_Swap(request) + default: + return } out.Result = response - encoder.Encode(out) - SendWSData(c, send.Bytes()) - return + Connection.WriteJSON(out) + UpdateMarkets() } - - switch request.Pair { - case coin.BTCDERO, coin.LTCDERO, coin.ARRRDERO, coin.XMRDERO: - response = Dero_Swap(request) - case coin.DEROBTC, coin.DEROLTC, coin.DEROARRR, coin.DEROXMR: - response = Reverse_Swap(request) - default: - } - - out.Result = response - encoder.Encode(out) - - SendWSData(c, send.Bytes()) - - var track coin.Swap_Tracking - - track.ID = response.ID - out.Method = "track" - go func() { - for { - time.Sleep(time.Second * 15) - track.State = SwapTracking(response.ID) - out.Result = track - send.Reset() - encoder.Encode(out) - if _, ok := WSConnections.Load(c); ok { - SendWSData(c, send.Bytes()) - } else { - break - } - if track.State == SWAP_DONE || track.State == SWAP_EXPIRED { - break - } - } - }() - case "market": - - mk.RLock() - defer mk.RUnlock() - - var data bytes.Buffer - - out.Method = "market" - inner := mk.Pairs - out.Result = inner - - encoder := json.NewEncoder(&data) - encoder.Encode(out) - - SendWSData(c, data.Bytes()) - - case "balance": - - var data bytes.Buffer - - out.Method = "balance" - out.Result = UpdatePool() - - encoder := json.NewEncoder(&data) - encoder.Encode(out) - - SendWSData(c, data.Bytes()) - - case "client": - - var client clients.ClientInfo - - p := in.Params.(map[string]any) - client.Nickname = p["nickname"].(string) - - q := p["pair_info"].([]any) - for i := range q { - r := q[i].(map[string]any) - client.PairInfo = append(client.PairInfo, clients.PairInfo{Balance: r["balance"].(float64), Pair: r["pair"].(string)}) - } - if _, ok := clients.Clients.Load(c); !ok { - log.Printf("Client Hello from %s\n", client.Nickname) - } - clients.Clients.Store(c, client) - - case "client_ok": - - if clients.ActiveClients.CheckClientState(c) { - - clients.ActiveClients.ChangeClientState(clients.UNLOCK, c) - client := clients.ActiveClients.GetOrigin(c) - - out.Method = "swap" - out.Result = in.Result - encoder.Encode(out) - - SendWSData(client, send.Bytes()) - } - - default: } - - }) - - return u -} - -func webSocketHandler(w http.ResponseWriter, r *http.Request) { - - upgrader := newUpgrader() - conn, err := upgrader.Upgrade(w, r, nil) - if err != nil { - log.Println(err) - return - } - - WSConnections.Store(conn, true) -} - -func SendWSData(c *websocket.Conn, data []byte) { - - if len(data) == 0 { - return - } - - if c != nil { - err := c.WriteMessage(websocket.TextMessage, data) - if err != nil { - log.Println(err) - } - } else { - WSConnections.Range(func(k any, v any) bool { - - conn := k.(*websocket.Conn) - - err := conn.WriteMessage(websocket.TextMessage, data) - if err != nil { - log.Println(err) - } - - return true - }) + log.Println("Websocket error, re-connect in 10 seconds") + time.Sleep(time.Second * 10) } } - -func StartServer() { - - // cert files - // Let's Encrypt: - // certFile = fullchain.pem - // keyFile = privkey.pem - - // comment the following block to disable TLS - cert, err := tls.LoadX509KeyPair("/etc/letsencrypt/live/mg25ot4aggt8dprv.myfritz.net/fullchain.pem", "/etc/letsencrypt/live/mg25ot4aggt8dprv.myfritz.net/privkey.pem") - if err != nil { - log.Println(err) - os.Exit(2) - } - - mux := http.NewServeMux() - mux.HandleFunc("/ws", webSocketHandler) - //mux.HandleFunc("/api", ) - - srv := nbhttp.NewServer(nbhttp.Config{ - Network: "tcp", - Handler: mux, - // comment the following 2 lines and uncomment "Addrs" to start server without TLS - AddrsTLS: []string{cfg.Settings.ListenAddress}, - TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}}, - //Addrs: []string{cfg.Settings.ListenAddress}, - }) - - err = srv.Start() - if err != nil { - log.Println(err) - os.Exit(2) - } - srv.Wait() -} diff --git a/swap.go b/swap.go index 958ae8d..4ec3c7f 100644 --- a/swap.go +++ b/swap.go @@ -1,14 +1,14 @@ package main import ( - "dero-swap/cfg" - "dero-swap/coin" - "dero-swap/dero" - "dero-swap/monero" "encoding/json" "fmt" "log" "os" + "swap-client/cfg" + "swap-client/coin" + "swap-client/dero" + "swap-client/monero" "time" ) diff --git a/swap_manager.go b/swap_manager.go index 0b0a53a..6fa06a4 100644 --- a/swap_manager.go +++ b/swap_manager.go @@ -1,14 +1,14 @@ package main import ( - "dero-swap/coin" - "dero-swap/dero" - "dero-swap/monero" "encoding/json" "fmt" "io/fs" "log" "os" + "swap-client/coin" + "swap-client/dero" + "swap-client/monero" "time" "github.com/deroproject/derohe/rpc"