mirror of
https://github.com/basicswap/basicswap.git
synced 2025-11-05 18:38:09 +01:00
@@ -32,7 +32,7 @@ from .interface.part import PARTInterface, PARTInterfaceAnon, PARTInterfaceBlind
|
|||||||
from . import __version__
|
from . import __version__
|
||||||
from .rpc import escape_rpcauth
|
from .rpc import escape_rpcauth
|
||||||
from .rpc_xmr import make_xmr_rpc2_func
|
from .rpc_xmr import make_xmr_rpc2_func
|
||||||
from .ui.util import getCoinName, known_chart_coins
|
from .ui.util import getCoinName
|
||||||
from .util import (
|
from .util import (
|
||||||
AutomationConstraint,
|
AutomationConstraint,
|
||||||
AutomationConstraintTemporary,
|
AutomationConstraintTemporary,
|
||||||
@@ -65,6 +65,12 @@ from basicswap.util.network import is_private_ip_address
|
|||||||
from .chainparams import (
|
from .chainparams import (
|
||||||
Coins,
|
Coins,
|
||||||
chainparams,
|
chainparams,
|
||||||
|
Fiat,
|
||||||
|
ticker_map,
|
||||||
|
)
|
||||||
|
from .explorers import (
|
||||||
|
default_chart_api_key,
|
||||||
|
default_coingecko_api_key,
|
||||||
)
|
)
|
||||||
from .script import (
|
from .script import (
|
||||||
OpCodes,
|
OpCodes,
|
||||||
@@ -126,6 +132,8 @@ from .basicswap_util import (
|
|||||||
BidStates,
|
BidStates,
|
||||||
DebugTypes,
|
DebugTypes,
|
||||||
EventLogTypes,
|
EventLogTypes,
|
||||||
|
fiatTicker,
|
||||||
|
get_api_key_setting,
|
||||||
KeyTypes,
|
KeyTypes,
|
||||||
MessageTypes,
|
MessageTypes,
|
||||||
NotificationTypes as NT,
|
NotificationTypes as NT,
|
||||||
@@ -9894,7 +9902,7 @@ class BasicSwap(BaseApp):
|
|||||||
seen_tickers = []
|
seen_tickers = []
|
||||||
for ticker in tickers:
|
for ticker in tickers:
|
||||||
upcased_ticker = ticker.strip().upper()
|
upcased_ticker = ticker.strip().upper()
|
||||||
if upcased_ticker not in known_chart_coins:
|
if upcased_ticker.lower() not in ticker_map:
|
||||||
raise ValueError(f"Unknown coin: {ticker}")
|
raise ValueError(f"Unknown coin: {ticker}")
|
||||||
if upcased_ticker in seen_tickers:
|
if upcased_ticker in seen_tickers:
|
||||||
raise ValueError(f"Duplicate coin: {ticker}")
|
raise ValueError(f"Duplicate coin: {ticker}")
|
||||||
@@ -11058,6 +11066,160 @@ class BasicSwap(BaseApp):
|
|||||||
).isWalletEncryptedLocked()
|
).isWalletEncryptedLocked()
|
||||||
return self._is_encrypted, self._is_locked
|
return self._is_encrypted, self._is_locked
|
||||||
|
|
||||||
|
def getExchangeName(self, coin_id: int, exchange_name: str) -> str:
|
||||||
|
if coin_id == Coins.BCH:
|
||||||
|
return "bitcoin-cash"
|
||||||
|
if coin_id == Coins.FIRO:
|
||||||
|
return "zcoin"
|
||||||
|
return chainparams[coin_id]["name"]
|
||||||
|
|
||||||
|
def lookupFiatRates(
|
||||||
|
self,
|
||||||
|
coins_list,
|
||||||
|
currency_to: int = Fiat.USD,
|
||||||
|
rate_source: str = "coingecko.com",
|
||||||
|
saved_ttl: int = 300,
|
||||||
|
):
|
||||||
|
self.log.debug(f"lookupFiatRates {coins_list}.")
|
||||||
|
ensure(len(coins_list) > 0, "Must specify coin/s")
|
||||||
|
|
||||||
|
now: int = int(time.time())
|
||||||
|
oldest_time_valid: int = now - saved_ttl
|
||||||
|
return_rates = {}
|
||||||
|
|
||||||
|
headers = {"User-Agent": "Mozilla/5.0", "Connection": "close"}
|
||||||
|
|
||||||
|
cursor = self.openDB()
|
||||||
|
try:
|
||||||
|
parameters = {
|
||||||
|
"rate_source": rate_source,
|
||||||
|
"oldest_time_valid": oldest_time_valid,
|
||||||
|
"currency_to": currency_to,
|
||||||
|
}
|
||||||
|
coins_list_query = ""
|
||||||
|
for i, coin_id in enumerate(coins_list):
|
||||||
|
try:
|
||||||
|
_ = Coins(coin_id)
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(f"Unknown coin type {coin_id}")
|
||||||
|
|
||||||
|
param_name = f"coin_{i}"
|
||||||
|
if i > 0:
|
||||||
|
coins_list_query += ","
|
||||||
|
coins_list_query += f":{param_name}"
|
||||||
|
parameters[param_name] = coin_id
|
||||||
|
|
||||||
|
query = f"SELECT currency_from, rate FROM coinrates WHERE currency_from IN ({coins_list_query}) AND currency_to = :currency_to AND source = :rate_source AND last_updated >= :oldest_time_valid"
|
||||||
|
rows = cursor.execute(query, parameters)
|
||||||
|
|
||||||
|
for row in rows:
|
||||||
|
return_rates[int(row[0])] = float(row[1])
|
||||||
|
|
||||||
|
need_coins = []
|
||||||
|
new_values = {}
|
||||||
|
exchange_name_map = {}
|
||||||
|
for coin_id in coins_list:
|
||||||
|
if coin_id not in return_rates:
|
||||||
|
need_coins.append(coin_id)
|
||||||
|
|
||||||
|
if len(need_coins) < 1:
|
||||||
|
return return_rates
|
||||||
|
|
||||||
|
if rate_source == "coingecko.com":
|
||||||
|
ticker_to: str = fiatTicker(currency_to).lower()
|
||||||
|
# Update all requested coins
|
||||||
|
coin_ids: str = ""
|
||||||
|
for coin_id in coins_list:
|
||||||
|
if len(coin_ids) > 0:
|
||||||
|
coin_ids += ","
|
||||||
|
exchange_name = self.getExchangeName(coin_id, rate_source)
|
||||||
|
coin_ids += exchange_name
|
||||||
|
exchange_name_map[exchange_name] = coin_id
|
||||||
|
|
||||||
|
api_key: str = get_api_key_setting(
|
||||||
|
self.settings,
|
||||||
|
"coingecko_api_key",
|
||||||
|
default_coingecko_api_key,
|
||||||
|
escape=True,
|
||||||
|
)
|
||||||
|
url: str = (
|
||||||
|
f"https://api.coingecko.com/api/v3/simple/price?ids={coin_ids}&vs_currencies={ticker_to}"
|
||||||
|
)
|
||||||
|
if api_key != "":
|
||||||
|
url += f"&api_key={api_key}"
|
||||||
|
|
||||||
|
self.log.debug(f"lookupFiatRates: {url}")
|
||||||
|
js = json.loads(self.readURL(url, timeout=10, headers=headers))
|
||||||
|
|
||||||
|
for k, v in js.items():
|
||||||
|
return_rates[int(exchange_name_map[k])] = v[ticker_to]
|
||||||
|
new_values[exchange_name_map[k]] = v[ticker_to]
|
||||||
|
elif rate_source == "cryptocompare.com":
|
||||||
|
ticker_to: str = fiatTicker(currency_to).upper()
|
||||||
|
for coin_id in need_coins:
|
||||||
|
coin_ticker: str = chainparams[coin_id]["ticker"]
|
||||||
|
|
||||||
|
api_key: str = get_api_key_setting(
|
||||||
|
self.settings,
|
||||||
|
"chart_api_key",
|
||||||
|
default_chart_api_key,
|
||||||
|
escape=True,
|
||||||
|
)
|
||||||
|
url: str = (
|
||||||
|
f"https://min-api.cryptocompare.com/data/price?fsym={coin_ticker}&tsyms={ticker_to}"
|
||||||
|
)
|
||||||
|
if api_key != "":
|
||||||
|
url += f"&api_key={api_key}"
|
||||||
|
|
||||||
|
self.log.debug(f"lookupFiatRates: {url}")
|
||||||
|
js = json.loads(self.readURL(url, timeout=10, headers=headers))
|
||||||
|
return_rates[int(coin_id)] = js[ticker_to]
|
||||||
|
new_values[coin_id] = js[ticker_to]
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unknown rate source {rate_source}")
|
||||||
|
|
||||||
|
if len(new_values) < 1:
|
||||||
|
return return_rates
|
||||||
|
|
||||||
|
# ON CONFLICT clause does not match any PRIMARY KEY or UNIQUE constraint
|
||||||
|
update_query = """
|
||||||
|
UPDATE coinrates SET
|
||||||
|
rate=:rate,
|
||||||
|
last_updated=:last_updated
|
||||||
|
WHERE currency_from = :currency_from AND currency_to = :currency_to AND source = :rate_source
|
||||||
|
"""
|
||||||
|
|
||||||
|
insert_query = """INSERT INTO coinrates(currency_from, currency_to, rate, source, last_updated)
|
||||||
|
VALUES(:currency_from, :currency_to, :rate, :rate_source, :last_updated)"""
|
||||||
|
|
||||||
|
for k, v in new_values.items():
|
||||||
|
cursor.execute(
|
||||||
|
update_query,
|
||||||
|
{
|
||||||
|
"currency_from": k,
|
||||||
|
"currency_to": currency_to,
|
||||||
|
"rate": v,
|
||||||
|
"rate_source": rate_source,
|
||||||
|
"last_updated": now,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if cursor.rowcount < 1:
|
||||||
|
cursor.execute(
|
||||||
|
insert_query,
|
||||||
|
{
|
||||||
|
"currency_from": k,
|
||||||
|
"currency_to": currency_to,
|
||||||
|
"rate": v,
|
||||||
|
"rate_source": rate_source,
|
||||||
|
"last_updated": now,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.commitDB()
|
||||||
|
return return_rates
|
||||||
|
finally:
|
||||||
|
self.closeDB(cursor, commit=False)
|
||||||
|
|
||||||
def lookupRates(self, coin_from, coin_to, output_array=False):
|
def lookupRates(self, coin_from, coin_to, output_array=False):
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"lookupRates {}, {}.".format(
|
"lookupRates {}, {}.".format(
|
||||||
@@ -11070,25 +11232,14 @@ class BasicSwap(BaseApp):
|
|||||||
ci_to = self.ci(int(coin_to))
|
ci_to = self.ci(int(coin_to))
|
||||||
name_from = ci_from.chainparams()["name"]
|
name_from = ci_from.chainparams()["name"]
|
||||||
name_to = ci_to.chainparams()["name"]
|
name_to = ci_to.chainparams()["name"]
|
||||||
exchange_name_from = ci_from.getExchangeName("coingecko.com")
|
|
||||||
exchange_name_to = ci_to.getExchangeName("coingecko.com")
|
|
||||||
ticker_from = ci_from.chainparams()["ticker"]
|
ticker_from = ci_from.chainparams()["ticker"]
|
||||||
ticker_to = ci_to.chainparams()["ticker"]
|
ticker_to = ci_to.chainparams()["ticker"]
|
||||||
headers = {"User-Agent": "Mozilla/5.0", "Connection": "close"}
|
|
||||||
rv = {}
|
rv = {}
|
||||||
|
|
||||||
if rate_sources.get("coingecko.com", True):
|
if rate_sources.get("coingecko.com", True):
|
||||||
try:
|
try:
|
||||||
url = "https://api.coingecko.com/api/v3/simple/price?ids={},{}&vs_currencies=usd,btc".format(
|
js = self.lookupFiatRates([int(coin_from), int(coin_to)])
|
||||||
exchange_name_from, exchange_name_to
|
rate = float(js[int(coin_from)]) / float(js[int(coin_to)])
|
||||||
)
|
|
||||||
self.log.debug(f"lookupRates: {url}")
|
|
||||||
start = time.time()
|
|
||||||
js = json.loads(self.readURL(url, timeout=10, headers=headers))
|
|
||||||
js["time_taken"] = time.time() - start
|
|
||||||
rate = float(js[exchange_name_from]["usd"]) / float(
|
|
||||||
js[exchange_name_to]["usd"]
|
|
||||||
)
|
|
||||||
js["rate_inferred"] = ci_to.format_amount(rate, conv_int=True, r=1)
|
js["rate_inferred"] = ci_to.format_amount(rate, conv_int=True, r=1)
|
||||||
rv["coingecko"] = js
|
rv["coingecko"] = js
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -11096,12 +11247,10 @@ class BasicSwap(BaseApp):
|
|||||||
if self.debug:
|
if self.debug:
|
||||||
self.log.error(traceback.format_exc())
|
self.log.error(traceback.format_exc())
|
||||||
|
|
||||||
if exchange_name_from != name_from:
|
js[name_from] = {"usd": js[int(coin_from)]}
|
||||||
js[name_from] = js[exchange_name_from]
|
js.pop(int(coin_from))
|
||||||
js.pop(exchange_name_from)
|
js[name_to] = {"usd": js[int(coin_to)]}
|
||||||
if exchange_name_to != name_to:
|
js.pop(int(coin_to))
|
||||||
js[name_to] = js[exchange_name_to]
|
|
||||||
js.pop(exchange_name_to)
|
|
||||||
|
|
||||||
if output_array:
|
if output_array:
|
||||||
|
|
||||||
@@ -11120,8 +11269,6 @@ class BasicSwap(BaseApp):
|
|||||||
ticker_to,
|
ticker_to,
|
||||||
format_float(float(js[name_from]["usd"])),
|
format_float(float(js[name_from]["usd"])),
|
||||||
format_float(float(js[name_to]["usd"])),
|
format_float(float(js[name_to]["usd"])),
|
||||||
format_float(float(js[name_from]["btc"])),
|
|
||||||
format_float(float(js[name_to]["btc"])),
|
|
||||||
format_float(float(js["rate_inferred"])),
|
format_float(float(js["rate_inferred"])),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2021-2024 tecnovert
|
# Copyright (c) 2021-2024 tecnovert
|
||||||
# Copyright (c) 2024 The Basicswap developers
|
# Copyright (c) 2024-2025 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -9,12 +9,14 @@
|
|||||||
import struct
|
import struct
|
||||||
import hashlib
|
import hashlib
|
||||||
from enum import IntEnum, auto
|
from enum import IntEnum, auto
|
||||||
|
from html import escape as html_escape
|
||||||
from .util.address import (
|
from .util.address import (
|
||||||
encodeAddress,
|
encodeAddress,
|
||||||
decodeAddress,
|
decodeAddress,
|
||||||
)
|
)
|
||||||
from .chainparams import (
|
from .chainparams import (
|
||||||
chainparams,
|
chainparams,
|
||||||
|
Fiat,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -520,7 +522,7 @@ def getLastBidState(packed_states):
|
|||||||
return BidStates.BID_STATE_UNKNOWN
|
return BidStates.BID_STATE_UNKNOWN
|
||||||
|
|
||||||
|
|
||||||
def strSwapType(swap_type):
|
def strSwapType(swap_type) -> str:
|
||||||
if swap_type == SwapTypes.SELLER_FIRST:
|
if swap_type == SwapTypes.SELLER_FIRST:
|
||||||
return "seller_first"
|
return "seller_first"
|
||||||
if swap_type == SwapTypes.XMR_SWAP:
|
if swap_type == SwapTypes.XMR_SWAP:
|
||||||
@@ -528,7 +530,7 @@ def strSwapType(swap_type):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def strSwapDesc(swap_type):
|
def strSwapDesc(swap_type) -> str:
|
||||||
if swap_type == SwapTypes.SELLER_FIRST:
|
if swap_type == SwapTypes.SELLER_FIRST:
|
||||||
return "Secret Hash"
|
return "Secret Hash"
|
||||||
if swap_type == SwapTypes.XMR_SWAP:
|
if swap_type == SwapTypes.XMR_SWAP:
|
||||||
@@ -536,6 +538,31 @@ def strSwapDesc(swap_type):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def fiatTicker(fiat_ind: int) -> str:
|
||||||
|
try:
|
||||||
|
return Fiat(fiat_ind).name
|
||||||
|
except Exception as e: # noqa: F841
|
||||||
|
raise ValueError(f"Unknown fiat ind {fiat_ind}")
|
||||||
|
|
||||||
|
|
||||||
|
def fiatFromTicker(ticker: str) -> int:
|
||||||
|
ticker_uc = ticker.upper()
|
||||||
|
for entry in Fiat:
|
||||||
|
if entry.name == ticker_uc:
|
||||||
|
return entry
|
||||||
|
raise ValueError(f"Unknown fiat {ticker}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_key_setting(
|
||||||
|
settings, setting_name: str, default_value: str = "", escape: bool = False
|
||||||
|
):
|
||||||
|
setting_name_enc: str = setting_name + "_enc"
|
||||||
|
if setting_name_enc in settings:
|
||||||
|
rv = bytes.fromhex(settings[setting_name_enc]).decode("utf-8")
|
||||||
|
return html_escape(rv) if escape else rv
|
||||||
|
return settings.get(setting_name, default_value)
|
||||||
|
|
||||||
|
|
||||||
inactive_states = [
|
inactive_states = [
|
||||||
BidStates.SWAP_COMPLETED,
|
BidStates.SWAP_COMPLETED,
|
||||||
BidStates.BID_ERROR,
|
BidStates.BID_ERROR,
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ class Coins(IntEnum):
|
|||||||
DOGE = 18
|
DOGE = 18
|
||||||
|
|
||||||
|
|
||||||
|
class Fiat(IntEnum):
|
||||||
|
USD = -1
|
||||||
|
GBP = -2
|
||||||
|
EUR = -3
|
||||||
|
|
||||||
|
|
||||||
chainparams = {
|
chainparams = {
|
||||||
Coins.PART: {
|
Coins.PART: {
|
||||||
"name": "particl",
|
"name": "particl",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2019-2024 tecnovert
|
# Copyright (c) 2019-2024 tecnovert
|
||||||
# Copyright (c) 2024 The Basicswap developers
|
# Copyright (c) 2024-2025 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ from enum import IntEnum, auto
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
CURRENT_DB_VERSION = 25
|
CURRENT_DB_VERSION = 26
|
||||||
CURRENT_DB_DATA_VERSION = 5
|
CURRENT_DB_DATA_VERSION = 5
|
||||||
|
|
||||||
|
|
||||||
@@ -644,6 +644,17 @@ class CheckedBlock(Table):
|
|||||||
block_time = Column("integer")
|
block_time = Column("integer")
|
||||||
|
|
||||||
|
|
||||||
|
class CoinRates(Table):
|
||||||
|
__tablename__ = "coinrates"
|
||||||
|
|
||||||
|
record_id = Column("integer", primary_key=True, autoincrement=True)
|
||||||
|
currency_from = Column("integer")
|
||||||
|
currency_to = Column("integer")
|
||||||
|
rate = Column("string")
|
||||||
|
source = Column("string")
|
||||||
|
last_updated = Column("integer")
|
||||||
|
|
||||||
|
|
||||||
def create_db(db_path: str, log) -> None:
|
def create_db(db_path: str, log) -> None:
|
||||||
con = None
|
con = None
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2022-2024 tecnovert
|
# Copyright (c) 2022-2024 tecnovert
|
||||||
# Copyright (c) 2024 The Basicswap developers
|
# Copyright (c) 2024-2025 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -410,6 +410,19 @@ def upgradeDatabase(self, db_version):
|
|||||||
elif current_version == 24:
|
elif current_version == 24:
|
||||||
db_version += 1
|
db_version += 1
|
||||||
cursor.execute("ALTER TABLE bidstates ADD COLUMN can_accept INTEGER")
|
cursor.execute("ALTER TABLE bidstates ADD COLUMN can_accept INTEGER")
|
||||||
|
elif current_version == 25:
|
||||||
|
db_version += 1
|
||||||
|
cursor.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE coinrates (
|
||||||
|
record_id INTEGER NOT NULL,
|
||||||
|
currency_from INTEGER,
|
||||||
|
currency_to INTEGER,
|
||||||
|
rate VARCHAR,
|
||||||
|
source VARCHAR,
|
||||||
|
last_updated INTEGER,
|
||||||
|
PRIMARY KEY (record_id))"""
|
||||||
|
)
|
||||||
if current_version != db_version:
|
if current_version != db_version:
|
||||||
self.db_version = db_version
|
self.db_version = db_version
|
||||||
self.setIntKV("db_version", db_version, cursor)
|
self.setIntKV("db_version", db_version, cursor)
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2019-2023 tecnovert
|
# Copyright (c) 2019-2023 tecnovert
|
||||||
|
# Copyright (c) 2025 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
default_chart_api_key = (
|
||||||
|
"95dd900af910656e0e17c41f2ddc5dba77d01bf8b0e7d2787634a16bd976c553"
|
||||||
|
)
|
||||||
|
default_coingecko_api_key = "CG-8hm3r9iLfpEXv4ied8oLbeUj"
|
||||||
|
|
||||||
|
|
||||||
class Explorer:
|
class Explorer:
|
||||||
def __init__(self, swapclient, coin_type, base_url):
|
def __init__(self, swapclient, coin_type, base_url):
|
||||||
self.swapclient = swapclient
|
self.swapclient = swapclient
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from .util import (
|
|||||||
toBool,
|
toBool,
|
||||||
)
|
)
|
||||||
from .basicswap_util import (
|
from .basicswap_util import (
|
||||||
|
fiatFromTicker,
|
||||||
strBidState,
|
strBidState,
|
||||||
strTxState,
|
strTxState,
|
||||||
SwapTypes,
|
SwapTypes,
|
||||||
@@ -22,6 +23,9 @@ from .basicswap_util import (
|
|||||||
from .chainparams import (
|
from .chainparams import (
|
||||||
Coins,
|
Coins,
|
||||||
chainparams,
|
chainparams,
|
||||||
|
Fiat,
|
||||||
|
getCoinIdFromTicker,
|
||||||
|
getCoinIdFromName,
|
||||||
)
|
)
|
||||||
from .ui.util import (
|
from .ui.util import (
|
||||||
PAGE_LIMIT,
|
PAGE_LIMIT,
|
||||||
@@ -33,7 +37,6 @@ from .ui.util import (
|
|||||||
get_data_entry,
|
get_data_entry,
|
||||||
get_data_entry_or,
|
get_data_entry_or,
|
||||||
have_data_entry,
|
have_data_entry,
|
||||||
tickerToCoinId,
|
|
||||||
listOldBidStates,
|
listOldBidStates,
|
||||||
checkAddressesOwned,
|
checkAddressesOwned,
|
||||||
)
|
)
|
||||||
@@ -124,7 +127,7 @@ def js_wallets(self, url_split, post_string, is_json):
|
|||||||
swap_client.checkSystemStatus()
|
swap_client.checkSystemStatus()
|
||||||
if len(url_split) > 3:
|
if len(url_split) > 3:
|
||||||
ticker_str = url_split[3]
|
ticker_str = url_split[3]
|
||||||
coin_type = tickerToCoinId(ticker_str)
|
coin_type = getCoinIdFromTicker(ticker_str)
|
||||||
|
|
||||||
if len(url_split) > 4:
|
if len(url_split) > 4:
|
||||||
cmd = url_split[4]
|
cmd = url_split[4]
|
||||||
@@ -820,7 +823,7 @@ def js_validateamount(self, url_split, post_string: str, is_json: bool) -> bytes
|
|||||||
f"Unknown rounding method, must be one of {valid_round_methods}"
|
f"Unknown rounding method, must be one of {valid_round_methods}"
|
||||||
)
|
)
|
||||||
|
|
||||||
coin_type = tickerToCoinId(ticker_str)
|
coin_type = getCoinIdFromTicker(ticker_str)
|
||||||
ci = swap_client.ci(coin_type)
|
ci = swap_client.ci(coin_type)
|
||||||
|
|
||||||
r = 0
|
r = 0
|
||||||
@@ -951,7 +954,7 @@ def js_404(self, url_split, post_string, is_json) -> bytes:
|
|||||||
def js_help(self, url_split, post_string, is_json) -> bytes:
|
def js_help(self, url_split, post_string, is_json) -> bytes:
|
||||||
# TODO: Add details and examples
|
# TODO: Add details and examples
|
||||||
commands = []
|
commands = []
|
||||||
for k in pages:
|
for k in endpoints:
|
||||||
commands.append(k)
|
commands.append(k)
|
||||||
return bytes(json.dumps({"commands": commands}), "UTF-8")
|
return bytes(json.dumps({"commands": commands}), "UTF-8")
|
||||||
|
|
||||||
@@ -959,22 +962,22 @@ def js_help(self, url_split, post_string, is_json) -> bytes:
|
|||||||
def js_readurl(self, url_split, post_string, is_json) -> bytes:
|
def js_readurl(self, url_split, post_string, is_json) -> bytes:
|
||||||
swap_client = self.server.swap_client
|
swap_client = self.server.swap_client
|
||||||
post_data = {} if post_string == "" else getFormData(post_string, is_json)
|
post_data = {} if post_string == "" else getFormData(post_string, is_json)
|
||||||
if have_data_entry(post_data, "url"):
|
if not have_data_entry(post_data, "url"):
|
||||||
url = get_data_entry(post_data, "url")
|
raise ValueError("Requires URL.")
|
||||||
default_headers = {
|
url = get_data_entry(post_data, "url")
|
||||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
default_headers = {
|
||||||
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||||
"Accept-Language": "en-US,en;q=0.5",
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
|
||||||
}
|
"Accept-Language": "en-US,en;q=0.5",
|
||||||
response = swap_client.readURL(url, headers=default_headers)
|
}
|
||||||
try:
|
response = swap_client.readURL(url, headers=default_headers)
|
||||||
error = json.loads(response.decode())
|
try:
|
||||||
if "Error" in error:
|
error = json.loads(response.decode())
|
||||||
return json.dumps({"Error": error["Error"]}).encode()
|
if "Error" in error:
|
||||||
except json.JSONDecodeError:
|
return json.dumps({"Error": error["Error"]}).encode()
|
||||||
pass
|
except json.JSONDecodeError:
|
||||||
return response
|
pass
|
||||||
raise ValueError("Requires URL.")
|
return response
|
||||||
|
|
||||||
|
|
||||||
def js_active(self, url_split, post_string, is_json) -> bytes:
|
def js_active(self, url_split, post_string, is_json) -> bytes:
|
||||||
@@ -1035,7 +1038,62 @@ def js_active(self, url_split, post_string, is_json) -> bytes:
|
|||||||
return bytes(json.dumps(all_bids), "UTF-8")
|
return bytes(json.dumps(all_bids), "UTF-8")
|
||||||
|
|
||||||
|
|
||||||
pages = {
|
def js_coinprices(self, url_split, post_string, is_json) -> bytes:
|
||||||
|
swap_client = self.server.swap_client
|
||||||
|
post_data = {} if post_string == "" else getFormData(post_string, is_json)
|
||||||
|
if not have_data_entry(post_data, "coins"):
|
||||||
|
raise ValueError("Requires coins list.")
|
||||||
|
|
||||||
|
currency_to = Fiat.USD
|
||||||
|
if have_data_entry(post_data, "currency_to"):
|
||||||
|
currency_to = fiatFromTicker(get_data_entry(post_data, "currency_to"))
|
||||||
|
|
||||||
|
rate_source: str = "coingecko.com"
|
||||||
|
if have_data_entry(post_data, "source"):
|
||||||
|
rate_source = get_data_entry(post_data, "source")
|
||||||
|
|
||||||
|
match_input_key: bool = toBool(
|
||||||
|
get_data_entry_or(post_data, "match_input_key", "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
coins = get_data_entry(post_data, "coins")
|
||||||
|
coins_list = coins.split(",")
|
||||||
|
coin_ids = []
|
||||||
|
input_id_map = {}
|
||||||
|
for coin in coins_list:
|
||||||
|
if coin.isdigit():
|
||||||
|
try:
|
||||||
|
coin_id = Coins(int(coin))
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(f"Unknown coin type {coin}")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
coin_id = getCoinIdFromTicker(coin)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
coin_id = getCoinIdFromName(coin)
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(f"Unknown coin type {coin}")
|
||||||
|
coin_ids.append(coin_id)
|
||||||
|
input_id_map[coin_id] = coin
|
||||||
|
|
||||||
|
coinprices = swap_client.lookupFiatRates(
|
||||||
|
coin_ids, currency_to=currency_to, rate_source=rate_source
|
||||||
|
)
|
||||||
|
|
||||||
|
rv = {}
|
||||||
|
for k, v in coinprices.items():
|
||||||
|
if match_input_key:
|
||||||
|
rv[input_id_map[k]] = v
|
||||||
|
else:
|
||||||
|
rv[int(k)] = v
|
||||||
|
return bytes(
|
||||||
|
json.dumps({"currency": currency_to.name, "source": rate_source, "rates": rv}),
|
||||||
|
"UTF-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
endpoints = {
|
||||||
"coins": js_coins,
|
"coins": js_coins,
|
||||||
"wallets": js_wallets,
|
"wallets": js_wallets,
|
||||||
"offers": js_offers,
|
"offers": js_offers,
|
||||||
@@ -1061,10 +1119,11 @@ pages = {
|
|||||||
"help": js_help,
|
"help": js_help,
|
||||||
"readurl": js_readurl,
|
"readurl": js_readurl,
|
||||||
"active": js_active,
|
"active": js_active,
|
||||||
|
"coinprices": js_coinprices,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def js_url_to_function(url_split):
|
def js_url_to_function(url_split):
|
||||||
if len(url_split) > 2:
|
if len(url_split) > 2:
|
||||||
return pages.get(url_split[2], js_404)
|
return endpoints.get(url_split[2], js_404)
|
||||||
return js_index
|
return js_index
|
||||||
|
|||||||
@@ -877,29 +877,30 @@ const coinNameToSymbol = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getUsdValue = (cryptoValue, coinSymbol) => {
|
const getUsdValue = (cryptoValue, coinSymbol) => {
|
||||||
|
let source = "cryptocompare.com";
|
||||||
|
let coin_id = coinSymbol;
|
||||||
if (coinSymbol === 'WOW') {
|
if (coinSymbol === 'WOW') {
|
||||||
return fetch(`https://api.coingecko.com/api/v3/simple/price?ids=wownero&vs_currencies=usd`)
|
source = "coingecko.com"
|
||||||
.then(response => response.json())
|
coin_id = "wownero"
|
||||||
.then(data => {
|
|
||||||
const exchangeRate = data.wownero.usd;
|
|
||||||
if (!isNaN(exchangeRate)) {
|
|
||||||
return cryptoValue * exchangeRate;
|
|
||||||
} else {
|
|
||||||
throw new Error(`Invalid exchange rate for ${coinSymbol}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return fetch(`https://min-api.cryptocompare.com/data/price?fsym=${coinSymbol}&tsyms=USD`)
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
const exchangeRate = data.USD;
|
|
||||||
if (!isNaN(exchangeRate)) {
|
|
||||||
return cryptoValue * exchangeRate;
|
|
||||||
} else {
|
|
||||||
throw new Error(`Invalid exchange rate for ${coinSymbol}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fetch("/json/coinprices", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {'Content-Type': 'application/json'},
|
||||||
|
body: JSON.stringify({
|
||||||
|
coins: coin_id,
|
||||||
|
source: source
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const exchangeRate = data.rates[coin_id];
|
||||||
|
if (!isNaN(exchangeRate)) {
|
||||||
|
return cryptoValue * exchangeRate;
|
||||||
|
} else {
|
||||||
|
throw new Error(`Invalid exchange rate for ${coinSymbol}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateUsdValue = async (cryptoCell, coinFullName, usdValueSpan) => {
|
const updateUsdValue = async (cryptoCell, coinFullName, usdValueSpan) => {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ from .util import (
|
|||||||
get_data_entry_or,
|
get_data_entry_or,
|
||||||
have_data_entry,
|
have_data_entry,
|
||||||
inputAmount,
|
inputAmount,
|
||||||
known_chart_coins,
|
|
||||||
listAvailableCoins,
|
listAvailableCoins,
|
||||||
PAGE_LIMIT,
|
PAGE_LIMIT,
|
||||||
setCoinFilter,
|
setCoinFilter,
|
||||||
@@ -32,6 +31,7 @@ from basicswap.basicswap_util import (
|
|||||||
SwapTypes,
|
SwapTypes,
|
||||||
DebugTypes,
|
DebugTypes,
|
||||||
getLockName,
|
getLockName,
|
||||||
|
get_api_key_setting,
|
||||||
strBidState,
|
strBidState,
|
||||||
strSwapDesc,
|
strSwapDesc,
|
||||||
strSwapType,
|
strSwapType,
|
||||||
@@ -40,12 +40,12 @@ from basicswap.basicswap_util import (
|
|||||||
)
|
)
|
||||||
from basicswap.chainparams import (
|
from basicswap.chainparams import (
|
||||||
Coins,
|
Coins,
|
||||||
|
ticker_map,
|
||||||
)
|
)
|
||||||
|
from basicswap.explorers import (
|
||||||
default_chart_api_key = (
|
default_chart_api_key,
|
||||||
"95dd900af910656e0e17c41f2ddc5dba77d01bf8b0e7d2787634a16bd976c553"
|
default_coingecko_api_key,
|
||||||
)
|
)
|
||||||
default_coingecko_api_key = "CG-8hm3r9iLfpEXv4ied8oLbeUj"
|
|
||||||
|
|
||||||
|
|
||||||
def value_or_none(v):
|
def value_or_none(v):
|
||||||
@@ -973,30 +973,20 @@ def page_offers(self, url_split, post_string, sent=False):
|
|||||||
|
|
||||||
coins_from, coins_to = listAvailableCoins(swap_client, split_from=True)
|
coins_from, coins_to = listAvailableCoins(swap_client, split_from=True)
|
||||||
|
|
||||||
chart_api_key = swap_client.settings.get("chart_api_key", "")
|
chart_api_key = get_api_key_setting(
|
||||||
if chart_api_key == "":
|
swap_client.settings, "chart_api_key", default_chart_api_key
|
||||||
chart_api_key_enc = swap_client.settings.get("chart_api_key_enc", "")
|
)
|
||||||
chart_api_key = (
|
coingecko_api_key = get_api_key_setting(
|
||||||
default_chart_api_key
|
swap_client.settings, "coingecko_api_key", default_coingecko_api_key
|
||||||
if chart_api_key_enc == ""
|
)
|
||||||
else bytes.fromhex(chart_api_key_enc).decode("utf-8")
|
|
||||||
)
|
|
||||||
|
|
||||||
coingecko_api_key = swap_client.settings.get("coingecko_api_key", "")
|
|
||||||
if coingecko_api_key == "":
|
|
||||||
coingecko_api_key_enc = swap_client.settings.get("coingecko_api_key_enc", "")
|
|
||||||
coingecko_api_key = (
|
|
||||||
default_coingecko_api_key
|
|
||||||
if coingecko_api_key_enc == ""
|
|
||||||
else bytes.fromhex(coingecko_api_key_enc).decode("utf-8")
|
|
||||||
)
|
|
||||||
|
|
||||||
offers_count = len(formatted_offers)
|
offers_count = len(formatted_offers)
|
||||||
|
|
||||||
enabled_chart_coins = []
|
enabled_chart_coins = []
|
||||||
enabled_chart_coins_setting = swap_client.settings.get("enabled_chart_coins", "")
|
enabled_chart_coins_setting = swap_client.settings.get("enabled_chart_coins", "")
|
||||||
if enabled_chart_coins_setting.lower() == "all":
|
if enabled_chart_coins_setting.lower() == "all":
|
||||||
enabled_chart_coins = known_chart_coins
|
for coin_ticker in ticker_map:
|
||||||
|
enabled_chart_coins.append(coin_ticker.upper())
|
||||||
elif enabled_chart_coins_setting.strip() == "":
|
elif enabled_chart_coins_setting.strip() == "":
|
||||||
for coin_id in swap_client.coin_clients:
|
for coin_id in swap_client.coin_clients:
|
||||||
if not swap_client.isCoinActive(coin_id):
|
if not swap_client.isCoinActive(coin_id):
|
||||||
@@ -1007,7 +997,7 @@ def page_offers(self, url_split, post_string, sent=False):
|
|||||||
continue
|
continue
|
||||||
if (
|
if (
|
||||||
enabled_ticker not in enabled_chart_coins
|
enabled_ticker not in enabled_chart_coins
|
||||||
and enabled_ticker in known_chart_coins
|
and enabled_ticker.lower() in ticker_map
|
||||||
):
|
):
|
||||||
enabled_chart_coins.append(enabled_ticker)
|
enabled_chart_coins.append(enabled_ticker)
|
||||||
else:
|
else:
|
||||||
@@ -1016,7 +1006,7 @@ def page_offers(self, url_split, post_string, sent=False):
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
upcased_ticker not in enabled_chart_coins
|
upcased_ticker not in enabled_chart_coins
|
||||||
and upcased_ticker in known_chart_coins
|
and upcased_ticker.lower() in ticker_map
|
||||||
):
|
):
|
||||||
enabled_chart_coins.append(upcased_ticker)
|
enabled_chart_coins.append(upcased_ticker)
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ from basicswap.util import (
|
|||||||
toBool,
|
toBool,
|
||||||
InactiveCoin,
|
InactiveCoin,
|
||||||
)
|
)
|
||||||
|
from basicswap.basicswap_util import (
|
||||||
|
get_api_key_setting,
|
||||||
|
)
|
||||||
from basicswap.chainparams import (
|
from basicswap.chainparams import (
|
||||||
Coins,
|
Coins,
|
||||||
)
|
)
|
||||||
@@ -168,23 +171,13 @@ def page_settings(self, url_split, post_string):
|
|||||||
"debug_ui": swap_client.debug_ui,
|
"debug_ui": swap_client.debug_ui,
|
||||||
"expire_db_records": swap_client._expire_db_records,
|
"expire_db_records": swap_client._expire_db_records,
|
||||||
}
|
}
|
||||||
if "chart_api_key_enc" in swap_client.settings:
|
|
||||||
chart_api_key = html.escape(
|
|
||||||
bytes.fromhex(swap_client.settings.get("chart_api_key_enc", "")).decode(
|
|
||||||
"utf-8"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
chart_api_key = swap_client.settings.get("chart_api_key", "")
|
|
||||||
|
|
||||||
if "coingecko_api_key_enc" in swap_client.settings:
|
chart_api_key = get_api_key_setting(
|
||||||
coingecko_api_key = html.escape(
|
swap_client.settings, "chart_api_key", escape=True
|
||||||
bytes.fromhex(swap_client.settings.get("coingecko_api_key_enc", "")).decode(
|
)
|
||||||
"utf-8"
|
coingecko_api_key = get_api_key_setting(
|
||||||
)
|
swap_client.settings, "coingecko_api_key", escape=True
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
coingecko_api_key = swap_client.settings.get("coingecko_api_key", "")
|
|
||||||
|
|
||||||
chart_settings = {
|
chart_settings = {
|
||||||
"show_chart": swap_client.settings.get("show_chart", True),
|
"show_chart": swap_client.settings.get("show_chart", True),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
# Copyright (c) 2024 The Basicswap developers
|
# Copyright (c) 2024-2025 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -15,6 +15,7 @@ from basicswap.util import (
|
|||||||
from basicswap.chainparams import (
|
from basicswap.chainparams import (
|
||||||
Coins,
|
Coins,
|
||||||
chainparams,
|
chainparams,
|
||||||
|
getCoinIdFromTicker,
|
||||||
)
|
)
|
||||||
from basicswap.basicswap_util import (
|
from basicswap.basicswap_util import (
|
||||||
ActionTypes,
|
ActionTypes,
|
||||||
@@ -34,30 +35,6 @@ from basicswap.basicswap_util import (
|
|||||||
from basicswap.protocols.xmr_swap_1 import getChainBSplitKey, getChainBRemoteSplitKey
|
from basicswap.protocols.xmr_swap_1 import getChainBSplitKey, getChainBRemoteSplitKey
|
||||||
|
|
||||||
PAGE_LIMIT = 1000
|
PAGE_LIMIT = 1000
|
||||||
invalid_coins_from = []
|
|
||||||
known_chart_coins = [
|
|
||||||
"BTC",
|
|
||||||
"PART",
|
|
||||||
"XMR",
|
|
||||||
"LTC",
|
|
||||||
"FIRO",
|
|
||||||
"DASH",
|
|
||||||
"PIVX",
|
|
||||||
"DOGE",
|
|
||||||
"ETH",
|
|
||||||
"DCR",
|
|
||||||
"ZANO",
|
|
||||||
"WOW",
|
|
||||||
"BCH",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def tickerToCoinId(ticker):
|
|
||||||
search_str = ticker.upper()
|
|
||||||
for c in Coins:
|
|
||||||
if c.name == search_str:
|
|
||||||
return c.value
|
|
||||||
raise ValueError("Unknown coin")
|
|
||||||
|
|
||||||
|
|
||||||
def getCoinType(coin_type_ind):
|
def getCoinType(coin_type_ind):
|
||||||
@@ -65,7 +42,7 @@ def getCoinType(coin_type_ind):
|
|||||||
try:
|
try:
|
||||||
return int(coin_type_ind)
|
return int(coin_type_ind)
|
||||||
except Exception:
|
except Exception:
|
||||||
return tickerToCoinId(coin_type_ind)
|
return getCoinIdFromTicker(coin_type_ind)
|
||||||
|
|
||||||
|
|
||||||
def validateAmountString(amount, ci):
|
def validateAmountString(amount, ci):
|
||||||
@@ -667,12 +644,12 @@ def listAvailableCoins(swap_client, with_variants=True, split_from=False):
|
|||||||
continue
|
continue
|
||||||
if v["connection_type"] == "rpc":
|
if v["connection_type"] == "rpc":
|
||||||
coins.append((int(k), getCoinName(k)))
|
coins.append((int(k), getCoinName(k)))
|
||||||
if split_from and k not in invalid_coins_from:
|
if split_from:
|
||||||
coins_from.append(coins[-1])
|
coins_from.append(coins[-1])
|
||||||
if with_variants and k == Coins.PART:
|
if with_variants and k == Coins.PART:
|
||||||
for v in (Coins.PART_ANON, Coins.PART_BLIND):
|
for v in (Coins.PART_ANON, Coins.PART_BLIND):
|
||||||
coins.append((int(v), getCoinName(v)))
|
coins.append((int(v), getCoinName(v)))
|
||||||
if split_from and v not in invalid_coins_from:
|
if split_from:
|
||||||
coins_from.append(coins[-1])
|
coins_from.append(coins[-1])
|
||||||
if with_variants and k == Coins.LTC:
|
if with_variants and k == Coins.LTC:
|
||||||
for v in (Coins.LTC_MWEB,):
|
for v in (Coins.LTC_MWEB,):
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ python tests/basicswap/extended/test_xmr_persistent.py
|
|||||||
|
|
||||||
|
|
||||||
# Copy coin releases to permanent storage for faster subsequent startups
|
# Copy coin releases to permanent storage for faster subsequent startups
|
||||||
cp -r ${TEST_PATH}/bin/ ~/tmp/basicswap_bin/
|
cp -r ${TEST_PATH}/bin/* ~/tmp/basicswap_bin/
|
||||||
|
|
||||||
|
|
||||||
# Continue existing chains with
|
# Continue existing chains with
|
||||||
|
|||||||
Reference in New Issue
Block a user