210 lines
5.0 KiB
Go
210 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"swap-client/cfg"
|
|
"swap-client/coin"
|
|
"swap-client/dero"
|
|
"swap-client/monero"
|
|
"time"
|
|
)
|
|
|
|
func CreateSwap(pair string, wallet string, dest string, amount float64, price float64) int64 {
|
|
|
|
var entry coin.Swap_Entry
|
|
var height uint64
|
|
var payout float64 = amount
|
|
|
|
// get current block height. Ignore transactions < height
|
|
switch pair {
|
|
case coin.BTCDERO, coin.LTCDERO, coin.ARRRDERO:
|
|
height = coin.XTCCheckBlockHeight(pair)
|
|
case coin.XMRDERO:
|
|
height = monero.GetHeight()
|
|
default:
|
|
height = dero.CheckBlockHeight()
|
|
}
|
|
|
|
if height == 0 {
|
|
return 0
|
|
}
|
|
|
|
entry.Coin = pair
|
|
entry.Wallet = wallet
|
|
entry.Destination = dest
|
|
entry.Price = price
|
|
entry.Amount = amount
|
|
entry.Created = time.Now().UnixMilli()
|
|
entry.Block = height
|
|
entry.Status = 0
|
|
|
|
// create an integrated address for all Dero -> X swaps
|
|
if pair == coin.DEROLTC || pair == coin.DEROBTC || pair == coin.DEROARRR || pair == coin.DEROXMR {
|
|
if entry.Wallet = dero.MakeIntegratedAddress(entry.Created); entry.Wallet == "" {
|
|
return 0
|
|
}
|
|
payout = entry.Price
|
|
}
|
|
|
|
json_bytes, err := json.Marshal(&entry)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
err = os.WriteFile(fmt.Sprintf("swaps/active/%d", entry.Created), json_bytes, 0644)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
log.Printf("Swap request (%d) of %.8f (%s) successfully created\n", entry.Created, payout, entry.Coin)
|
|
|
|
return entry.Created
|
|
}
|
|
|
|
// X to Dero Swaps
|
|
func XTCSwap(pair string, dst_addr string, amount float64, resp *coin.Swap_Response) (err error) {
|
|
|
|
// check if Dero address is registered on chain
|
|
if !dero.IsDeroAddressRegistered(dst_addr) {
|
|
return fmt.Errorf("dero address is not registered")
|
|
}
|
|
|
|
// check balance and include locked swap balance
|
|
balance, err := dero.CheckWalletBalance()
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't check swap balance")
|
|
}
|
|
if coin.Locked.GetLockedBalance(pair)+amount+dero.TxFee > balance {
|
|
return fmt.Errorf("insufficient swap balance")
|
|
}
|
|
|
|
coin.Locked.AddLockedBalance(pair, amount)
|
|
|
|
// create/get a deposit address
|
|
switch pair {
|
|
case coin.BTCDERO:
|
|
resp.Wallet = coin.BTC_address
|
|
case coin.LTCDERO:
|
|
resp.Wallet = coin.LTC_address
|
|
case coin.XMRDERO:
|
|
resp.Wallet = monero.MakeIntegratedAddress()
|
|
case coin.ARRRDERO:
|
|
resp.Wallet = coin.ARRR_address
|
|
}
|
|
if resp.Wallet == "" {
|
|
return fmt.Errorf("no swap deposit address available")
|
|
}
|
|
|
|
var coin_value float64
|
|
var atomicUnits uint = 8
|
|
|
|
switch pair {
|
|
case coin.BTCDERO:
|
|
coin_value = mk.Pairs.BTC
|
|
case coin.LTCDERO:
|
|
coin_value = mk.Pairs.LTC
|
|
case coin.ARRRDERO:
|
|
coin_value = mk.Pairs.ARRR
|
|
case coin.XMRDERO:
|
|
coin_value = mk.Pairs.XMR
|
|
atomicUnits = 12
|
|
}
|
|
|
|
deposit_value := coin_value * amount
|
|
|
|
var loops int = 5
|
|
var isAvailable bool
|
|
|
|
// if there is a request with the same deposit amount, run in a loop and lower deposit value by 1 Sat
|
|
for i := 0; i < loops; i++ {
|
|
if coin.IsAmountFree(pair, deposit_value) {
|
|
isAvailable = true
|
|
break
|
|
}
|
|
deposit_value -= SATOSHI
|
|
}
|
|
if !isAvailable || deposit_value == 0 {
|
|
return fmt.Errorf("Pre-Check: Couldn't create swap")
|
|
}
|
|
|
|
deposit_value = coin.RoundFloat(deposit_value, atomicUnits)
|
|
|
|
resp.ID = CreateSwap(pair, resp.Wallet, dst_addr, amount, deposit_value)
|
|
if resp.ID == 0 {
|
|
return fmt.Errorf("couldn't create swap")
|
|
}
|
|
resp.Deposit = deposit_value
|
|
|
|
return nil
|
|
}
|
|
|
|
// Dero to X swaps
|
|
func DeroXTCSwap(pair string, dst_addr string, amount float64, resp *coin.Swap_Response) (err error) {
|
|
|
|
var balance float64
|
|
var atomicUnits uint = 8
|
|
|
|
// validate destination wallet and check for sufficient swap balance
|
|
if pair != coin.DEROXMR {
|
|
if !coin.XTCValidateAddress(pair, dst_addr) {
|
|
return fmt.Errorf("%s address is not valid", pair[5:])
|
|
}
|
|
balance = coin.XTCGetBalance(pair)
|
|
} else {
|
|
if !monero.ValidateAddress(dst_addr) {
|
|
return fmt.Errorf("XMR address is not valid")
|
|
}
|
|
balance = monero.GetBalance()
|
|
atomicUnits = 12
|
|
}
|
|
|
|
var coin_value float64
|
|
var fees float64
|
|
|
|
// determine fees and current price
|
|
switch pair {
|
|
case coin.DEROLTC:
|
|
coin_value = mk.Pairs.DEROLTC
|
|
fees = cfg.SwapFees.Withdrawal.DeroLTC
|
|
case coin.DEROARRR:
|
|
coin_value = mk.Pairs.DEROARRR
|
|
fees = cfg.SwapFees.Withdrawal.DeroARRR
|
|
case coin.DEROBTC:
|
|
coin_value = mk.Pairs.DEROBTC
|
|
fees = cfg.SwapFees.Withdrawal.DeroBTC
|
|
case coin.DEROXMR:
|
|
coin_value = mk.Pairs.DEROXMR
|
|
fees = cfg.SwapFees.Withdrawal.DeroXMR
|
|
}
|
|
|
|
payout_value := coin_value * amount
|
|
if payout_value-fees < 0 {
|
|
return fmt.Errorf("fees > payout value")
|
|
}
|
|
if payout_value == 0 {
|
|
return fmt.Errorf("couldn't create swap")
|
|
}
|
|
|
|
// check for reserved balance
|
|
if coin.Locked.GetLockedBalance(pair)+payout_value+fees > balance {
|
|
return fmt.Errorf("insufficient swap balance")
|
|
}
|
|
|
|
coin.Locked.AddLockedBalance(pair, payout_value)
|
|
|
|
payout_value -= fees
|
|
payout_value = coin.RoundFloat(payout_value, atomicUnits)
|
|
resp.Swap = payout_value
|
|
|
|
resp.ID = CreateSwap(pair, "", dst_addr, amount, payout_value)
|
|
if resp.ID == 0 {
|
|
return fmt.Errorf("couldn't create swap")
|
|
}
|
|
resp.Wallet = dero.MakeIntegratedAddress(resp.ID)
|
|
|
|
return nil
|
|
}
|