Compare commits

..

9 Commits

Author SHA1 Message Date
2d04e29bfc README.md aktualisiert 2024-04-26 15:15:09 +01:00
mmarcel
20e3ee21d8 fixes 2024-04-26 16:05:47 +02:00
mmarcel
58037695d0 Merge branch 'swap-client' of https://git.mysrv.cloud/mmarcel/dero-swaps into swap-client 2024-04-23 20:22:43 +02:00
mmarcel
8afd6defc8 LTC support 2024-04-23 20:21:35 +02:00
8ac6a545f0 README.md aktualisiert 2024-04-20 21:48:31 +01:00
ccfd94694e README.md aktualisiert 2024-04-20 21:45:47 +01:00
mmarcel
0875a4bf03 Readme 2024-04-20 22:39:29 +02:00
mmarcel
f7bdab393e Updated config.json 2024-04-20 22:26:40 +02:00
mmarcel
764ad4aa1b Swap Client 2024-04-20 22:23:48 +02:00
17 changed files with 277 additions and 548 deletions

View File

@ -1,4 +1,4 @@
# Dero Swaps
# Dero Swap Client
## Build from source
Clone the repository to your local disk.\
@ -11,35 +11,15 @@ 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,
"server" : (string) IP:Port of swap server,
"nickname" : (string) name of client,
"monero_wallet" : (string) IP:Port of Monero Wallet,
"dero_daemon" : (string) IP:Port of Dero Daemon,
"dero_wallet" : (string) IP:Port of Dero Wallet,
"dero_login" : (string, optional) user:password used to access wallet RPC,
"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**
```
{
@ -74,66 +54,22 @@ Example file:
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
- ltc-dero
- dero-ltc
## 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.
The swap client connects to the specified server, provides information about the current balance and listens for incoming swap requests.
### 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*\
Download the Monero-CLI files from https://www.getmonero.org/downloads/#cli\
Create a new wallet. If there is no local daemon, use a remote node\
(eg. node.moneroworld.com:18089; `./monero-wallet-cli --daemon-address node.moneroworld.com:18089`)\
Start the RPC server (RPC login not implemented yet)\
(eg. `./monero-wallet-rpc --daemon-address node.moneroworld.com:18089 --rpc-bind-port 18090 --wallet-file <wallet> --password <wallet_pw> --disable-rpc-login`)\
Update **config.json**\
Run client
**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
```
## Important Notes
The Dero wallet needs the "missing entries" fix! (https://github.com/deroproject/derohe/pull/150)

View File

@ -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,11 @@ func LoadConfig() {
log.Println("Dero Wallet: No RPC authorization specified")
}
Server_URL = url.URL{Scheme: "wss", Host: Settings.ServerAddress, Path: "/ws"}
monero.Monero_Wallet = jsonrpc.NewClient("http://" + Settings.Monero_Wallet + "/json_rpc")
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
// check if pair is "supported"
for _, p := range Settings.Pairs {
@ -87,6 +88,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,12 +100,10 @@ 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)
case coin.XMRDERO, coin.DEROXMR:
if Settings.Monero_Wallet == "" {
log.Printf("%s pair is set, but wallet 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 == "" {
@ -108,18 +112,6 @@ func CheckConfig() bool {
} 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)
return false
}
}
}

View File

@ -1,18 +1,17 @@
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"`
Monero_Daemon string `json:"monero_daemon"`
Monero_Wallet string `json:"Monero_Wallet"`
LTC_Daemon string `json:"LTC_Daemon"`
LTC_Dir string `json:"LTC_Dir"`
Pairs []string `json:"pairs"`
//SwapFees float64 `json:"swap_fees"`
}
@ -48,3 +47,5 @@ type (
var Settings Config
var SwapFees Fees
var Server_URL url.URL

View File

@ -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
}

View File

@ -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)}

View File

@ -10,7 +10,7 @@ type (
Pair string `json:"pair"`
Amount float64 `json:"amount"`
DeroAddr string `json:"dero_address"`
Extern string `json:"extern,omitempty"`
Extern bool `json:"extern,omitempty"`
}
Swap_Response struct {
ID int64 `json:"id"`

8
config.json Normal file
View File

@ -0,0 +1,8 @@
{
"server" : "mg25ot4aggt8dprv.myfritz.net:10413",
"nickname" : "mmarcel",
"monero_wallet" : "localhost:18090",
"dero_daemon" : "localhost:10102",
"dero_wallet" : "localhost:10103",
"pairs" : ["xmr-dero","dero-xmr"]
}

View File

@ -5,7 +5,7 @@ import (
"log"
"time"
"dero-swap/coin"
"swap-client/coin"
"github.com/deroproject/derohe/cryptography/crypto"
"github.com/deroproject/derohe/rpc"

12
fees.json Normal file
View File

@ -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
}
}

16
go.mod
View File

@ -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
)

61
go.sum Normal file
View File

@ -0,0 +1,61 @@
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deroproject/derohe v0.0.0-20240229002921-e9df1205b660 h1:GwFMlJiyJ72+U5xLaeqZaUcVtEIJ5DqIjHTzUX2//OU=
github.com/deroproject/derohe v0.0.0-20240229002921-e9df1205b660/go.mod h1:EWHh1VkXRnCHvyGML98kXhngDFYebmOhk/9kZ1ATJ1c=
github.com/deroproject/graviton v0.0.0-20220130070622-2c248a53b2e1 h1:nsiNx83HYmRmYpYO37pUzSTmB7p9PFtGBl4FyD+a0jg=
github.com/deroproject/graviton v0.0.0-20220130070622-2c248a53b2e1/go.mod h1:a4u6QJtGGIADg1JwujD77UtaAyhIxg14+I0C7xjyQcc=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/lesismal/llib v1.1.13 h1:+w1+t0PykXpj2dXQck0+p6vdC9/mnbEXHgUy/HXDGfE=
github.com/lesismal/llib v1.1.13/go.mod h1:70tFXXe7P1FZ02AU9l8LgSOK7d7sRrpnkUr3rd3gKSg=
github.com/lesismal/nbio v1.5.6 h1:ad8s3AnZnAjMOHhnClHxWp69FsjiS40zOvJ0BYS9IG8=
github.com/lesismal/nbio v1.5.6/go.mod h1:mvfYBAA1jmrafXf2XvkM28jWkMTfA5jGks+HKDBMmOc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/ybbus/jsonrpc/v3 v3.1.5 h1:0cC/QzS8OCuXYqqDbYnKKhsEe+IZLrNlDx8KPCieeW0=
github.com/ybbus/jsonrpc/v3 v3.1.5/go.mod h1:U1QbyNfL5Pvi2roT0OpRbJeyvGxfWYSgKJHjxWdAEeE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20210513122933-cd7d49e622d5/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

10
main.go
View File

@ -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)
}

200
price.go
View File

@ -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,81 +120,34 @@ 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, ltc float64
var deroxmr, deroltc 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
// sometimes TradeOgre's BID/ASK values are swapped
if deroxmr > 0 && xmr > 0 && deroxmr > xmr {
swap := xmr
xmr = deroxmr
deroxmr = swap
}
if deroltc > 0 && ltc > 0 && deroltc > ltc {
swap := ltc
ltc = deroltc
deroltc = 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()
@ -207,39 +159,6 @@ func UpdateMarkets() {
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()
@ -251,56 +170,69 @@ func UpdateMarkets() {
log.Println("DERO->XMR 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 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")
}
}
balance := UpdatePool()
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
if Connection != nil {
Connection.WriteJSON(out)
} else {
log.Println("<nil> server connection")
}
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 {
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.DEROLTC:
pair.Balance = coin.XTCGetBalance(p)
pair.Pair = p
case coin.DEROXMR:
pair.Balance = monero.GetBalance()
pair.Pair = p
case coin.XMRDERO, coin.LTCDERO:
pair.Balance = dero.GetBalance()
pair.Pair = p
default:
continue
}
info.PairInfo = append(info.PairInfo, pair)
}
balance.External = clients.GetExternalBalances()
return balance
return info
}

View File

@ -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"`
}
)

293
server.go
View File

@ -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, coin.LTCDERO:
response = Dero_Swap(request)
case coin.DEROXMR, coin.DEROLTC:
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()
}

View File

@ -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"
)

View File

@ -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"