Compare commits
193 Commits
v0.14.2
...
cryptoguar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
631ccea626 | ||
|
|
a5c3c692a0 | ||
|
|
b2df4ea80d | ||
|
|
18a7105f20 | ||
|
|
fcdb2e7dfe | ||
|
|
3c5e8481cd | ||
|
|
97bb615176 | ||
|
|
f1c2b41714 | ||
|
|
8d317e4b67 | ||
|
|
45ed2cdb87 | ||
|
|
d64e3f4be9 | ||
|
|
57d885bc0c | ||
|
|
205c6e2b58 | ||
|
|
d95f3ccd24 | ||
|
|
d6a9425b22 | ||
|
|
e4cc5da490 | ||
|
|
e177d36bd4 | ||
|
|
05ffa5e3ac | ||
|
|
6165cbc4c3 | ||
|
|
7e6f94319d | ||
|
|
71fd3d10aa | ||
|
|
e4ed9aebdf | ||
|
|
b97a9f4a27 | ||
|
|
510eff6163 | ||
|
|
efb84f58af | ||
|
|
831ef40977 | ||
|
|
a0456cb689 | ||
|
|
c7818f5fac | ||
|
|
713577d868 | ||
|
|
37be3bcab5 | ||
|
|
4ae97790aa | ||
|
|
8928451af0 | ||
|
|
473e4fd400 | ||
|
|
ff2fc35f72 | ||
|
|
edb3b19dcf | ||
|
|
aac2f51b88 | ||
|
|
57b96cd985 | ||
|
|
4d5551cd84 | ||
|
|
7ee4720738 | ||
|
|
c76fe79848 | ||
|
|
f13c481b51 | ||
|
|
6f776971b1 | ||
|
|
c79ed493aa | ||
|
|
b6709d0cdc | ||
|
|
c945e267e7 | ||
|
|
ef65420978 | ||
|
|
6da4bf6aaf | ||
|
|
6e56b7f421 | ||
|
|
f084c6f538 | ||
|
|
443bd6917f | ||
|
|
b55d126a0a | ||
|
|
586ff3288f | ||
|
|
0398fce5a8 | ||
|
|
ef082ff7be | ||
|
|
168284ce25 | ||
|
|
e797e23625 | ||
|
|
d3fcdc8052 | ||
|
|
2c176a8c86 | ||
|
|
e92d5560af | ||
|
|
0171ad6889 | ||
|
|
5d381d4b73 | ||
|
|
9e24d9a12a | ||
|
|
21ef6f3129 | ||
|
|
67d808cbe4 | ||
|
|
5d1bed6423 | ||
|
|
edc11b4c96 | ||
|
|
5daf591985 | ||
|
|
aee66712b8 | ||
|
|
8de365f9d3 | ||
|
|
765ef9571a | ||
|
|
c575625097 | ||
|
|
fe02441619 | ||
|
|
c992ef571a | ||
|
|
5f275132de | ||
|
|
64151f4203 | ||
|
|
734214af53 | ||
|
|
1cb8ffb632 | ||
|
|
40d06df325 | ||
|
|
62031173f5 | ||
|
|
f473d66de5 | ||
|
|
e548cf2b3b | ||
|
|
d1baf4bc10 | ||
|
|
3b8e084b2e | ||
|
|
0a697c61e8 | ||
|
|
5af59dd8da | ||
|
|
a75cd28995 | ||
|
|
f40d98ef23 | ||
|
|
b14fba0e1f | ||
|
|
4d928dc98e | ||
|
|
1845f802a2 | ||
|
|
7ec9dfa35a | ||
|
|
b70e46ffc1 | ||
|
|
07de2d61af | ||
|
|
65fbcda556 | ||
|
|
40f334ed0e | ||
|
|
77bb3e6353 | ||
|
|
3b60472c04 | ||
|
|
b87e034719 | ||
|
|
def7aae1ec | ||
|
|
294595adbd | ||
|
|
ab04f27497 | ||
|
|
159974d414 | ||
|
|
110b91bb75 | ||
|
|
3cea5449c9 | ||
|
|
07ed0af468 | ||
|
|
feabc619ae | ||
|
|
e3f7b5b79b | ||
|
|
35bede48b0 | ||
|
|
af6154705c | ||
|
|
f010fc0c83 | ||
|
|
3da9221d43 | ||
|
|
a7f0f257b8 | ||
|
|
c095e22fdb | ||
|
|
0c98dff044 | ||
|
|
69cc56e4a7 | ||
|
|
a54e6daaa1 | ||
|
|
3009cacdb2 | ||
|
|
b9bacb9988 | ||
|
|
6905c6a131 | ||
|
|
ce7b94a878 | ||
|
|
c49cdb2e98 | ||
|
|
0ae4651a78 | ||
|
|
12d24800b8 | ||
|
|
c09eab71cc | ||
|
|
5bbafbdb3c | ||
|
|
157b63a5d0 | ||
|
|
341d39a6a3 | ||
|
|
bb8dad1607 | ||
|
|
20bcef1891 | ||
|
|
f9bf29e68c | ||
|
|
820e5af5fb | ||
|
|
681122bcca | ||
|
|
9418ea4385 | ||
|
|
73ab5e7391 | ||
|
|
bf6d07a726 | ||
|
|
e4849d6dfe | ||
|
|
2002fcb31b | ||
|
|
21c828051c | ||
|
|
c7e84e2249 | ||
|
|
a3645c286d | ||
|
|
618df98abf | ||
|
|
4bbf739786 | ||
|
|
878a145420 | ||
|
|
32bd44b19a | ||
|
|
c5ced6994a | ||
|
|
2929e74c78 | ||
|
|
0c01dcf2f5 | ||
|
|
9eacd35319 | ||
|
|
ca6af04eba | ||
|
|
691e3f1b82 | ||
|
|
80dbbd3d12 | ||
|
|
28d99c4c0f | ||
|
|
3f8012f0d0 | ||
|
|
a53de511ce | ||
|
|
34eb5900fb | ||
|
|
514f7efc6e | ||
|
|
de81ec5d75 | ||
|
|
4b23834af8 | ||
|
|
0e2be676db | ||
|
|
3be72b3c71 | ||
|
|
889ffaaa33 | ||
|
|
50515568d8 | ||
|
|
56f96291e4 | ||
|
|
f5db8cf7ce | ||
|
|
ea91647862 | ||
|
|
d7a5467f4f | ||
|
|
95db6655e7 | ||
|
|
36ec1e8683 | ||
|
|
1797db97a0 | ||
|
|
10964f0f51 | ||
|
|
d2733b704d | ||
|
|
b1401ee00b | ||
|
|
e71589a292 | ||
|
|
54f56e0e2c | ||
|
|
73543a5477 | ||
|
|
7ad92b1bbd | ||
|
|
a1e2592965 | ||
|
|
ff29100fd4 | ||
|
|
059356ccd8 | ||
|
|
5d0c7d28e4 | ||
|
|
75d0ca926f | ||
|
|
8582dc479b | ||
|
|
b7383d99dc | ||
|
|
d88f5728a4 | ||
|
|
6d66ee8653 | ||
|
|
ec21ea05bf | ||
|
|
bba517c8b7 | ||
|
|
ebcc4ccb06 | ||
|
|
656335b541 | ||
|
|
e39613f49d | ||
|
|
706d251ef4 | ||
|
|
80e17c739e | ||
|
|
69ca41c68d |
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "pip" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
open-pull-requests-limit: 20
|
||||||
|
target-branch: "dev"
|
||||||
17
.github/workflows/ci.yml
vendored
@@ -30,6 +30,9 @@ jobs:
|
|||||||
- name: Install
|
- name: Install
|
||||||
run: |
|
run: |
|
||||||
pip install .
|
pip install .
|
||||||
|
# Print the core versions to a file for caching
|
||||||
|
basicswap-prepare --version --withcoins=bitcoin | tail -n +2 > core_versions.txt
|
||||||
|
cat core_versions.txt
|
||||||
- name: Running flake8
|
- name: Running flake8
|
||||||
run: |
|
run: |
|
||||||
flake8 --ignore=E203,E501,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,.eggs,.tox,bin/install_certifi.py
|
flake8 --ignore=E203,E501,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,.eggs,.tox,bin/install_certifi.py
|
||||||
@@ -44,15 +47,21 @@ jobs:
|
|||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
env:
|
env:
|
||||||
cache-name: cache-cores
|
cache-name: cache-cores
|
||||||
CACHE_KEY: $(printf $(python bin/basicswap-prepare.py --version --withcoins=bitcoin) | sha256sum | head -c 64)
|
|
||||||
with:
|
with:
|
||||||
path: $BIN_DIR
|
path: /tmp/cached_bin
|
||||||
key: $CACHE_KEY
|
key: cores-${{ runner.os }}-${{ hashFiles('**/core_versions.txt') }}
|
||||||
|
|
||||||
- if: ${{ steps.cache-yarn.outputs.cache-hit != 'true' }}
|
- if: ${{ steps.cache-cores.outputs.cache-hit != 'true' }}
|
||||||
name: Running basicswap-prepare
|
name: Running basicswap-prepare
|
||||||
run: |
|
run: |
|
||||||
basicswap-prepare --bindir="$BIN_DIR" --preparebinonly --withcoins=particl,bitcoin,monero
|
basicswap-prepare --bindir="$BIN_DIR" --preparebinonly --withcoins=particl,bitcoin,monero
|
||||||
|
- name: Running test_xmr
|
||||||
|
run: |
|
||||||
|
export PYTHONPATH=$(pwd)
|
||||||
|
export PARTICL_BINDIR="$BIN_DIR/particl"
|
||||||
|
export BITCOIN_BINDIR="$BIN_DIR/bitcoin"
|
||||||
|
export XMR_BINDIR="$BIN_DIR/monero"
|
||||||
|
pytest tests/basicswap/test_btc_xmr.py::TestBTC -k "test_003_api or test_02_a_leader_recover_a_lock_tx"
|
||||||
- name: Running test_encrypted_xmr_reload
|
- name: Running test_encrypted_xmr_reload
|
||||||
run: |
|
run: |
|
||||||
export PYTHONPATH=$(pwd)
|
export PYTHONPATH=$(pwd)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ ENV LANG=C.UTF-8 \
|
|||||||
|
|
||||||
RUN apt-get update; \
|
RUN apt-get update; \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
python3-pip libpython3-dev gnupg pkg-config gcc libc-dev gosu tzdata;
|
python3-pip libpython3-dev gnupg pkg-config gcc libc-dev gosu tzdata cmake ninja-build;
|
||||||
|
|
||||||
# Install requirements first so as to skip in subsequent rebuilds
|
# Install requirements first so as to skip in subsequent rebuilds
|
||||||
COPY ./requirements.txt requirements.txt
|
COPY ./requirements.txt requirements.txt
|
||||||
|
|||||||
18
README.md
@@ -64,6 +64,12 @@ BasicSwap is compatible with the following digital assets.
|
|||||||
<td>XMR
|
<td>XMR
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Bitcoin Cash
|
||||||
|
</td>
|
||||||
|
<td>BCH
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Dash
|
<td>Dash
|
||||||
</td>
|
</td>
|
||||||
@@ -106,6 +112,18 @@ BasicSwap is compatible with the following digital assets.
|
|||||||
<td>PART
|
<td>PART
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Dogecoin
|
||||||
|
</td>
|
||||||
|
<td>DOGE
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Namecoin
|
||||||
|
</td>
|
||||||
|
<td>NMC
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
If you’d like to add a cryptocurrency to BasicSwap, refer to how other cryptocurrencies have been integrated to the DEX by following [this link](https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_apply.html).
|
If you’d like to add a cryptocurrency to BasicSwap, refer to how other cryptocurrencies have been integrated to the DEX by following [this link](https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_apply.html).
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
name = "basicswap"
|
name = "basicswap"
|
||||||
|
|
||||||
__version__ = "0.14.2"
|
__version__ = "0.14.3"
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
@@ -28,6 +28,9 @@ from .rpc import (
|
|||||||
from .util import (
|
from .util import (
|
||||||
TemporaryError,
|
TemporaryError,
|
||||||
)
|
)
|
||||||
|
from .util.logging import (
|
||||||
|
BSXLogger,
|
||||||
|
)
|
||||||
from .chainparams import (
|
from .chainparams import (
|
||||||
Coins,
|
Coins,
|
||||||
chainparams,
|
chainparams,
|
||||||
@@ -75,6 +78,7 @@ class BaseApp(DBMethods):
|
|||||||
self.delay_event.set()
|
self.delay_event.set()
|
||||||
|
|
||||||
def prepareLogging(self):
|
def prepareLogging(self):
|
||||||
|
logging.setLoggerClass(BSXLogger)
|
||||||
self.log = logging.getLogger(self.log_name)
|
self.log = logging.getLogger(self.log_name)
|
||||||
self.log.propagate = False
|
self.log.propagate = False
|
||||||
|
|
||||||
|
|||||||
567
basicswap/bin/prepare.py
Normal file → Executable file
@@ -2,32 +2,33 @@
|
|||||||
# -*- 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.
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import signal
|
import signal
|
||||||
import logging
|
|
||||||
import traceback
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
|
||||||
import basicswap.config as cfg
|
import basicswap.config as cfg
|
||||||
from basicswap import __version__
|
from basicswap import __version__
|
||||||
from basicswap.ui.util import getCoinName
|
from basicswap.ui.util import getCoinName
|
||||||
from basicswap.basicswap import BasicSwap
|
from basicswap.basicswap import BasicSwap
|
||||||
from basicswap.chainparams import chainparams
|
from basicswap.chainparams import chainparams, Coins
|
||||||
from basicswap.http_server import HttpThread
|
from basicswap.http_server import HttpThread
|
||||||
from basicswap.contrib.websocket_server import WebsocketServer
|
from basicswap.contrib.websocket_server import WebsocketServer
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger()
|
initial_logger = logging.getLogger()
|
||||||
logger.level = logging.DEBUG
|
initial_logger.level = logging.DEBUG
|
||||||
if not len(logger.handlers):
|
if not len(initial_logger.handlers):
|
||||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
initial_logger.addHandler(initial_logger.StreamHandler(sys.stdout))
|
||||||
|
logger = initial_logger
|
||||||
|
|
||||||
swap_client = None
|
swap_client = None
|
||||||
|
|
||||||
@@ -48,9 +49,10 @@ def is_known_coin(coin_name: str) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def signal_handler(sig, frame):
|
def signal_handler(sig, frame):
|
||||||
global swap_client
|
os.write(
|
||||||
logger.info("Signal %d detected, ending program." % (sig))
|
sys.stdout.fileno(), f"Signal {sig} detected, ending program.\n".encode("utf-8")
|
||||||
if swap_client is not None:
|
)
|
||||||
|
if swap_client is not None and not swap_client.chainstate_delay_event.is_set():
|
||||||
swap_client.stopRunning()
|
swap_client.stopRunning()
|
||||||
|
|
||||||
|
|
||||||
@@ -58,23 +60,29 @@ def startDaemon(node_dir, bin_dir, daemon_bin, opts=[], extra_config={}):
|
|||||||
daemon_bin = os.path.expanduser(os.path.join(bin_dir, daemon_bin))
|
daemon_bin = os.path.expanduser(os.path.join(bin_dir, daemon_bin))
|
||||||
datadir_path = os.path.expanduser(node_dir)
|
datadir_path = os.path.expanduser(node_dir)
|
||||||
|
|
||||||
# Rewrite litecoin.conf for 0.21.3
|
# Rewrite litecoin.conf
|
||||||
# TODO: Remove
|
# TODO: Remove
|
||||||
|
needs_rewrite: bool = False
|
||||||
ltc_conf_path = os.path.join(datadir_path, "litecoin.conf")
|
ltc_conf_path = os.path.join(datadir_path, "litecoin.conf")
|
||||||
if os.path.exists(ltc_conf_path):
|
if os.path.exists(ltc_conf_path):
|
||||||
config_to_add = ["blockfilterindex=0", "peerblockfilters=0"]
|
|
||||||
with open(ltc_conf_path) as fp:
|
with open(ltc_conf_path) as fp:
|
||||||
for line in fp:
|
for line in fp:
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line in config_to_add:
|
if line.endswith("=onion"):
|
||||||
config_to_add.remove(line)
|
needs_rewrite = True
|
||||||
|
break
|
||||||
if len(config_to_add) > 0:
|
if needs_rewrite:
|
||||||
logger.info("Rewriting litecoin.conf")
|
logger.info("Rewriting litecoin.conf")
|
||||||
shutil.copyfile(ltc_conf_path, ltc_conf_path + ".last")
|
shutil.copyfile(ltc_conf_path, ltc_conf_path + ".last")
|
||||||
with open(ltc_conf_path, "a") as fp:
|
with (
|
||||||
for line in config_to_add:
|
open(ltc_conf_path + ".last") as fp_from,
|
||||||
fp.write(line + "\n")
|
open(ltc_conf_path, "w") as fp_to,
|
||||||
|
):
|
||||||
|
for line in fp_from:
|
||||||
|
if line.strip().endswith("=onion"):
|
||||||
|
fp_to.write(line.strip()[:-6] + "\n")
|
||||||
|
else:
|
||||||
|
fp_to.write(line)
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
daemon_bin,
|
daemon_bin,
|
||||||
@@ -241,12 +249,25 @@ def getWalletBinName(coin_id: int, coin_settings, default_name: str) -> str:
|
|||||||
) + (".exe" if os.name == "nt" else "")
|
) + (".exe" if os.name == "nt" else "")
|
||||||
|
|
||||||
|
|
||||||
def getCoreBinArgs(coin_id: int, coin_settings):
|
def getCoreBinArgs(coin_id: int, coin_settings, prepare=False, use_tor_proxy=False):
|
||||||
extra_args = []
|
extra_args = []
|
||||||
if "config_filename" in coin_settings:
|
if "config_filename" in coin_settings:
|
||||||
extra_args.append("--conf=" + coin_settings["config_filename"])
|
extra_args.append("--conf=" + coin_settings["config_filename"])
|
||||||
if "port" in coin_settings:
|
if "port" in coin_settings and coin_id != Coins.BTC:
|
||||||
extra_args.append("--port=" + str(int(coin_settings["port"])))
|
if prepare is False and use_tor_proxy:
|
||||||
|
if coin_id == Coins.BCH:
|
||||||
|
# Without this BCH (27.1) will bind to the default BTC port, even with proxy set
|
||||||
|
extra_args.append("--bind=127.0.0.1:" + str(int(coin_settings["port"])))
|
||||||
|
else:
|
||||||
|
extra_args.append("--port=" + str(int(coin_settings["port"])))
|
||||||
|
|
||||||
|
# BTC versions from v28 fail to start if the onionport is in use.
|
||||||
|
# As BCH may use port 8334, disable it here.
|
||||||
|
# When tor is enabled a bind option for the onionport will be added to bitcoin.conf.
|
||||||
|
# https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-28.0.md?plain=1#L84
|
||||||
|
if prepare is False and use_tor_proxy is False and coin_id == Coins.BTC:
|
||||||
|
port: int = coin_settings.get("port", 8333)
|
||||||
|
extra_args.append(f"--bind=0.0.0.0:{port}")
|
||||||
return extra_args
|
return extra_args
|
||||||
|
|
||||||
|
|
||||||
@@ -421,7 +442,9 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||||||
swap_client.log.info(f"Starting {display_name} daemon")
|
swap_client.log.info(f"Starting {display_name} daemon")
|
||||||
|
|
||||||
filename: str = getCoreBinName(coin_id, v, c + "d")
|
filename: str = getCoreBinName(coin_id, v, c + "d")
|
||||||
extra_opts = getCoreBinArgs(coin_id, v)
|
extra_opts = getCoreBinArgs(
|
||||||
|
coin_id, v, use_tor_proxy=swap_client.use_tor_proxy
|
||||||
|
)
|
||||||
daemons.append(
|
daemons.append(
|
||||||
startDaemon(v["datadir"], v["bindir"], filename, opts=extra_opts)
|
startDaemon(v["datadir"], v["bindir"], filename, opts=extra_opts)
|
||||||
)
|
)
|
||||||
@@ -512,7 +535,7 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||||||
signal.CTRL_C_EVENT if os.name == "nt" else signal.SIGINT
|
signal.CTRL_C_EVENT if os.name == "nt" else signal.SIGINT
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
swap_client.log.info("Interrupting %d, error %s", d.handle.pid, str(e))
|
swap_client.log.info(f"Interrupting {d.handle.pid}, error {e}")
|
||||||
for d in daemons:
|
for d in daemons:
|
||||||
try:
|
try:
|
||||||
d.handle.wait(timeout=120)
|
d.handle.wait(timeout=120)
|
||||||
@@ -520,8 +543,8 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||||||
if fp:
|
if fp:
|
||||||
fp.close()
|
fp.close()
|
||||||
closed_pids.append(d.handle.pid)
|
closed_pids.append(d.handle.pid)
|
||||||
except Exception as ex:
|
except Exception as e:
|
||||||
swap_client.log.error("Error: {}".format(ex))
|
swap_client.log.error(f"Error: {e}")
|
||||||
|
|
||||||
if os.path.exists(pids_path):
|
if os.path.exists(pids_path):
|
||||||
with open(pids_path) as fd:
|
with open(pids_path) as fd:
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2019-2024 tecnovert
|
# Copyright (c) 2019-2024 tecnovert
|
||||||
|
# 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.
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ class Coins(IntEnum):
|
|||||||
LTC_MWEB = 15
|
LTC_MWEB = 15
|
||||||
# ZANO = 16
|
# ZANO = 16
|
||||||
BCH = 17
|
BCH = 17
|
||||||
|
DOGE = 18
|
||||||
|
|
||||||
|
|
||||||
chainparams = {
|
chainparams = {
|
||||||
@@ -89,6 +91,8 @@ chainparams = {
|
|||||||
"bip44": 0,
|
"bip44": 0,
|
||||||
"min_amount": 100000,
|
"min_amount": 100000,
|
||||||
"max_amount": 10000000 * COIN,
|
"max_amount": 10000000 * COIN,
|
||||||
|
"ext_public_key_prefix": 0x0488B21E,
|
||||||
|
"ext_secret_key_prefix": 0x0488ADE4,
|
||||||
},
|
},
|
||||||
"testnet": {
|
"testnet": {
|
||||||
"rpcport": 18332,
|
"rpcport": 18332,
|
||||||
@@ -100,6 +104,8 @@ chainparams = {
|
|||||||
"min_amount": 100000,
|
"min_amount": 100000,
|
||||||
"max_amount": 10000000 * COIN,
|
"max_amount": 10000000 * COIN,
|
||||||
"name": "testnet3",
|
"name": "testnet3",
|
||||||
|
"ext_public_key_prefix": 0x043587CF,
|
||||||
|
"ext_secret_key_prefix": 0x04358394,
|
||||||
},
|
},
|
||||||
"regtest": {
|
"regtest": {
|
||||||
"rpcport": 18443,
|
"rpcport": 18443,
|
||||||
@@ -110,6 +116,8 @@ chainparams = {
|
|||||||
"bip44": 1,
|
"bip44": 1,
|
||||||
"min_amount": 100000,
|
"min_amount": 100000,
|
||||||
"max_amount": 10000000 * COIN,
|
"max_amount": 10000000 * COIN,
|
||||||
|
"ext_public_key_prefix": 0x043587CF,
|
||||||
|
"ext_secret_key_prefix": 0x04358394,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Coins.LTC: {
|
Coins.LTC: {
|
||||||
@@ -153,12 +161,51 @@ chainparams = {
|
|||||||
"max_amount": 10000000 * COIN,
|
"max_amount": 10000000 * COIN,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Coins.DOGE: {
|
||||||
|
"name": "dogecoin",
|
||||||
|
"ticker": "DOGE",
|
||||||
|
"message_magic": "Dogecoin Signed Message:\n",
|
||||||
|
"blocks_target": 60 * 1,
|
||||||
|
"decimal_places": 8,
|
||||||
|
"mainnet": {
|
||||||
|
"rpcport": 22555,
|
||||||
|
"pubkey_address": 30,
|
||||||
|
"script_address": 22,
|
||||||
|
"key_prefix": 158,
|
||||||
|
"hrp": "doge",
|
||||||
|
"bip44": 3,
|
||||||
|
"min_amount": 100000, # TODO increase above fee
|
||||||
|
"max_amount": 10000000 * COIN,
|
||||||
|
},
|
||||||
|
"testnet": {
|
||||||
|
"rpcport": 44555,
|
||||||
|
"pubkey_address": 113,
|
||||||
|
"script_address": 196,
|
||||||
|
"key_prefix": 241,
|
||||||
|
"hrp": "tdge",
|
||||||
|
"bip44": 1,
|
||||||
|
"min_amount": 100000,
|
||||||
|
"max_amount": 10000000 * COIN,
|
||||||
|
"name": "testnet4",
|
||||||
|
},
|
||||||
|
"regtest": {
|
||||||
|
"rpcport": 18332,
|
||||||
|
"pubkey_address": 111,
|
||||||
|
"script_address": 196,
|
||||||
|
"key_prefix": 239,
|
||||||
|
"hrp": "rdge",
|
||||||
|
"bip44": 1,
|
||||||
|
"min_amount": 100000,
|
||||||
|
"max_amount": 10000000 * COIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
Coins.DCR: {
|
Coins.DCR: {
|
||||||
"name": "decred",
|
"name": "decred",
|
||||||
"ticker": "DCR",
|
"ticker": "DCR",
|
||||||
"message_magic": "Decred Signed Message:\n",
|
"message_magic": "Decred Signed Message:\n",
|
||||||
"blocks_target": 60 * 5,
|
"blocks_target": 60 * 5,
|
||||||
"decimal_places": 8,
|
"decimal_places": 8,
|
||||||
|
"has_multiwallet": False,
|
||||||
"mainnet": {
|
"mainnet": {
|
||||||
"rpcport": 9109,
|
"rpcport": 9109,
|
||||||
"pubkey_address": 0x073F,
|
"pubkey_address": 0x073F,
|
||||||
@@ -364,6 +411,7 @@ chainparams = {
|
|||||||
"has_cltv": False,
|
"has_cltv": False,
|
||||||
"has_csv": False,
|
"has_csv": False,
|
||||||
"has_segwit": False,
|
"has_segwit": False,
|
||||||
|
"has_multiwallet": False,
|
||||||
"mainnet": {
|
"mainnet": {
|
||||||
"rpcport": 8888,
|
"rpcport": 8888,
|
||||||
"pubkey_address": 82,
|
"pubkey_address": 82,
|
||||||
@@ -403,6 +451,7 @@ chainparams = {
|
|||||||
"decimal_places": 8,
|
"decimal_places": 8,
|
||||||
"has_csv": True,
|
"has_csv": True,
|
||||||
"has_segwit": True,
|
"has_segwit": True,
|
||||||
|
"has_multiwallet": False,
|
||||||
"mainnet": {
|
"mainnet": {
|
||||||
"rpcport": 44444,
|
"rpcport": 44444,
|
||||||
"pubkey_address": 53,
|
"pubkey_address": 53,
|
||||||
@@ -479,10 +528,13 @@ chainparams = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name_map = {}
|
||||||
ticker_map = {}
|
ticker_map = {}
|
||||||
|
|
||||||
|
|
||||||
for c, params in chainparams.items():
|
for c, params in chainparams.items():
|
||||||
|
name_map[params["name"].lower()] = c
|
||||||
ticker_map[params["ticker"].lower()] = c
|
ticker_map[params["ticker"].lower()] = c
|
||||||
|
|
||||||
|
|
||||||
@@ -490,4 +542,11 @@ def getCoinIdFromTicker(ticker: str) -> str:
|
|||||||
try:
|
try:
|
||||||
return ticker_map[ticker.lower()]
|
return ticker_map[ticker.lower()]
|
||||||
except Exception:
|
except Exception:
|
||||||
raise ValueError("Unknown coin")
|
raise ValueError(f"Unknown coin {ticker}")
|
||||||
|
|
||||||
|
|
||||||
|
def getCoinIdFromName(name: str) -> str:
|
||||||
|
try:
|
||||||
|
return name_map[name.lower()]
|
||||||
|
except Exception:
|
||||||
|
raise ValueError(f"Unknown coin {name}")
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ LITECOIND = os.getenv("LITECOIND", "litecoind" + bin_suffix)
|
|||||||
LITECOIN_CLI = os.getenv("LITECOIN_CLI", "litecoin-cli" + bin_suffix)
|
LITECOIN_CLI = os.getenv("LITECOIN_CLI", "litecoin-cli" + bin_suffix)
|
||||||
LITECOIN_TX = os.getenv("LITECOIN_TX", "litecoin-tx" + bin_suffix)
|
LITECOIN_TX = os.getenv("LITECOIN_TX", "litecoin-tx" + bin_suffix)
|
||||||
|
|
||||||
|
DOGECOIND = os.getenv("DOGECOIND", "dogecoind" + bin_suffix)
|
||||||
|
DOGECOIN_CLI = os.getenv("DOGECOIN_CLI", "dogecoin-cli" + bin_suffix)
|
||||||
|
DOGECOIN_TX = os.getenv("DOGECOIN_TX", "dogecoin-tx" + bin_suffix)
|
||||||
|
|
||||||
NAMECOIN_BINDIR = os.path.expanduser(
|
NAMECOIN_BINDIR = os.path.expanduser(
|
||||||
os.getenv("NAMECOIN_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "namecoin"))
|
os.getenv("NAMECOIN_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "namecoin"))
|
||||||
)
|
)
|
||||||
|
|||||||
64
basicswap/contrib/test_framework/descriptors.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2019 Pieter Wuille
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
"""Utility functions related to output descriptors"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
INPUT_CHARSET = "0123456789()[],'/*abcdefgh@:$%{}IJKLMNOPQRSTUVWXYZ&+-.;<=>?!^_|~ijklmnopqrstuvwxyzABCDEFGH`#\"\\ "
|
||||||
|
CHECKSUM_CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||||
|
GENERATOR = [0xf5dee51989, 0xa9fdca3312, 0x1bab10e32d, 0x3706b1677a, 0x644d626ffd]
|
||||||
|
|
||||||
|
def descsum_polymod(symbols):
|
||||||
|
"""Internal function that computes the descriptor checksum."""
|
||||||
|
chk = 1
|
||||||
|
for value in symbols:
|
||||||
|
top = chk >> 35
|
||||||
|
chk = (chk & 0x7ffffffff) << 5 ^ value
|
||||||
|
for i in range(5):
|
||||||
|
chk ^= GENERATOR[i] if ((top >> i) & 1) else 0
|
||||||
|
return chk
|
||||||
|
|
||||||
|
def descsum_expand(s):
|
||||||
|
"""Internal function that does the character to symbol expansion"""
|
||||||
|
groups = []
|
||||||
|
symbols = []
|
||||||
|
for c in s:
|
||||||
|
if not c in INPUT_CHARSET:
|
||||||
|
return None
|
||||||
|
v = INPUT_CHARSET.find(c)
|
||||||
|
symbols.append(v & 31)
|
||||||
|
groups.append(v >> 5)
|
||||||
|
if len(groups) == 3:
|
||||||
|
symbols.append(groups[0] * 9 + groups[1] * 3 + groups[2])
|
||||||
|
groups = []
|
||||||
|
if len(groups) == 1:
|
||||||
|
symbols.append(groups[0])
|
||||||
|
elif len(groups) == 2:
|
||||||
|
symbols.append(groups[0] * 3 + groups[1])
|
||||||
|
return symbols
|
||||||
|
|
||||||
|
def descsum_create(s):
|
||||||
|
"""Add a checksum to a descriptor without"""
|
||||||
|
symbols = descsum_expand(s) + [0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
checksum = descsum_polymod(symbols) ^ 1
|
||||||
|
return s + '#' + ''.join(CHECKSUM_CHARSET[(checksum >> (5 * (7 - i))) & 31] for i in range(8))
|
||||||
|
|
||||||
|
def descsum_check(s, require=True):
|
||||||
|
"""Verify that the checksum is correct in a descriptor"""
|
||||||
|
if not '#' in s:
|
||||||
|
return not require
|
||||||
|
if s[-9] != '#':
|
||||||
|
return False
|
||||||
|
if not all(x in CHECKSUM_CHARSET for x in s[-8:]):
|
||||||
|
return False
|
||||||
|
symbols = descsum_expand(s[:-9]) + [CHECKSUM_CHARSET.find(x) for x in s[-8:]]
|
||||||
|
return descsum_polymod(symbols) == 1
|
||||||
|
|
||||||
|
def drop_origins(s):
|
||||||
|
'''Drop the key origins from a descriptor'''
|
||||||
|
desc = re.sub(r'\[.+?\]', '', s)
|
||||||
|
if '#' in s:
|
||||||
|
desc = desc[:desc.index('#')]
|
||||||
|
return descsum_create(desc)
|
||||||
@@ -578,10 +578,8 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||||||
return page_offers(self, url_split, post_string, sent=True)
|
return page_offers(self, url_split, post_string, sent=True)
|
||||||
if page == "bid":
|
if page == "bid":
|
||||||
return page_bid(self, url_split, post_string)
|
return page_bid(self, url_split, post_string)
|
||||||
if page == "receivedbids":
|
if page == "bids":
|
||||||
return page_bids(self, url_split, post_string, received=True)
|
return page_bids(self, url_split, post_string)
|
||||||
if page == "sentbids":
|
|
||||||
return page_bids(self, url_split, post_string, sent=True)
|
|
||||||
if page == "availablebids":
|
if page == "availablebids":
|
||||||
return page_bids(self, url_split, post_string, available=True)
|
return page_bids(self, url_split, post_string, available=True)
|
||||||
if page == "watched":
|
if page == "watched":
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 tecnovert
|
# Copyright (c) 2024 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.
|
||||||
|
|
||||||
@@ -53,6 +54,10 @@ class CoinInterface:
|
|||||||
self._mx_wallet = threading.Lock()
|
self._mx_wallet = threading.Lock()
|
||||||
self._altruistic = True
|
self._altruistic = True
|
||||||
|
|
||||||
|
def interface_type(self) -> int:
|
||||||
|
# coin_type() returns the base coin type, interface_type() returns the coin+balance type.
|
||||||
|
return self.coin_type()
|
||||||
|
|
||||||
def setDefaults(self):
|
def setDefaults(self):
|
||||||
self._unknown_wallet_seed = True
|
self._unknown_wallet_seed = True
|
||||||
self._restore_height = None
|
self._restore_height = None
|
||||||
@@ -188,7 +193,7 @@ class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
|
|||||||
def curve_type():
|
def curve_type():
|
||||||
return Curves.secp256k1
|
return Curves.secp256k1
|
||||||
|
|
||||||
def getNewSecretKey(self) -> bytes:
|
def getNewRandomKey(self) -> bytes:
|
||||||
return i2b(getSecretInt())
|
return i2b(getSecretInt())
|
||||||
|
|
||||||
def getPubkey(self, privkey: bytes) -> bytes:
|
def getPubkey(self, privkey: bytes) -> bytes:
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# 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.
|
||||||
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
from basicswap.contrib.test_framework.messages import COutPoint, CTransaction, CTxIn
|
from basicswap.contrib.test_framework.messages import COutPoint, CTransaction, CTxIn
|
||||||
from basicswap.util import b2h, b2i, ensure, i2h
|
from basicswap.util import b2i, ensure, i2b
|
||||||
from basicswap.util.script import decodePushData, decodeScriptNum
|
from basicswap.util.script import decodePushData, decodeScriptNum
|
||||||
from .btc import BTCInterface, ensure_op, findOutput
|
from .btc import BTCInterface, ensure_op, findOutput
|
||||||
from basicswap.rpc import make_rpc_func
|
from basicswap.rpc import make_rpc_func
|
||||||
@@ -454,11 +454,14 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockSpendTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
"createSCLockSpendTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
size,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize_without_witness()
|
return tx.serialize_without_witness()
|
||||||
@@ -506,11 +509,14 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockRefundTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize_without_witness(), refund_script, tx.vout[0].nValue
|
return tx.serialize_without_witness(), refund_script, tx.vout[0].nValue
|
||||||
@@ -582,11 +588,14 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockRefundSpendToFTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize_without_witness()
|
return tx.serialize_without_witness()
|
||||||
@@ -780,7 +789,7 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
||||||
ensure(tx.nLockTime == 0, "Bad nLockTime") # TODO match txns created by cores
|
ensure(tx.nLockTime == 0, "Bad nLockTime") # TODO match txns created by cores
|
||||||
@@ -835,7 +844,7 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock refund tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock refund tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
||||||
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
||||||
@@ -881,7 +890,7 @@ class BCHInterface(BTCInterface):
|
|||||||
size = self.getTxSize(tx)
|
size = self.getTxSize(tx)
|
||||||
vsize = size
|
vsize = size
|
||||||
|
|
||||||
self._log.info(
|
self._log.info_s(
|
||||||
"tx amount, vsize, fee: %ld, %ld, %ld", locked_coin, vsize, fee_paid
|
"tx amount, vsize, fee: %ld, %ld, %ld", locked_coin, vsize, fee_paid
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -905,7 +914,7 @@ class BCHInterface(BTCInterface):
|
|||||||
# Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr
|
# Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock refund spend tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock refund spend tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
||||||
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
||||||
@@ -947,9 +956,7 @@ class BCHInterface(BTCInterface):
|
|||||||
size = self.getTxSize(tx)
|
size = self.getTxSize(tx)
|
||||||
vsize = size
|
vsize = size
|
||||||
|
|
||||||
self._log.info(
|
self._log.info_s(f"tx amount, vsize, fee: {tx_value}, {vsize}, {fee_paid}")
|
||||||
"tx amount, vsize, fee: %ld, %ld, %ld", tx_value, vsize, fee_paid
|
|
||||||
)
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -962,7 +969,7 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock spend tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock spend tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
||||||
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
||||||
@@ -995,7 +1002,7 @@ class BCHInterface(BTCInterface):
|
|||||||
size = self.getTxSize(tx)
|
size = self.getTxSize(tx)
|
||||||
vsize = size
|
vsize = size
|
||||||
|
|
||||||
self._log.info(
|
self._log.info_s(
|
||||||
"tx amount, vsize, fee: %ld, %ld, %ld", tx.vout[0].nValue, vsize, fee_paid
|
"tx amount, vsize, fee: %ld, %ld, %ld", tx.vout[0].nValue, vsize, fee_paid
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1115,11 +1122,14 @@ class BCHInterface(BTCInterface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createMercyTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createMercyTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
1,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {1}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
txHex = tx.serialize_without_witness()
|
txHex = tx.serialize_without_witness()
|
||||||
|
|||||||
@@ -2,7 +2,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.
|
||||||
|
|
||||||
@@ -18,15 +18,9 @@ from basicswap.basicswap_util import (
|
|||||||
getVoutByAddress,
|
getVoutByAddress,
|
||||||
getVoutByScriptPubKey,
|
getVoutByScriptPubKey,
|
||||||
)
|
)
|
||||||
from basicswap.contrib.test_framework import (
|
from basicswap.interface.base import Secp256k1Interface
|
||||||
segwit_addr,
|
|
||||||
)
|
|
||||||
from basicswap.interface.base import (
|
|
||||||
Secp256k1Interface,
|
|
||||||
)
|
|
||||||
from basicswap.util import (
|
from basicswap.util import (
|
||||||
ensure,
|
ensure,
|
||||||
b2h,
|
|
||||||
i2b,
|
i2b,
|
||||||
b2i,
|
b2i,
|
||||||
i2h,
|
i2h,
|
||||||
@@ -35,6 +29,7 @@ from basicswap.util.ecc import (
|
|||||||
pointToCPK,
|
pointToCPK,
|
||||||
CPKToPoint,
|
CPKToPoint,
|
||||||
)
|
)
|
||||||
|
from basicswap.util.extkey import ExtKeyPair
|
||||||
from basicswap.util.script import (
|
from basicswap.util.script import (
|
||||||
decodeScriptNum,
|
decodeScriptNum,
|
||||||
getCompactSizeLen,
|
getCompactSizeLen,
|
||||||
@@ -44,6 +39,7 @@ from basicswap.util.script import (
|
|||||||
from basicswap.util.address import (
|
from basicswap.util.address import (
|
||||||
toWIF,
|
toWIF,
|
||||||
b58encode,
|
b58encode,
|
||||||
|
b58decode,
|
||||||
decodeWif,
|
decodeWif,
|
||||||
decodeAddress,
|
decodeAddress,
|
||||||
pubkeyToAddress,
|
pubkeyToAddress,
|
||||||
@@ -63,6 +59,8 @@ from coincurve.ecdsaotves import (
|
|||||||
ecdsaotves_rec_enc_key,
|
ecdsaotves_rec_enc_key,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from basicswap.contrib.test_framework import segwit_addr
|
||||||
|
from basicswap.contrib.test_framework.descriptors import descsum_create
|
||||||
from basicswap.contrib.test_framework.messages import (
|
from basicswap.contrib.test_framework.messages import (
|
||||||
COIN,
|
COIN,
|
||||||
COutPoint,
|
COutPoint,
|
||||||
@@ -217,6 +215,10 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
rv += output.nValue
|
rv += output.nValue
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 110
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
return 147
|
return 147
|
||||||
@@ -262,10 +264,22 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
self._rpcport = coin_settings["rpcport"]
|
self._rpcport = coin_settings["rpcport"]
|
||||||
self._rpcauth = coin_settings["rpcauth"]
|
self._rpcauth = coin_settings["rpcauth"]
|
||||||
self.rpc = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
self.rpc = make_rpc_func(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||||
self._rpc_wallet = "wallet.dat"
|
self._rpc_wallet = coin_settings.get("wallet_name", "wallet.dat")
|
||||||
|
self._rpc_wallet_watch = coin_settings.get(
|
||||||
|
"watch_wallet_name", self._rpc_wallet
|
||||||
|
)
|
||||||
self.rpc_wallet = make_rpc_func(
|
self.rpc_wallet = make_rpc_func(
|
||||||
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet
|
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet
|
||||||
)
|
)
|
||||||
|
if self._rpc_wallet_watch == self._rpc_wallet:
|
||||||
|
self.rpc_wallet_watch = self.rpc_wallet
|
||||||
|
else:
|
||||||
|
self.rpc_wallet_watch = make_rpc_func(
|
||||||
|
self._rpcport,
|
||||||
|
self._rpcauth,
|
||||||
|
host=self._rpc_host,
|
||||||
|
wallet=self._rpc_wallet_watch,
|
||||||
|
)
|
||||||
self.blocks_confirmed = coin_settings["blocks_confirmed"]
|
self.blocks_confirmed = coin_settings["blocks_confirmed"]
|
||||||
self.setConfTarget(coin_settings["conf_target"])
|
self.setConfTarget(coin_settings["conf_target"])
|
||||||
self._use_segwit = coin_settings["use_segwit"]
|
self._use_segwit = coin_settings["use_segwit"]
|
||||||
@@ -274,6 +288,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||||
self._expect_seedid_hex = None
|
self._expect_seedid_hex = None
|
||||||
self._altruistic = coin_settings.get("altruistic", True)
|
self._altruistic = coin_settings.get("altruistic", True)
|
||||||
|
self._use_descriptors = coin_settings.get("use_descriptors", False)
|
||||||
|
|
||||||
def open_rpc(self, wallet=None):
|
def open_rpc(self, wallet=None):
|
||||||
return openrpc(self._rpcport, self._rpcauth, wallet=wallet, host=self._rpc_host)
|
return openrpc(self._rpcport, self._rpcauth, wallet=wallet, host=self._rpc_host)
|
||||||
@@ -297,16 +312,19 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
# Wallet name is "" for some LTC and PART installs on older cores
|
# Wallet name is "" for some LTC and PART installs on older cores
|
||||||
if self._rpc_wallet not in wallets and len(wallets) > 0:
|
if self._rpc_wallet not in wallets and len(wallets) > 0:
|
||||||
self._log.debug("Changing {} wallet name.".format(self.ticker()))
|
self._log.warning(f"Changing {self.ticker()} wallet name.")
|
||||||
for wallet_name in wallets:
|
for wallet_name in wallets:
|
||||||
# Skip over other expected wallets
|
# Skip over other expected wallets
|
||||||
if wallet_name in ("mweb",):
|
if wallet_name in ("mweb",):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
change_watchonly_wallet: bool = (
|
||||||
|
self._rpc_wallet_watch == self._rpc_wallet
|
||||||
|
)
|
||||||
|
|
||||||
self._rpc_wallet = wallet_name
|
self._rpc_wallet = wallet_name
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"Switched {} wallet name to {}.".format(
|
f"Switched {self.ticker()} wallet name to {self._rpc_wallet}."
|
||||||
self.ticker(), self._rpc_wallet
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
self.rpc_wallet = make_rpc_func(
|
self.rpc_wallet = make_rpc_func(
|
||||||
self._rpcport,
|
self._rpcport,
|
||||||
@@ -314,6 +332,8 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
host=self._rpc_host,
|
host=self._rpc_host,
|
||||||
wallet=self._rpc_wallet,
|
wallet=self._rpc_wallet,
|
||||||
)
|
)
|
||||||
|
if change_watchonly_wallet:
|
||||||
|
self.rpc_wallet_watch = self.rpc_wallet
|
||||||
break
|
break
|
||||||
|
|
||||||
return len(wallets)
|
return len(wallets)
|
||||||
@@ -358,9 +378,40 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
raise ValueError(f"Block header not found at time: {time}")
|
raise ValueError(f"Block header not found at time: {time}")
|
||||||
|
|
||||||
def initialiseWallet(self, key_bytes: bytes) -> None:
|
def initialiseWallet(self, key_bytes: bytes) -> None:
|
||||||
key_wif = self.encodeKey(key_bytes)
|
assert len(key_bytes) == 32
|
||||||
self.rpc_wallet("sethdseed", [True, key_wif])
|
|
||||||
self._have_checked_seed = False
|
self._have_checked_seed = False
|
||||||
|
if self._use_descriptors:
|
||||||
|
self._log.info("Importing descriptors")
|
||||||
|
ek = ExtKeyPair()
|
||||||
|
ek.set_seed(key_bytes)
|
||||||
|
ek_encoded: str = self.encode_secret_extkey(ek.encode_v())
|
||||||
|
desc_external = descsum_create(f"wpkh({ek_encoded}/0h/0h/*h)")
|
||||||
|
desc_internal = descsum_create(f"wpkh({ek_encoded}/0h/1h/*h)")
|
||||||
|
rv = self.rpc_wallet(
|
||||||
|
"importdescriptors",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{"desc": desc_external, "timestamp": "now", "active": True},
|
||||||
|
{
|
||||||
|
"desc": desc_internal,
|
||||||
|
"timestamp": "now",
|
||||||
|
"active": True,
|
||||||
|
"internal": True,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
num_successful: int = 0
|
||||||
|
for entry in rv:
|
||||||
|
if entry.get("success", False) is True:
|
||||||
|
num_successful += 1
|
||||||
|
if num_successful != 2:
|
||||||
|
self._log.error(f"Failed to import descriptors: {rv}.")
|
||||||
|
raise ValueError("Failed to import descriptors.")
|
||||||
|
else:
|
||||||
|
key_wif = self.encodeKey(key_bytes)
|
||||||
|
self.rpc_wallet("sethdseed", [True, key_wif])
|
||||||
|
|
||||||
def getWalletInfo(self):
|
def getWalletInfo(self):
|
||||||
rv = self.rpc_wallet("getwalletinfo")
|
rv = self.rpc_wallet("getwalletinfo")
|
||||||
@@ -370,16 +421,23 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
return rv
|
return rv
|
||||||
|
|
||||||
def getWalletRestoreHeight(self) -> int:
|
def getWalletRestoreHeight(self) -> int:
|
||||||
start_time = self.rpc_wallet("getwalletinfo")["keypoololdest"]
|
if self._use_descriptors:
|
||||||
|
descriptor = self.getActiveDescriptor()
|
||||||
|
if descriptor is None:
|
||||||
|
start_time = 0
|
||||||
|
else:
|
||||||
|
start_time = descriptor["timestamp"]
|
||||||
|
else:
|
||||||
|
start_time = self.rpc_wallet("getwalletinfo")["keypoololdest"]
|
||||||
|
|
||||||
blockchaininfo = self.getBlockchainInfo()
|
blockchaininfo = self.getBlockchainInfo()
|
||||||
best_block = blockchaininfo["bestblockhash"]
|
best_block = blockchaininfo["bestblockhash"]
|
||||||
|
|
||||||
chain_synced = round(blockchaininfo["verificationprogress"], 3)
|
chain_synced = round(blockchaininfo["verificationprogress"], 3)
|
||||||
if chain_synced < 1.0:
|
if chain_synced < 1.0:
|
||||||
raise ValueError("{} chain isn't synced.".format(self.coin_name()))
|
raise ValueError(f"{self.coin_name()} chain isn't synced.")
|
||||||
|
|
||||||
self._log.debug("Finding block at time: {}".format(start_time))
|
self._log.debug(f"Finding block at time: {start_time}")
|
||||||
|
|
||||||
rpc_conn = self.open_rpc()
|
rpc_conn = self.open_rpc()
|
||||||
try:
|
try:
|
||||||
@@ -390,16 +448,43 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
)
|
)
|
||||||
if block_header["time"] < start_time:
|
if block_header["time"] < start_time:
|
||||||
return block_header["height"]
|
return block_header["height"]
|
||||||
|
if "previousblockhash" not in block_header: # Genesis block
|
||||||
|
return block_header["height"]
|
||||||
block_hash = block_header["previousblockhash"]
|
block_hash = block_header["previousblockhash"]
|
||||||
finally:
|
finally:
|
||||||
self.close_rpc(rpc_conn)
|
self.close_rpc(rpc_conn)
|
||||||
raise ValueError("{} wallet restore height not found.".format(self.coin_name()))
|
raise ValueError(f"{self.coin_name()} wallet restore height not found.")
|
||||||
|
|
||||||
def getWalletSeedID(self) -> str:
|
def getWalletSeedID(self) -> str:
|
||||||
wi = self.rpc_wallet("getwalletinfo")
|
wi = self.rpc_wallet("getwalletinfo")
|
||||||
return "Not found" if "hdseedid" not in wi else wi["hdseedid"]
|
return "Not found" if "hdseedid" not in wi else wi["hdseedid"]
|
||||||
|
|
||||||
|
def getActiveDescriptor(self):
|
||||||
|
descriptors = self.rpc_wallet("listdescriptors")["descriptors"]
|
||||||
|
for descriptor in descriptors:
|
||||||
|
if (
|
||||||
|
descriptor["desc"].startswith("wpkh")
|
||||||
|
and descriptor["active"] is True
|
||||||
|
and descriptor["internal"] is False
|
||||||
|
):
|
||||||
|
return descriptor
|
||||||
|
return None
|
||||||
|
|
||||||
def checkExpectedSeed(self, expect_seedid: str) -> bool:
|
def checkExpectedSeed(self, expect_seedid: str) -> bool:
|
||||||
|
if self._use_descriptors:
|
||||||
|
descriptor = self.getActiveDescriptor()
|
||||||
|
if descriptor is None:
|
||||||
|
self._log.debug("Could not find active descriptor.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
end = descriptor["desc"].find("/")
|
||||||
|
if end < 10:
|
||||||
|
return False
|
||||||
|
extkey = descriptor["desc"][5:end]
|
||||||
|
extkey_data = b58decode(extkey)[4:-4]
|
||||||
|
extkey_data_hash: bytes = hash160(extkey_data)
|
||||||
|
return True if extkey_data_hash.hex() == expect_seedid else False
|
||||||
|
|
||||||
wallet_seed_id = self.getWalletSeedID()
|
wallet_seed_id = self.getWalletSeedID()
|
||||||
self._expect_seedid_hex = expect_seedid
|
self._expect_seedid_hex = expect_seedid
|
||||||
self._have_checked_seed = True
|
self._have_checked_seed = True
|
||||||
@@ -424,6 +509,10 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
||||||
if not or_watch_only:
|
if not or_watch_only:
|
||||||
return addr_info["ismine"]
|
return addr_info["ismine"]
|
||||||
|
|
||||||
|
if self._use_descriptors:
|
||||||
|
addr_info = self.rpc_wallet_watch("getaddressinfo", [address])
|
||||||
|
|
||||||
return addr_info["ismine"] or addr_info["iswatchonly"]
|
return addr_info["ismine"] or addr_info["iswatchonly"]
|
||||||
|
|
||||||
def checkAddressMine(self, address: str) -> None:
|
def checkAddressMine(self, address: str) -> None:
|
||||||
@@ -491,6 +580,20 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
pkh = hash160(pk)
|
pkh = hash160(pk)
|
||||||
return segwit_addr.encode(bech32_prefix, version, pkh)
|
return segwit_addr.encode(bech32_prefix, version, pkh)
|
||||||
|
|
||||||
|
def encode_secret_extkey(self, ek_data: bytes) -> str:
|
||||||
|
assert len(ek_data) == 74
|
||||||
|
prefix = self.chainparams_network()["ext_secret_key_prefix"]
|
||||||
|
data: bytes = prefix.to_bytes(4, "big") + ek_data
|
||||||
|
checksum = sha256(sha256(data))
|
||||||
|
return b58encode(data + checksum[0:4])
|
||||||
|
|
||||||
|
def encode_public_extkey(self, ek_data: bytes) -> str:
|
||||||
|
assert len(ek_data) == 74
|
||||||
|
prefix = self.chainparams_network()["ext_public_key_prefix"]
|
||||||
|
data: bytes = prefix.to_bytes(4, "big") + ek_data
|
||||||
|
checksum = sha256(sha256(data))
|
||||||
|
return b58encode(data + checksum[0:4])
|
||||||
|
|
||||||
def pkh_to_address(self, pkh: bytes) -> str:
|
def pkh_to_address(self, pkh: bytes) -> str:
|
||||||
# pkh is ripemd160(sha256(pk))
|
# pkh is ripemd160(sha256(pk))
|
||||||
assert len(pkh) == 20
|
assert len(pkh) == 20
|
||||||
@@ -526,7 +629,12 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
pk = self.getPubkey(key)
|
pk = self.getPubkey(key)
|
||||||
return hash160(pk)
|
return hash160(pk)
|
||||||
|
|
||||||
def getSeedHash(self, seed) -> bytes:
|
def getSeedHash(self, seed: bytes) -> bytes:
|
||||||
|
if self._use_descriptors:
|
||||||
|
ek = ExtKeyPair()
|
||||||
|
ek.set_seed(seed)
|
||||||
|
return hash160(ek.encode_p())
|
||||||
|
|
||||||
return self.getAddressHashFromKey(seed)[::-1]
|
return self.getAddressHashFromKey(seed)[::-1]
|
||||||
|
|
||||||
def encodeKey(self, key_bytes: bytes) -> str:
|
def encodeKey(self, key_bytes: bytes) -> str:
|
||||||
@@ -626,11 +734,14 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockRefundTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize(), refund_script, tx.vout[0].nValue
|
return tx.serialize(), refund_script, tx.vout[0].nValue
|
||||||
@@ -681,11 +792,14 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockRefundSpendTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
@@ -748,11 +862,14 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockRefundSpendToFTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
@@ -795,11 +912,14 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockSpendTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
@@ -824,7 +944,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
||||||
ensure(tx.nLockTime == 0, "Bad nLockTime") # TODO match txns created by cores
|
ensure(tx.nLockTime == 0, "Bad nLockTime") # TODO match txns created by cores
|
||||||
@@ -912,7 +1032,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock refund tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock refund tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
||||||
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
||||||
@@ -951,7 +1071,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||||
fee_rate_paid = fee_paid * 1000 // vsize
|
fee_rate_paid = fee_paid * 1000 // vsize
|
||||||
|
|
||||||
self._log.info(
|
self._log.info_s(
|
||||||
"tx amount, vsize, feerate: %ld, %ld, %ld",
|
"tx amount, vsize, feerate: %ld, %ld, %ld",
|
||||||
locked_coin,
|
locked_coin,
|
||||||
vsize,
|
vsize,
|
||||||
@@ -980,7 +1100,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
# Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr
|
# Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock refund spend tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock refund spend tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
||||||
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
||||||
@@ -1017,7 +1137,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||||
fee_rate_paid = fee_paid * 1000 // vsize
|
fee_rate_paid = fee_paid * 1000 // vsize
|
||||||
|
|
||||||
self._log.info(
|
self._log.info_s(
|
||||||
"tx amount, vsize, feerate: %ld, %ld, %ld", tx_value, vsize, fee_rate_paid
|
"tx amount, vsize, feerate: %ld, %ld, %ld", tx_value, vsize, fee_rate_paid
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1035,7 +1155,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock spend tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock spend tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
ensure(tx.nVersion == self.txVersion(), "Bad version")
|
||||||
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
ensure(tx.nLockTime == 0, "nLockTime not 0")
|
||||||
@@ -1073,7 +1193,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||||
fee_rate_paid = fee_paid * 1000 // vsize
|
fee_rate_paid = fee_paid * 1000 // vsize
|
||||||
|
|
||||||
self._log.info(
|
self._log.info_s(
|
||||||
"tx amount, vsize, feerate: %ld, %ld, %ld",
|
"tx amount, vsize, feerate: %ld, %ld, %ld",
|
||||||
tx.vout[0].nValue,
|
tx.vout[0].nValue,
|
||||||
vsize,
|
vsize,
|
||||||
@@ -1296,7 +1416,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
def getWalletTransaction(self, txid: bytes):
|
def getWalletTransaction(self, txid: bytes):
|
||||||
try:
|
try:
|
||||||
return bytes.fromhex(self.rpc_wallet("gettransaction", [txid.hex()]))
|
return bytes.fromhex(self.rpc_wallet("gettransaction", [txid.hex()])["hex"])
|
||||||
except Exception as e: # noqa: F841
|
except Exception as e: # noqa: F841
|
||||||
# TODO: filter errors
|
# TODO: filter errors
|
||||||
return None
|
return None
|
||||||
@@ -1383,7 +1503,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
witness_bytes = 109
|
witness_bytes = 109
|
||||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||||
pay_fee = round(fee_rate * vsize / 1000)
|
pay_fee = round(fee_rate * vsize / 1000)
|
||||||
self._log.info(
|
self._log.info_s(
|
||||||
f"BLockSpendTx fee_rate, vsize, fee: {fee_rate}, {vsize}, {pay_fee}."
|
f"BLockSpendTx fee_rate, vsize, fee: {fee_rate}, {vsize}, {pay_fee}."
|
||||||
)
|
)
|
||||||
return pay_fee
|
return pay_fee
|
||||||
@@ -1397,10 +1517,13 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
cb_swap_value: int,
|
cb_swap_value: int,
|
||||||
b_fee: int,
|
b_fee: int,
|
||||||
restore_height: int,
|
restore_height: int,
|
||||||
|
spend_actual_balance: bool = False,
|
||||||
lock_tx_vout=None,
|
lock_tx_vout=None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"spendBLockTx: {} {}\n".format(chain_b_lock_txid.hex(), lock_tx_vout)
|
"spendBLockTx: {} {}\n".format(
|
||||||
|
self._log.id(chain_b_lock_txid), lock_tx_vout
|
||||||
|
)
|
||||||
)
|
)
|
||||||
locked_n = lock_tx_vout
|
locked_n = lock_tx_vout
|
||||||
|
|
||||||
@@ -1408,7 +1531,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
script_pk = self.getPkDest(Kbs)
|
script_pk = self.getPkDest(Kbs)
|
||||||
|
|
||||||
if locked_n is None:
|
if locked_n is None:
|
||||||
wtx = self.rpc_wallet(
|
wtx = self.rpc_wallet_watch(
|
||||||
"gettransaction",
|
"gettransaction",
|
||||||
[
|
[
|
||||||
chain_b_lock_txid.hex(),
|
chain_b_lock_txid.hex(),
|
||||||
@@ -1445,10 +1568,23 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
return bytes.fromhex(self.publishTx(b_lock_spend_tx))
|
return bytes.fromhex(self.publishTx(b_lock_spend_tx))
|
||||||
|
|
||||||
def importWatchOnlyAddress(self, address: str, label: str):
|
def importWatchOnlyAddress(self, address: str, label: str) -> None:
|
||||||
|
if self._use_descriptors:
|
||||||
|
desc_watch = descsum_create(f"addr({address})")
|
||||||
|
rv = self.rpc_wallet_watch(
|
||||||
|
"importdescriptors",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
{"desc": desc_watch, "timestamp": "now", "active": False},
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
ensure(rv[0]["success"] is True, "importdescriptors failed for watchonly")
|
||||||
|
return
|
||||||
|
|
||||||
self.rpc_wallet("importaddress", [address, label, False])
|
self.rpc_wallet("importaddress", [address, label, False])
|
||||||
|
|
||||||
def isWatchOnlyAddress(self, address: str):
|
def isWatchOnlyAddress(self, address: str) -> bool:
|
||||||
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
||||||
return addr_info["iswatchonly"]
|
return addr_info["iswatchonly"]
|
||||||
|
|
||||||
@@ -1466,10 +1602,11 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
vout: int = -1,
|
vout: int = -1,
|
||||||
):
|
):
|
||||||
# Add watchonly address and rescan if required
|
# Add watchonly address and rescan if required
|
||||||
|
|
||||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||||
self.importWatchOnlyAddress(dest_address, "bid")
|
self.importWatchOnlyAddress(dest_address, "bid")
|
||||||
self._log.info("Imported watch-only addr: {}".format(dest_address))
|
self._log.info(
|
||||||
|
"Imported watch-only addr: {}".format(self._log.addr(dest_address))
|
||||||
|
)
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"Rescanning {} chain from height: {}".format(
|
"Rescanning {} chain from height: {}".format(
|
||||||
self.coin_name(), rescan_from
|
self.coin_name(), rescan_from
|
||||||
@@ -1479,7 +1616,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
return_txid = True if txid is None else False
|
return_txid = True if txid is None else False
|
||||||
if txid is None:
|
if txid is None:
|
||||||
txns = self.rpc_wallet(
|
txns = self.rpc_wallet_watch(
|
||||||
"listunspent",
|
"listunspent",
|
||||||
[
|
[
|
||||||
0,
|
0,
|
||||||
@@ -1500,7 +1637,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# set `include_watchonly` explicitly to `True` to get transactions for watchonly addresses also in BCH
|
# set `include_watchonly` explicitly to `True` to get transactions for watchonly addresses also in BCH
|
||||||
tx = self.rpc_wallet("gettransaction", [txid.hex(), True])
|
tx = self.rpc_wallet_watch("gettransaction", [txid.hex(), True])
|
||||||
|
|
||||||
block_height = 0
|
block_height = 0
|
||||||
if "blockhash" in tx:
|
if "blockhash" in tx:
|
||||||
@@ -1802,16 +1939,20 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
def unlockWallet(self, password: str):
|
def unlockWallet(self, password: str):
|
||||||
if password == "":
|
if password == "":
|
||||||
return
|
return
|
||||||
self._log.info("unlockWallet - {}".format(self.ticker()))
|
self._log.info(f"unlockWallet - {self.ticker()}")
|
||||||
|
|
||||||
if self.coin_type() == Coins.BTC:
|
if self.coin_type() == Coins.BTC:
|
||||||
# Recreate wallet if none found
|
# Recreate wallet if none found
|
||||||
# Required when encrypting an existing btc wallet, workaround is to delete the btc wallet and recreate
|
# Required when encrypting an existing btc wallet, workaround is to delete the btc wallet and recreate
|
||||||
wallets = self.rpc("listwallets")
|
wallets = self.rpc("listwallets")
|
||||||
if len(wallets) < 1:
|
if len(wallets) < 1:
|
||||||
self._log.info("Creating wallet.dat for {}.".format(self.coin_name()))
|
self._log.info(
|
||||||
|
f'Creating wallet "{self._rpc_wallet}" for {self.coin_name()}.'
|
||||||
|
)
|
||||||
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
|
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors
|
||||||
self.rpc("createwallet", ["wallet.dat", False, True, "", False, False])
|
self.rpc(
|
||||||
|
"createwallet", [self._rpc_wallet, False, True, "", False, False]
|
||||||
|
)
|
||||||
self.rpc_wallet("encryptwallet", [password])
|
self.rpc_wallet("encryptwallet", [password])
|
||||||
|
|
||||||
# Max timeout value, ~3 years
|
# Max timeout value, ~3 years
|
||||||
@@ -1819,7 +1960,7 @@ class BTCInterface(Secp256k1Interface):
|
|||||||
self._sc.checkWalletSeed(self.coin_type())
|
self._sc.checkWalletSeed(self.coin_type())
|
||||||
|
|
||||||
def lockWallet(self):
|
def lockWallet(self):
|
||||||
self._log.info("lockWallet - {}".format(self.ticker()))
|
self._log.info(f"lockWallet - {self.ticker()}")
|
||||||
self.rpc_wallet("walletlock")
|
self.rpc_wallet("walletlock")
|
||||||
|
|
||||||
def get_p2sh_script_pubkey(self, script: bytearray) -> bytearray:
|
def get_p2sh_script_pubkey(self, script: bytearray) -> bytearray:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2024 tecnovert
|
# Copyright (c) 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.
|
||||||
|
|
||||||
@@ -27,7 +27,6 @@ from basicswap.interface.btc import (
|
|||||||
)
|
)
|
||||||
from basicswap.util import (
|
from basicswap.util import (
|
||||||
ensure,
|
ensure,
|
||||||
b2h,
|
|
||||||
b2i,
|
b2i,
|
||||||
i2b,
|
i2b,
|
||||||
i2h,
|
i2h,
|
||||||
@@ -211,6 +210,10 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
def txoType():
|
def txoType():
|
||||||
return CTxOut
|
return CTxOut
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 224
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
return 327
|
return 327
|
||||||
@@ -273,6 +276,9 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
self._connection_type = coin_settings["connection_type"]
|
self._connection_type = coin_settings["connection_type"]
|
||||||
self._altruistic = coin_settings.get("altruistic", True)
|
self._altruistic = coin_settings.get("altruistic", True)
|
||||||
|
|
||||||
|
if "wallet_name" in coin_settings:
|
||||||
|
raise ValueError(f"Invalid setting for {self.coin_name()}: wallet_name")
|
||||||
|
|
||||||
def open_rpc(self):
|
def open_rpc(self):
|
||||||
return openrpc(self._rpcport, self._rpcauth, host=self._rpc_host)
|
return openrpc(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||||
|
|
||||||
@@ -1123,11 +1129,14 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
fee_info["size"] = size
|
fee_info["size"] = size
|
||||||
|
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockSpendTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
"createSCLockSpendTx {}{}.".format(
|
||||||
tx.TxHash().hex(),
|
self._log.id(tx.TxHash()),
|
||||||
tx_fee_rate,
|
(
|
||||||
size,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, size, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize(TxSerializeType.NoWitness)
|
return tx.serialize(TxSerializeType.NoWitness)
|
||||||
@@ -1167,11 +1176,14 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
tx.vout[0].value = locked_coin - pay_fee
|
tx.vout[0].value = locked_coin - pay_fee
|
||||||
|
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
"createSCLockRefundTx {}{}.".format(
|
||||||
tx.TxHash().hex(),
|
self._log.id(tx.TxHash()),
|
||||||
tx_fee_rate,
|
(
|
||||||
size,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, size, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize(TxSerializeType.NoWitness), refund_script, tx.vout[0].value
|
return tx.serialize(TxSerializeType.NoWitness), refund_script, tx.vout[0].value
|
||||||
@@ -1215,11 +1227,14 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
tx.vout[0].value = locked_coin - pay_fee
|
tx.vout[0].value = locked_coin - pay_fee
|
||||||
|
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundSpendTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
"createSCLockRefundSpendTx {}{}.".format(
|
||||||
tx.TxHash().hex(),
|
self._log.id(tx.TxHash()),
|
||||||
tx_fee_rate,
|
(
|
||||||
size,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, size, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize(TxSerializeType.NoWitness)
|
return tx.serialize(TxSerializeType.NoWitness)
|
||||||
@@ -1244,7 +1259,7 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.version == self.txVersion(), "Bad version")
|
ensure(tx.version == self.txVersion(), "Bad version")
|
||||||
ensure(tx.locktime == 0, "Bad locktime")
|
ensure(tx.locktime == 0, "Bad locktime")
|
||||||
@@ -1320,7 +1335,7 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock spend tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock spend tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.version == self.txVersion(), "Bad version")
|
ensure(tx.version == self.txVersion(), "Bad version")
|
||||||
ensure(tx.locktime == 0, "Bad locktime")
|
ensure(tx.locktime == 0, "Bad locktime")
|
||||||
@@ -1390,7 +1405,7 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
|
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock refund tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock refund tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.version == self.txVersion(), "Bad version")
|
ensure(tx.version == self.txVersion(), "Bad version")
|
||||||
ensure(tx.locktime == 0, "locktime not 0")
|
ensure(tx.locktime == 0, "locktime not 0")
|
||||||
@@ -1453,7 +1468,7 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
# Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr
|
# Must have only one output sending lock refund tx value - fee to leader's address, TODO: follower shouldn't need to verify destination addr
|
||||||
tx = self.loadTx(tx_bytes)
|
tx = self.loadTx(tx_bytes)
|
||||||
txid = self.getTxid(tx)
|
txid = self.getTxid(tx)
|
||||||
self._log.info("Verifying lock refund spend tx: {}.".format(b2h(txid)))
|
self._log.info("Verifying lock refund spend tx: {}.".format(self._log.id(txid)))
|
||||||
|
|
||||||
ensure(tx.version == self.txVersion(), "Bad version")
|
ensure(tx.version == self.txVersion(), "Bad version")
|
||||||
ensure(tx.locktime == 0, "locktime not 0")
|
ensure(tx.locktime == 0, "locktime not 0")
|
||||||
@@ -1539,11 +1554,14 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
tx.vout[0].value = locked_amount - pay_fee
|
tx.vout[0].value = locked_amount - pay_fee
|
||||||
|
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
"createSCLockRefundSpendToFTx {}{}.".format(
|
||||||
tx.TxHash().hex(),
|
self._log.id(tx.TxHash()),
|
||||||
tx_fee_rate,
|
(
|
||||||
size,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, size, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize(TxSerializeType.NoWitness)
|
return tx.serialize(TxSerializeType.NoWitness)
|
||||||
@@ -1712,7 +1730,7 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
witness_bytes = 115
|
witness_bytes = 115
|
||||||
size = len(tx.serialize()) + witness_bytes
|
size = len(tx.serialize()) + witness_bytes
|
||||||
pay_fee = round(fee_rate * size / 1000)
|
pay_fee = round(fee_rate * size / 1000)
|
||||||
self._log.info(
|
self._log.info_s(
|
||||||
f"BLockSpendTx fee_rate, vsize, fee: {fee_rate}, {size}, {pay_fee}."
|
f"BLockSpendTx fee_rate, vsize, fee: {fee_rate}, {size}, {pay_fee}."
|
||||||
)
|
)
|
||||||
return pay_fee
|
return pay_fee
|
||||||
@@ -1726,6 +1744,7 @@ class DCRInterface(Secp256k1Interface):
|
|||||||
cb_swap_value: int,
|
cb_swap_value: int,
|
||||||
b_fee: int,
|
b_fee: int,
|
||||||
restore_height: int,
|
restore_height: int,
|
||||||
|
spend_actual_balance: bool = False,
|
||||||
lock_tx_vout=None,
|
lock_tx_vout=None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
self._log.info("spendBLockTx %s:\n", chain_b_lock_txid.hex())
|
self._log.info("spendBLockTx %s:\n", chain_b_lock_txid.hex())
|
||||||
|
|||||||
62
basicswap/interface/doge.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2024 The BasicSwap developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
from .btc import BTCInterface
|
||||||
|
from basicswap.chainparams import Coins
|
||||||
|
from basicswap.util.crypto import hash160
|
||||||
|
|
||||||
|
from basicswap.contrib.test_framework.script import (
|
||||||
|
CScript,
|
||||||
|
OP_DUP,
|
||||||
|
OP_CHECKSIG,
|
||||||
|
OP_HASH160,
|
||||||
|
OP_EQUAL,
|
||||||
|
OP_EQUALVERIFY,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DOGEInterface(BTCInterface):
|
||||||
|
@staticmethod
|
||||||
|
def coin_type():
|
||||||
|
return Coins.DOGE
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 192
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||||
|
return 192
|
||||||
|
|
||||||
|
def __init__(self, coin_settings, network, swap_client=None):
|
||||||
|
super(DOGEInterface, self).__init__(coin_settings, network, swap_client)
|
||||||
|
|
||||||
|
def getScriptDest(self, script: bytearray) -> bytearray:
|
||||||
|
# P2SH
|
||||||
|
|
||||||
|
script_hash = hash160(script)
|
||||||
|
assert len(script_hash) == 20
|
||||||
|
|
||||||
|
return CScript([OP_HASH160, script_hash, OP_EQUAL])
|
||||||
|
|
||||||
|
def getScriptForPubkeyHash(self, pkh: bytes) -> bytearray:
|
||||||
|
# Return P2PKH
|
||||||
|
return CScript([OP_DUP, OP_HASH160, pkh, OP_EQUALVERIFY, OP_CHECKSIG])
|
||||||
|
|
||||||
|
def encodeScriptDest(self, script_dest: bytes) -> str:
|
||||||
|
# Extract hash from script
|
||||||
|
script_hash = script_dest[2:-1]
|
||||||
|
return self.sh_to_address(script_hash)
|
||||||
|
|
||||||
|
def getBLockSpendTxFee(self, tx, fee_rate: int) -> int:
|
||||||
|
add_bytes = 107
|
||||||
|
size = len(tx.serialize_with_witness()) + add_bytes
|
||||||
|
pay_fee = round(fee_rate * size / 1000)
|
||||||
|
self._log.info(
|
||||||
|
f"BLockSpendTx fee_rate, size, fee: {fee_rate}, {size}, {pay_fee}."
|
||||||
|
)
|
||||||
|
return pay_fee
|
||||||
@@ -45,6 +45,9 @@ class FIROInterface(BTCInterface):
|
|||||||
self._rpcport, self._rpcauth, host=self._rpc_host
|
self._rpcport, self._rpcauth, host=self._rpc_host
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if "wallet_name" in coin_settings:
|
||||||
|
raise ValueError(f"Invalid setting for {self.coin_name()}: wallet_name")
|
||||||
|
|
||||||
def getExchangeName(self, exchange_name: str) -> str:
|
def getExchangeName(self, exchange_name: str) -> str:
|
||||||
return "zcoin"
|
return "zcoin"
|
||||||
|
|
||||||
@@ -102,7 +105,9 @@ class FIROInterface(BTCInterface):
|
|||||||
|
|
||||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||||
self.importWatchOnlyAddress(dest_address, "bid")
|
self.importWatchOnlyAddress(dest_address, "bid")
|
||||||
self._log.info("Imported watch-only addr: {}".format(dest_address))
|
self._log.info(
|
||||||
|
"Imported watch-only addr: {}".format(self._log.addr(dest_address))
|
||||||
|
)
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"Rescanning {} chain from height: {}".format(
|
"Rescanning {} chain from height: {}".format(
|
||||||
self.coin_name(), rescan_from
|
self.coin_name(), rescan_from
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2023 tecnovert
|
# Copyright (c) 2020-2023 tecnovert
|
||||||
|
# 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.
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ class LTCInterface(BTCInterface):
|
|||||||
|
|
||||||
def __init__(self, coin_settings, network, swap_client=None):
|
def __init__(self, coin_settings, network, swap_client=None):
|
||||||
super(LTCInterface, self).__init__(coin_settings, network, swap_client)
|
super(LTCInterface, self).__init__(coin_settings, network, swap_client)
|
||||||
self._rpc_wallet_mweb = "mweb"
|
self._rpc_wallet_mweb = coin_settings.get("mweb_wallet_name", "mweb")
|
||||||
self.rpc_wallet_mweb = make_rpc_func(
|
self.rpc_wallet_mweb = make_rpc_func(
|
||||||
self._rpcport,
|
self._rpcport,
|
||||||
self._rpcauth,
|
self._rpcauth,
|
||||||
@@ -52,7 +53,6 @@ class LTCInterface(BTCInterface):
|
|||||||
|
|
||||||
def getWalletInfo(self):
|
def getWalletInfo(self):
|
||||||
rv = super(LTCInterface, self).getWalletInfo()
|
rv = super(LTCInterface, self).getWalletInfo()
|
||||||
|
|
||||||
mweb_info = self.rpc_wallet_mweb("getwalletinfo")
|
mweb_info = self.rpc_wallet_mweb("getwalletinfo")
|
||||||
rv["mweb_balance"] = mweb_info["balance"]
|
rv["mweb_balance"] = mweb_info["balance"]
|
||||||
rv["mweb_unconfirmed"] = mweb_info["unconfirmed_balance"]
|
rv["mweb_unconfirmed"] = mweb_info["unconfirmed_balance"]
|
||||||
@@ -88,13 +88,13 @@ class LTCInterface(BTCInterface):
|
|||||||
|
|
||||||
|
|
||||||
class LTCInterfaceMWEB(LTCInterface):
|
class LTCInterfaceMWEB(LTCInterface):
|
||||||
@staticmethod
|
|
||||||
def coin_type():
|
def interface_type(self) -> int:
|
||||||
return Coins.LTC_MWEB
|
return Coins.LTC_MWEB
|
||||||
|
|
||||||
def __init__(self, coin_settings, network, swap_client=None):
|
def __init__(self, coin_settings, network, swap_client=None):
|
||||||
super(LTCInterfaceMWEB, self).__init__(coin_settings, network, swap_client)
|
super(LTCInterfaceMWEB, self).__init__(coin_settings, network, swap_client)
|
||||||
self._rpc_wallet = "mweb"
|
self._rpc_wallet = coin_settings.get("mweb_wallet_name", "mweb")
|
||||||
self.rpc_wallet = make_rpc_func(
|
self.rpc_wallet = make_rpc_func(
|
||||||
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet
|
self._rpcport, self._rpcauth, host=self._rpc_host, wallet=self._rpc_wallet
|
||||||
)
|
)
|
||||||
@@ -128,7 +128,7 @@ class LTCInterfaceMWEB(LTCInterface):
|
|||||||
|
|
||||||
self._log.info("init_wallet - {}".format(self.ticker()))
|
self._log.info("init_wallet - {}".format(self.ticker()))
|
||||||
|
|
||||||
self._log.info("Creating mweb wallet for {}.".format(self.coin_name()))
|
self._log.info(f"Creating wallet {self._rpc_wallet} for {self.coin_name()}.")
|
||||||
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup
|
# wallet_name, disable_private_keys, blank, passphrase, avoid_reuse, descriptors, load_on_startup
|
||||||
self.rpc("createwallet", ["mweb", False, True, password, False, False, True])
|
self.rpc("createwallet", ["mweb", False, True, password, False, False, True])
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ class LTCInterfaceMWEB(LTCInterface):
|
|||||||
self.rpc_wallet("walletpassphrase", [password, 100000000])
|
self.rpc_wallet("walletpassphrase", [password, 100000000])
|
||||||
|
|
||||||
if self.getWalletSeedID() == "Not found":
|
if self.getWalletSeedID() == "Not found":
|
||||||
self._sc.initialiseWallet(self.coin_type())
|
self._sc.initialiseWallet(self.interface_type())
|
||||||
|
|
||||||
# Workaround to trigger mweb_spk_man->LoadMWEBKeychain()
|
# Workaround to trigger mweb_spk_man->LoadMWEBKeychain()
|
||||||
self.rpc("unloadwallet", ["mweb"])
|
self.rpc("unloadwallet", ["mweb"])
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2023 tecnovert
|
# Copyright (c) 2023 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.
|
||||||
|
|
||||||
@@ -41,7 +41,6 @@ from basicswap.util.address import (
|
|||||||
from basicswap.util import (
|
from basicswap.util import (
|
||||||
b2i,
|
b2i,
|
||||||
i2b,
|
i2b,
|
||||||
i2h,
|
|
||||||
ensure,
|
ensure,
|
||||||
)
|
)
|
||||||
from basicswap.basicswap_util import (
|
from basicswap.basicswap_util import (
|
||||||
@@ -81,6 +80,9 @@ class NAVInterface(BTCInterface):
|
|||||||
self._rpcport, self._rpcauth, host=self._rpc_host
|
self._rpcport, self._rpcauth, host=self._rpc_host
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if "wallet_name" in coin_settings:
|
||||||
|
raise ValueError(f"Invalid setting for {self.coin_name()}: wallet_name")
|
||||||
|
|
||||||
def use_p2shp2wsh(self) -> bool:
|
def use_p2shp2wsh(self) -> bool:
|
||||||
# p2sh-p2wsh
|
# p2sh-p2wsh
|
||||||
return True
|
return True
|
||||||
@@ -549,7 +551,9 @@ class NAVInterface(BTCInterface):
|
|||||||
|
|
||||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||||
self.importWatchOnlyAddress(dest_address, "bid")
|
self.importWatchOnlyAddress(dest_address, "bid")
|
||||||
self._log.info("Imported watch-only addr: {}".format(dest_address))
|
self._log.info(
|
||||||
|
"Imported watch-only addr: {}".format(self._log.addr(dest_address))
|
||||||
|
)
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"Rescanning {} chain from height: {}".format(
|
"Rescanning {} chain from height: {}".format(
|
||||||
self.coin_name(), rescan_from
|
self.coin_name(), rescan_from
|
||||||
@@ -666,6 +670,7 @@ class NAVInterface(BTCInterface):
|
|||||||
cb_swap_value: int,
|
cb_swap_value: int,
|
||||||
b_fee: int,
|
b_fee: int,
|
||||||
restore_height: int,
|
restore_height: int,
|
||||||
|
spend_actual_balance: bool = False,
|
||||||
lock_tx_vout=None,
|
lock_tx_vout=None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
self._log.info("spendBLockTx %s:\n", chain_b_lock_txid.hex())
|
self._log.info("spendBLockTx %s:\n", chain_b_lock_txid.hex())
|
||||||
@@ -812,11 +817,14 @@ class NAVInterface(BTCInterface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockRefundTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize(), refund_script, tx.vout[0].nValue
|
return tx.serialize(), refund_script, tx.vout[0].nValue
|
||||||
@@ -867,11 +875,14 @@ class NAVInterface(BTCInterface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockRefundSpendTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
@@ -924,11 +935,14 @@ class NAVInterface(BTCInterface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockRefundSpendToFTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
@@ -971,11 +985,14 @@ class NAVInterface(BTCInterface):
|
|||||||
|
|
||||||
tx.rehash()
|
tx.rehash()
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockSpendTx {}{}.".format(
|
||||||
i2h(tx.sha256),
|
self._log.id(i2b(tx.sha256)),
|
||||||
tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tx.serialize()
|
return tx.serialize()
|
||||||
|
|||||||
@@ -2,7 +2,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.
|
||||||
|
|
||||||
@@ -66,6 +66,10 @@ class PARTInterface(BTCInterface):
|
|||||||
def txVersion() -> int:
|
def txVersion() -> int:
|
||||||
return 0xA0
|
return 0xA0
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 138
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
return 200
|
return 200
|
||||||
@@ -187,10 +191,18 @@ class PARTInterface(BTCInterface):
|
|||||||
|
|
||||||
|
|
||||||
class PARTInterfaceBlind(PARTInterface):
|
class PARTInterfaceBlind(PARTInterface):
|
||||||
|
|
||||||
|
def interface_type(self) -> int:
|
||||||
|
return Coins.PART_BLIND
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def balance_type():
|
def balance_type():
|
||||||
return BalanceTypes.BLIND
|
return BalanceTypes.BLIND
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 980
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
return 1032
|
return 1032
|
||||||
@@ -240,7 +252,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes) -> bytes:
|
def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes) -> bytes:
|
||||||
|
|
||||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||||
ephemeral_key = self.getNewSecretKey()
|
ephemeral_key = self.getNewRandomKey()
|
||||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||||
assert len(ephemeral_pubkey) == 33
|
assert len(ephemeral_pubkey) == 33
|
||||||
nonce = self.getScriptLockTxNonce(vkbv)
|
nonce = self.getScriptLockTxNonce(vkbv)
|
||||||
@@ -257,9 +269,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
]
|
]
|
||||||
params = [inputs, outputs]
|
params = [inputs, outputs]
|
||||||
rv = self.rpc_wallet("createrawparttransaction", params)
|
rv = self.rpc_wallet("createrawparttransaction", params)
|
||||||
|
return bytes.fromhex(rv["hex"])
|
||||||
tx_bytes = bytes.fromhex(rv["hex"])
|
|
||||||
return tx_bytes
|
|
||||||
|
|
||||||
def fundSCLockTx(self, tx_bytes: bytes, feerate: int, vkbv: bytes) -> bytes:
|
def fundSCLockTx(self, tx_bytes: bytes, feerate: int, vkbv: bytes) -> bytes:
|
||||||
feerate_str = self.format_amount(feerate)
|
feerate_str = self.format_amount(feerate)
|
||||||
@@ -288,7 +298,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
"lockUnspents": True,
|
"lockUnspents": True,
|
||||||
"feeRate": feerate_str,
|
"feeRate": feerate_str,
|
||||||
}
|
}
|
||||||
rv = self.rpc(
|
rv = self.rpc_wallet(
|
||||||
"fundrawtransactionfrom", ["blind", tx_hex, {}, outputs_info, options]
|
"fundrawtransactionfrom", ["blind", tx_hex, {}, outputs_info, options]
|
||||||
)
|
)
|
||||||
return bytes.fromhex(rv["hex"])
|
return bytes.fromhex(rv["hex"])
|
||||||
@@ -307,7 +317,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
lock_tx_obj = self.rpc("decoderawtransaction", [tx_lock_bytes.hex()])
|
lock_tx_obj = self.rpc("decoderawtransaction", [tx_lock_bytes.hex()])
|
||||||
assert self.getTxid(tx_lock_bytes).hex() == lock_tx_obj["txid"]
|
assert self.getTxid(tx_lock_bytes).hex() == lock_tx_obj["txid"]
|
||||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||||
ephemeral_key = self.getNewSecretKey()
|
ephemeral_key = self.getNewRandomKey()
|
||||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||||
assert len(ephemeral_pubkey) == 33
|
assert len(ephemeral_pubkey) == 33
|
||||||
nonce = self.getScriptLockTxNonce(vkbv)
|
nonce = self.getScriptLockTxNonce(vkbv)
|
||||||
@@ -348,7 +358,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||||
|
|
||||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||||
zero_change_key = self.getNewSecretKey()
|
zero_change_key = self.getNewRandomKey()
|
||||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||||
inputs_info = {
|
inputs_info = {
|
||||||
"0": {
|
"0": {
|
||||||
@@ -428,7 +438,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||||
|
|
||||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||||
zero_change_key = self.getNewSecretKey()
|
zero_change_key = self.getNewRandomKey()
|
||||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||||
inputs_info = {
|
inputs_info = {
|
||||||
"0": {
|
"0": {
|
||||||
@@ -467,7 +477,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
):
|
):
|
||||||
lock_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
lock_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
||||||
lock_txid_hex = lock_tx_obj["txid"]
|
lock_txid_hex = lock_tx_obj["txid"]
|
||||||
self._log.info("Verifying lock tx: {}.".format(lock_txid_hex))
|
self._log.info("Verifying lock tx: {}.".format(self._log.id(lock_txid_hex)))
|
||||||
|
|
||||||
ensure(lock_tx_obj["version"] == self.txVersion(), "Bad version")
|
ensure(lock_tx_obj["version"] == self.txVersion(), "Bad version")
|
||||||
ensure(lock_tx_obj["locktime"] == 0, "Bad nLockTime")
|
ensure(lock_tx_obj["locktime"] == 0, "Bad nLockTime")
|
||||||
@@ -531,7 +541,9 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
):
|
):
|
||||||
lock_refund_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
lock_refund_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
||||||
lock_refund_txid_hex = lock_refund_tx_obj["txid"]
|
lock_refund_txid_hex = lock_refund_tx_obj["txid"]
|
||||||
self._log.info("Verifying lock refund tx: {}.".format(lock_refund_txid_hex))
|
self._log.info(
|
||||||
|
"Verifying lock refund tx: {}.".format(self._log.id(lock_refund_txid_hex))
|
||||||
|
)
|
||||||
|
|
||||||
ensure(lock_refund_tx_obj["version"] == self.txVersion(), "Bad version")
|
ensure(lock_refund_tx_obj["version"] == self.txVersion(), "Bad version")
|
||||||
ensure(lock_refund_tx_obj["locktime"] == 0, "Bad nLockTime")
|
ensure(lock_refund_tx_obj["locktime"] == 0, "Bad nLockTime")
|
||||||
@@ -620,7 +632,9 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
lock_refund_spend_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
lock_refund_spend_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
||||||
lock_refund_spend_txid_hex = lock_refund_spend_tx_obj["txid"]
|
lock_refund_spend_txid_hex = lock_refund_spend_tx_obj["txid"]
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"Verifying lock refund spend tx: {}.".format(lock_refund_spend_txid_hex)
|
"Verifying lock refund spend tx: {}.".format(
|
||||||
|
self._log.id(lock_refund_spend_txid_hex)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
ensure(lock_refund_spend_tx_obj["version"] == self.txVersion(), "Bad version")
|
ensure(lock_refund_spend_tx_obj["version"] == self.txVersion(), "Bad version")
|
||||||
@@ -745,7 +759,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||||
|
|
||||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||||
zero_change_key = self.getNewSecretKey()
|
zero_change_key = self.getNewRandomKey()
|
||||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||||
inputs_info = {
|
inputs_info = {
|
||||||
"0": {
|
"0": {
|
||||||
@@ -779,11 +793,14 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
)
|
)
|
||||||
actual_tx_fee_rate = pay_fee * 1000 // vsize
|
actual_tx_fee_rate = pay_fee * 1000 // vsize
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
"createSCLockSpendTx {}{}.".format(
|
||||||
lock_spend_tx_obj["txid"],
|
self._log.id(lock_spend_tx_obj["txid"]),
|
||||||
actual_tx_fee_rate,
|
(
|
||||||
vsize,
|
""
|
||||||
pay_fee,
|
if self._log.safe_logs
|
||||||
|
else f":\n fee_rate, vsize, fee: {actual_tx_fee_rate}, {vsize}, {pay_fee}"
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
fee_info["vsize"] = vsize
|
fee_info["vsize"] = vsize
|
||||||
@@ -798,7 +815,9 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
):
|
):
|
||||||
lock_spend_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
lock_spend_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
||||||
lock_spend_txid_hex = lock_spend_tx_obj["txid"]
|
lock_spend_txid_hex = lock_spend_tx_obj["txid"]
|
||||||
self._log.info("Verifying lock spend tx: {}.".format(lock_spend_txid_hex))
|
self._log.info(
|
||||||
|
"Verifying lock spend tx: {}.".format(self._log.id(lock_spend_txid_hex))
|
||||||
|
)
|
||||||
|
|
||||||
ensure(lock_spend_tx_obj["version"] == self.txVersion(), "Bad version")
|
ensure(lock_spend_tx_obj["version"] == self.txVersion(), "Bad version")
|
||||||
ensure(lock_spend_tx_obj["locktime"] == 0, "Bad nLockTime")
|
ensure(lock_spend_tx_obj["locktime"] == 0, "Bad nLockTime")
|
||||||
@@ -949,7 +968,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||||
|
|
||||||
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
# Use a junk change pubkey to avoid adding unused keys to the wallet
|
||||||
zero_change_key = self.getNewSecretKey()
|
zero_change_key = self.getNewRandomKey()
|
||||||
zero_change_pubkey = self.getPubkey(zero_change_key)
|
zero_change_pubkey = self.getPubkey(zero_change_key)
|
||||||
inputs_info = {
|
inputs_info = {
|
||||||
"0": {
|
"0": {
|
||||||
@@ -1158,10 +1177,44 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
sub_fee: bool = False,
|
sub_fee: bool = False,
|
||||||
lock_unspents: bool = True,
|
lock_unspents: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
txn = self.rpc_wallet(
|
# Estimate lock tx size / fee
|
||||||
"createrawtransaction", [[], {addr_to: self.format_amount(amount)}]
|
|
||||||
|
# self.createSCLockTx
|
||||||
|
vkbv = self.getNewRandomKey()
|
||||||
|
ephemeral_key = self.getNewRandomKey()
|
||||||
|
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||||
|
assert len(ephemeral_pubkey) == 33
|
||||||
|
nonce = self.getScriptLockTxNonce(vkbv)
|
||||||
|
inputs = []
|
||||||
|
outputs = [
|
||||||
|
{
|
||||||
|
"type": "blind",
|
||||||
|
"amount": self.format_amount(amount),
|
||||||
|
"address": addr_to,
|
||||||
|
"nonce": nonce.hex(),
|
||||||
|
"data": ephemeral_pubkey.hex(),
|
||||||
|
}
|
||||||
|
]
|
||||||
|
params = [inputs, outputs]
|
||||||
|
tx_hex = self.rpc_wallet("createrawparttransaction", params)["hex"]
|
||||||
|
|
||||||
|
# self.fundSCLockTx
|
||||||
|
tx_obj = self.rpc("decoderawtransaction", [tx_hex])
|
||||||
|
|
||||||
|
assert len(tx_obj["vout"]) == 1
|
||||||
|
txo = tx_obj["vout"][0]
|
||||||
|
blinded_info = self.rpc(
|
||||||
|
"rewindrangeproof", [txo["rangeproof"], txo["valueCommitment"], nonce.hex()]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
outputs_info = {
|
||||||
|
0: {
|
||||||
|
"value": blinded_info["amount"],
|
||||||
|
"blind": blinded_info["blind"],
|
||||||
|
"nonce": nonce.hex(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
"lockUnspents": lock_unspents,
|
"lockUnspents": lock_unspents,
|
||||||
"conf_target": self._conf_target,
|
"conf_target": self._conf_target,
|
||||||
@@ -1170,14 +1223,24 @@ class PARTInterfaceBlind(PARTInterface):
|
|||||||
options["subtractFeeFromOutputs"] = [
|
options["subtractFeeFromOutputs"] = [
|
||||||
0,
|
0,
|
||||||
]
|
]
|
||||||
return self.rpc_wallet("fundrawtransactionfrom", ["blind", txn, options])["hex"]
|
return self.rpc_wallet(
|
||||||
|
"fundrawtransactionfrom", ["blind", tx_hex, {}, outputs_info, options]
|
||||||
|
)["hex"]
|
||||||
|
|
||||||
|
|
||||||
class PARTInterfaceAnon(PARTInterface):
|
class PARTInterfaceAnon(PARTInterface):
|
||||||
|
|
||||||
|
def interface_type(self) -> int:
|
||||||
|
return Coins.PART_ANON
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def balance_type():
|
def balance_type():
|
||||||
return BalanceTypes.ANON
|
return BalanceTypes.ANON
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
return 1153
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
raise ValueError("Not possible")
|
raise ValueError("Not possible")
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2024 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.
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,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.
|
||||||
|
|
||||||
@@ -71,6 +71,11 @@ class XMRInterface(CoinInterface):
|
|||||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||||
raise ValueError("Not possible")
|
raise ValueError("Not possible")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def est_lock_tx_vsize() -> int:
|
||||||
|
# TODO: Estimate with ringsize
|
||||||
|
return 1604
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||||
# TODO: Estimate with ringsize
|
# TODO: Estimate with ringsize
|
||||||
@@ -78,7 +83,7 @@ class XMRInterface(CoinInterface):
|
|||||||
|
|
||||||
def is_transient_error(self, ex) -> bool:
|
def is_transient_error(self, ex) -> bool:
|
||||||
str_error: str = str(ex).lower()
|
str_error: str = str(ex).lower()
|
||||||
if "failed to get output distribution" in str_error:
|
if "failed to get earliest fork height" in str_error:
|
||||||
return True
|
return True
|
||||||
return super().is_transient_error(ex)
|
return super().is_transient_error(ex)
|
||||||
|
|
||||||
@@ -94,6 +99,7 @@ class XMRInterface(CoinInterface):
|
|||||||
self._log = self._sc.log if self._sc and self._sc.log else logging
|
self._log = self._sc.log if self._sc and self._sc.log else logging
|
||||||
self._wallet_password = None
|
self._wallet_password = None
|
||||||
self._have_checked_seed = False
|
self._have_checked_seed = False
|
||||||
|
self._wallet_filename = coin_settings.get("wallet_name", "swap_wallet")
|
||||||
|
|
||||||
daemon_login = None
|
daemon_login = None
|
||||||
if coin_settings.get("rpcuser", "") != "":
|
if coin_settings.get("rpcuser", "") != "":
|
||||||
@@ -170,14 +176,23 @@ class XMRInterface(CoinInterface):
|
|||||||
ensure(new_priority >= 0 and new_priority < 4, "Invalid fee_priority value")
|
ensure(new_priority >= 0 and new_priority < 4, "Invalid fee_priority value")
|
||||||
self._fee_priority = new_priority
|
self._fee_priority = new_priority
|
||||||
|
|
||||||
def setWalletFilename(self, wallet_filename):
|
|
||||||
self._wallet_filename = wallet_filename
|
|
||||||
|
|
||||||
def createWallet(self, params):
|
def createWallet(self, params):
|
||||||
if self._wallet_password is not None:
|
if self._wallet_password is not None:
|
||||||
params["password"] = self._wallet_password
|
params["password"] = self._wallet_password
|
||||||
rv = self.rpc_wallet("generate_from_keys", params)
|
rv = self.rpc_wallet("generate_from_keys", params)
|
||||||
self._log.info("generate_from_keys %s", dumpj(rv))
|
if "address" in rv:
|
||||||
|
new_address: str = rv["address"]
|
||||||
|
is_watch_only: bool = "Watch-only" in rv.get("info", "")
|
||||||
|
self._log.info(
|
||||||
|
"Generated{} {} wallet: {}".format(
|
||||||
|
" watch-only" if is_watch_only else "",
|
||||||
|
self.coin_name(),
|
||||||
|
self._log.addr(new_address),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self._log.debug("generate_from_keys %s", dumpj(rv))
|
||||||
|
raise ValueError("generate_from_keys failed")
|
||||||
|
|
||||||
def openWallet(self, filename):
|
def openWallet(self, filename):
|
||||||
params = {"filename": filename}
|
params = {"filename": filename}
|
||||||
@@ -326,7 +341,7 @@ class XMRInterface(CoinInterface):
|
|||||||
|
|
||||||
return float(self.format_amount(fee_per_k_bytes)), "get_fee_estimate"
|
return float(self.format_amount(fee_per_k_bytes)), "get_fee_estimate"
|
||||||
|
|
||||||
def getNewSecretKey(self) -> bytes:
|
def getNewRandomKey(self) -> bytes:
|
||||||
# Note: Returned bytes are in big endian order
|
# Note: Returned bytes are in big endian order
|
||||||
return i2b(edu.get_secret())
|
return i2b(edu.get_secret())
|
||||||
|
|
||||||
@@ -403,7 +418,9 @@ class XMRInterface(CoinInterface):
|
|||||||
params["priority"] = self._fee_priority
|
params["priority"] = self._fee_priority
|
||||||
rv = self.rpc_wallet("transfer", params)
|
rv = self.rpc_wallet("transfer", params)
|
||||||
self._log.info(
|
self._log.info(
|
||||||
"publishBLockTx %s to address_b58 %s", rv["tx_hash"], shared_addr
|
"publishBLockTx %s to address_b58 %s",
|
||||||
|
self._log.id(rv["tx_hash"]),
|
||||||
|
self._log.addr(shared_addr),
|
||||||
)
|
)
|
||||||
tx_hash = bytes.fromhex(rv["tx_hash"])
|
tx_hash = bytes.fromhex(rv["tx_hash"])
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
|
# 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.
|
||||||
|
|
||||||
@@ -14,6 +15,7 @@ from .util import (
|
|||||||
)
|
)
|
||||||
from .basicswap_util import (
|
from .basicswap_util import (
|
||||||
strBidState,
|
strBidState,
|
||||||
|
strTxState,
|
||||||
SwapTypes,
|
SwapTypes,
|
||||||
NotificationTypes as NT,
|
NotificationTypes as NT,
|
||||||
)
|
)
|
||||||
@@ -250,6 +252,8 @@ def js_offers(self, url_split, post_string, is_json, sent=False) -> bytes:
|
|||||||
"is_expired": o.expire_at <= swap_client.getTime(),
|
"is_expired": o.expire_at <= swap_client.getTime(),
|
||||||
"is_own_offer": o.was_sent,
|
"is_own_offer": o.was_sent,
|
||||||
"is_revoked": True if o.active_ind == 2 else False,
|
"is_revoked": True if o.active_ind == 2 else False,
|
||||||
|
"is_public": o.addr_to == swap_client.network_addr
|
||||||
|
or o.addr_to.strip() == "",
|
||||||
}
|
}
|
||||||
if with_extra_info:
|
if with_extra_info:
|
||||||
offer_data["amount_negotiable"] = o.amount_negotiable
|
offer_data["amount_negotiable"] = o.amount_negotiable
|
||||||
@@ -317,18 +321,36 @@ def formatBids(swap_client, bids, filters) -> bytes:
|
|||||||
with_extra_info = filters.get("with_extra_info", False)
|
with_extra_info = filters.get("with_extra_info", False)
|
||||||
rv = []
|
rv = []
|
||||||
for b in bids:
|
for b in bids:
|
||||||
|
ci_from = swap_client.ci(b[9])
|
||||||
|
offer = swap_client.getOffer(b[3])
|
||||||
|
ci_to = swap_client.ci(offer.coin_to) if offer else None
|
||||||
|
|
||||||
|
amount_to = None
|
||||||
|
if ci_to:
|
||||||
|
amount_to = ci_to.format_amount(
|
||||||
|
(b[4] * b[10]) // ci_from.COIN()
|
||||||
|
)
|
||||||
|
|
||||||
bid_data = {
|
bid_data = {
|
||||||
"bid_id": b[2].hex(),
|
"bid_id": b[2].hex(),
|
||||||
"offer_id": b[3].hex(),
|
"offer_id": b[3].hex(),
|
||||||
"created_at": b[0],
|
"created_at": b[0],
|
||||||
"expire_at": b[1],
|
"expire_at": b[1],
|
||||||
"coin_from": b[9],
|
"coin_from": ci_from.coin_name(),
|
||||||
"amount_from": swap_client.ci(b[9]).format_amount(b[4]),
|
"coin_to": ci_to.coin_name() if ci_to else "Unknown",
|
||||||
|
"amount_from": ci_from.format_amount(b[4]),
|
||||||
|
"amount_to": amount_to,
|
||||||
"bid_rate": swap_client.ci(b[14]).format_amount(b[10]),
|
"bid_rate": swap_client.ci(b[14]).format_amount(b[10]),
|
||||||
"bid_state": strBidState(b[5]),
|
"bid_state": strBidState(b[5]),
|
||||||
|
"addr_from": b[11],
|
||||||
|
"addr_to": offer.addr_to if offer else None
|
||||||
}
|
}
|
||||||
|
|
||||||
if with_extra_info:
|
if with_extra_info:
|
||||||
bid_data["addr_from"] = b[11]
|
bid_data.update({
|
||||||
|
"tx_state_a": strTxState(b[7]),
|
||||||
|
"tx_state_b": strTxState(b[8])
|
||||||
|
})
|
||||||
rv.append(bid_data)
|
rv.append(bid_data)
|
||||||
return bytes(json.dumps(rv), "UTF-8")
|
return bytes(json.dumps(rv), "UTF-8")
|
||||||
|
|
||||||
@@ -704,7 +726,10 @@ def js_identities(self, url_split, post_string: str, is_json: bool) -> bytes:
|
|||||||
ensure("address" in filters, "Must provide an address to modify data")
|
ensure("address" in filters, "Must provide an address to modify data")
|
||||||
swap_client.setIdentityData(filters, set_data)
|
swap_client.setIdentityData(filters, set_data)
|
||||||
|
|
||||||
return bytes(json.dumps(swap_client.listIdentities(filters)), "UTF-8")
|
rv = swap_client.listIdentities(filters)
|
||||||
|
if "address" in filters:
|
||||||
|
rv = {} if len(rv) < 1 else rv[0]
|
||||||
|
return bytes(json.dumps(rv), "UTF-8")
|
||||||
|
|
||||||
|
|
||||||
def js_automationstrategies(self, url_split, post_string: str, is_json: bool) -> bytes:
|
def js_automationstrategies(self, url_split, post_string: str, is_json: bool) -> bytes:
|
||||||
@@ -829,28 +854,40 @@ def js_getcoinseed(self, url_split, post_string, is_json) -> bytes:
|
|||||||
raise ValueError("Particl wallet seed is set from the Basicswap mnemonic.")
|
raise ValueError("Particl wallet seed is set from the Basicswap mnemonic.")
|
||||||
|
|
||||||
ci = swap_client.ci(coin)
|
ci = swap_client.ci(coin)
|
||||||
|
rv = {"coin": ci.ticker()}
|
||||||
if coin in (Coins.XMR, Coins.WOW):
|
if coin in (Coins.XMR, Coins.WOW):
|
||||||
key_view = swap_client.getWalletKey(coin, 1, for_ed25519=True)
|
key_view = swap_client.getWalletKey(coin, 1, for_ed25519=True)
|
||||||
key_spend = swap_client.getWalletKey(coin, 2, for_ed25519=True)
|
key_spend = swap_client.getWalletKey(coin, 2, for_ed25519=True)
|
||||||
address = ci.getAddressFromKeys(key_view, key_spend)
|
address = ci.getAddressFromKeys(key_view, key_spend)
|
||||||
return bytes(
|
|
||||||
json.dumps(
|
expect_address = swap_client.getCachedMainWalletAddress(ci)
|
||||||
{
|
rv.update(
|
||||||
"coin": ci.ticker(),
|
{
|
||||||
"key_view": ci.encodeKey(key_view),
|
"key_view": ci.encodeKey(key_view),
|
||||||
"key_spend": ci.encodeKey(key_spend),
|
"key_spend": ci.encodeKey(key_spend),
|
||||||
"address": address,
|
"address": address,
|
||||||
}
|
"expected_address": (
|
||||||
),
|
"Unset" if expect_address is None else expect_address
|
||||||
"UTF-8",
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
seed_key = swap_client.getWalletKey(coin, 1)
|
||||||
|
seed_id = ci.getSeedHash(seed_key)
|
||||||
|
expect_seedid = swap_client.getStringKV(
|
||||||
|
"main_wallet_seedid_" + ci.coin_name().lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
rv.update(
|
||||||
|
{
|
||||||
|
"seed": seed_key.hex(),
|
||||||
|
"seed_id": seed_id.hex(),
|
||||||
|
"expected_seed_id": "Unset" if expect_seedid is None else expect_seedid,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
seed_key = swap_client.getWalletKey(coin, 1)
|
|
||||||
seed_id = ci.getSeedHash(seed_key)
|
|
||||||
return bytes(
|
return bytes(
|
||||||
json.dumps(
|
json.dumps(rv),
|
||||||
{"coin": ci.ticker(), "seed": seed_key.hex(), "seed_id": seed_id.hex()}
|
|
||||||
),
|
|
||||||
"UTF-8",
|
"UTF-8",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -943,6 +980,67 @@ def js_readurl(self, url_split, post_string, is_json) -> bytes:
|
|||||||
raise ValueError("Requires URL.")
|
raise ValueError("Requires URL.")
|
||||||
|
|
||||||
|
|
||||||
|
def js_active(self, url_split, post_string, is_json) -> bytes:
|
||||||
|
swap_client = self.server.swap_client
|
||||||
|
swap_client.checkSystemStatus()
|
||||||
|
filters = {
|
||||||
|
"sort_by": "created_at",
|
||||||
|
"sort_dir": "desc"
|
||||||
|
}
|
||||||
|
EXCLUDED_STATES = [
|
||||||
|
'Completed',
|
||||||
|
'Expired',
|
||||||
|
'Timed-out',
|
||||||
|
'Abandoned',
|
||||||
|
'Failed, refunded',
|
||||||
|
'Failed, swiped',
|
||||||
|
'Failed',
|
||||||
|
'Error',
|
||||||
|
'received'
|
||||||
|
]
|
||||||
|
all_bids = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
received_bids = swap_client.listBids(filters=filters)
|
||||||
|
sent_bids = swap_client.listBids(sent=True, filters=filters)
|
||||||
|
for bid in received_bids + sent_bids:
|
||||||
|
try:
|
||||||
|
bid_state = strBidState(bid[5])
|
||||||
|
tx_state_a = strTxState(bid[7])
|
||||||
|
tx_state_b = strTxState(bid[8])
|
||||||
|
if bid_state in EXCLUDED_STATES:
|
||||||
|
continue
|
||||||
|
offer = swap_client.getOffer(bid[3])
|
||||||
|
if not offer:
|
||||||
|
continue
|
||||||
|
swap_data = {
|
||||||
|
"bid_id": bid[2].hex(),
|
||||||
|
"offer_id": bid[3].hex(),
|
||||||
|
"created_at": bid[0],
|
||||||
|
"bid_state": bid_state,
|
||||||
|
"tx_state_a": tx_state_a if tx_state_a else 'None',
|
||||||
|
"tx_state_b": tx_state_b if tx_state_b else 'None',
|
||||||
|
"coin_from": swap_client.ci(bid[9]).coin_name(),
|
||||||
|
"coin_to": swap_client.ci(offer.coin_to).coin_name(),
|
||||||
|
"amount_from": swap_client.ci(bid[9]).format_amount(bid[4]),
|
||||||
|
"amount_to": swap_client.ci(offer.coin_to).format_amount(
|
||||||
|
(bid[4] * bid[10]) // swap_client.ci(bid[9]).COIN()
|
||||||
|
),
|
||||||
|
"addr_from": bid[11],
|
||||||
|
"status": {
|
||||||
|
"main": bid_state,
|
||||||
|
"initial_tx": tx_state_a if tx_state_a else 'None',
|
||||||
|
"payment_tx": tx_state_b if tx_state_b else 'None'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all_bids.append(swap_data)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
except Exception:
|
||||||
|
return bytes(json.dumps([]), "UTF-8")
|
||||||
|
return bytes(json.dumps(all_bids), "UTF-8")
|
||||||
|
|
||||||
|
|
||||||
pages = {
|
pages = {
|
||||||
"coins": js_coins,
|
"coins": js_coins,
|
||||||
"wallets": js_wallets,
|
"wallets": js_wallets,
|
||||||
@@ -968,6 +1066,7 @@ pages = {
|
|||||||
"lock": js_lock,
|
"lock": js_lock,
|
||||||
"help": js_help,
|
"help": js_help,
|
||||||
"readurl": js_readurl,
|
"readurl": js_readurl,
|
||||||
|
"active": js_active,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1257
basicswap/pgp/keys/bitcoin_laanwj.pgp
Normal file
166
basicswap/pgp/keys/dash_pasta.pgp
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
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
|
||||||
|
t8FIucvlOdt4EqKaSmoBQZk0Aj/N5q4=
|
||||||
|
=vjZr
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
31
basicswap/pgp/keys/dogecoin_patricklodder.pgp
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
mQENBF8V/EkBCAC8YTo6YJLNY0To+25b+dcSRcMCo/g9TJlraoJagO9Hr0Njbryg
|
||||||
|
jG5iptxi6UjDD+8xPK7YYRhaKyzJq1yTjGe5u5WEEtMfNaiVgA6dSEOXTdH4xT6q
|
||||||
|
v3VundebzZ7TFue7kj7fzEh7t9x2k5+RI2RvOs26ANEBKgJliQIZDXKOLcQuW7k9
|
||||||
|
9pWvqMWqRyn8WVGNf/UGBoFDcXQ1wo3h6m/LMJIO5L2IGlQWPmc8WT3uHJ/X/5Ln
|
||||||
|
slQ1ml7h+JjNwN0rAY/ZaJHSEi2y0RtLRzISP0EsA6EbqvJNGI8jqs5rpImgUn9U
|
||||||
|
8Q8Xz6hLPAiVTmteF63LlKo03wRcH8d/FVSvABEBAAG0N1BhdHJpY2sgTG9kZGVy
|
||||||
|
IDxwYXRyaWNrbG9kZGVyQHVzZXJzLm5vcmVwbHkuZ2l0aHViLmNvbT6JAVQEEwEI
|
||||||
|
AD4CGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTcbvSov58bHk3h7lItOjRb
|
||||||
|
mNDcHwUCYtNqvwUJB3/VdgAKCRAtOjRbmNDcH+sVB/9jGPwrd1Om6L3ALzkZniR7
|
||||||
|
ODYFN4m8MRC4LPH2Ngt1Ea3/5DA68hEzQVGAFF+m7i7ZH9bmTvGB9R+qqF9WLTRc
|
||||||
|
aoO0XvYI8YrRLuhZFazafsLFRD5/c6QfpkBAjiDuxNIjEg2i+nY3avraxicKQKBY
|
||||||
|
PWWY0TFbz8K+CgIBh8Dnv7lqcxCFWHit/KHHjGAOvIPD5sLtv42dYk4TBEff4MVK
|
||||||
|
CzuCQtU8viy5doQPYHwfNADpOguskiNtFZmG2iPwgIE2tzHpLG2kidzZvJbHDcXY
|
||||||
|
XP13FnLvONf2bkS11gZSRm8pa6uay8/KfBNlCeMOYQDVoCuBbD5/2MwuV6o6OfSI
|
||||||
|
uQENBF8V/EkBCADN8eWUf0OtQdthNoWhRgotz/EzLI9r3sVv2SqbA++rHW9TC7mB
|
||||||
|
Wl/3e5emXWgKI1EK1Poz5HeKnL3SRx3xizgBTK6+RNQK6svvaLwcx06y8pZP9RqX
|
||||||
|
jLaRR67fXZCL+ulPtTcbt/JwlaTaokwWsgfy3UZRcK33llLbvWFjht2OGfx8B6Z9
|
||||||
|
UFRxW4sP0HuE3RrnMATGymWvOZlwYDr73HltksnOEFkz4lVP5VK9kdbndQjIB3Cf
|
||||||
|
zw/waTqjX+xXjJsFMYZhEDARhP5BQIoQvEv8KRtptNoLJGFZ9RGf+fIHiar2GAZL
|
||||||
|
4WZbZ0IuGLj419TkgvsUkI83Bx97DkS5Xa+jABEBAAGJATwEGAEIACYCGwwWIQTc
|
||||||
|
bvSov58bHk3h7lItOjRbmNDcHwUCYtNq0AUJB3/VhwAKCRAtOjRbmNDcH8cfB/4q
|
||||||
|
Puoir46sAGHBJt4TVe+R5ErVmGfGVUc3n6svguJnRMTAi1gpb6EapjdR9gUx+3Ja
|
||||||
|
wUE1keJuw5xeFi2JGp/XHt+8LAhsRAaLA4YViho8KL3yjzARvqrkYfl+FuO6kZIj
|
||||||
|
FEPJjRI1hOx5pWtPa3L3GZOexYDhRVdIJDci3gbFmU8HjgFx0G50zAysGR4DLVXj
|
||||||
|
FQBPvt4asUTdx30HU/pxWqFEzAeJPOVyjoxotdsMcIYXVBDhte5eADJ4OSMmc7k3
|
||||||
|
k46yHnbD4wyqqGtWqxHitTrl2U+M5MO5rlOZpGtIMtHz186OyMySZ5Gc886vPlOG
|
||||||
|
XgtNHT7E4rDrhySwy6Yk
|
||||||
|
=DQYN
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
41
basicswap/pgp/keys/dogecoin_xanimo.pgp
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
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
|
||||||
|
tS35OCTSJzNMoXtTZqCkDLc9
|
||||||
|
=Z8rt
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
@@ -1,52 +1,52 @@
|
|||||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
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
|
||||||
PSxdIAmV43ZrNIOxKzYOsA==
|
PSxdIAmV43ZrNIOxKzYOsA==
|
||||||
=w412
|
=w412
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
161
basicswap/pgp/keys/nicolasdorier.asc
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Comment: https://keybase.io/nicolasdorier
|
||||||
|
Version: Keybase Go 2.6.0 (linux)
|
||||||
|
|
||||||
|
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
|
||||||
|
=sNb2
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Comment: https://keybase.io/nicolasdorier
|
||||||
|
Version: Keybase Go 5.0.0 (windows)
|
||||||
|
|
||||||
|
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
|
||||||
|
=ut7t
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
|
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Comment: https://keybase.io/nicolasdorier
|
||||||
|
Version: Keybase Go 5.0.0 (windows)
|
||||||
|
|
||||||
|
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
|
||||||
|
=n82A
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iQIzBAABCAAdFiEEjlF9wS7BzDf2QjqKE/E2UcnPDWsFAmeP/40ACgkQE/E2UcnP
|
||||||
|
DWuraw/9HCuAZG+D6xSLWmjA9Dtj9OZMEOIxqvxw+1e2KQ5ek4d1waL63NWFQfMi
|
||||||
|
fDlKKeFbZoL6Dfjbx0GoUJKTfrIVKog6DlVzIi5PuUwPOCBFuLl0g5kHlC20jbPw
|
||||||
|
nu7T6fj6/oD/lqo0rzFDkbsX7Fk4GGC7rYLKfdtYhDgMq9ro7QhSxAOJanRyqzXL
|
||||||
|
dvPNxlyksOyttJLSAZI9BOkrpTWoyb3asOli5oHgdcheHd/2fjby69huS3UWEjdO
|
||||||
|
9Bm73UFlxF2hxCTc2Fqvvb3SBDmNCLlFM0f+DDJNMJGUQViVCar0YRw3R+/NBo83
|
||||||
|
ptutp3bpabHijQFEEpIx/19nh9RQMJjaHHHqdPcTeg8bU/Yeq36TI7gsCenK0mQT
|
||||||
|
75MscvJAG0enoKVrTZez5ner9ZwLOevAKzRe4huRJZZjM8gM6sb2OKslJLqTxEVt
|
||||||
|
G3b8BLB9IUAxCeyuvGSG/3RV3MgZLnLy5MLYjh72+Kmo6HpuajJwPuvUck5ZYcGE
|
||||||
|
jjeRFZmqZj0FtCrcfStau/0liyAxU5k/43RwMvujO1uTTgOVHw1QhhMEkZ9bYhhO
|
||||||
|
JgeCEkwL1Bjjved1NSySjZbt2sFbG89as14ezHxgc4HaujJ6bGkINnkPOPWM1tk4
|
||||||
|
DjjEO/0PY9i0m/ivQUXf5ZPSnlkAR8x6Ve2S2MvQd7nFoS/YfLs=
|
||||||
|
=0pTn
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
21
basicswap/pgp/sigs/utxo-snapshot-bitcoin-mainnet-hashes.asc
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA256
|
||||||
|
|
||||||
|
725a049bc5a9fd60b05bba4d4825d35115d99f05ab5b7716d4507c295d05172d utxo-snapshot-bitcoin-mainnet-820852.tar
|
||||||
|
744c42885df700513331a978b289d9c9d5b27e0cf1147f2f5a287b4492ff940c utxo-snapshot-bitcoin-mainnet-867690.tar
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iQIzBAEBCAAdFiEEjlF9wS7BzDf2QjqKE/E2UcnPDWsFAmedUAYACgkQE/E2UcnP
|
||||||
|
DWs1Vw/+P3CGP9LLVv2deNocBFunUz+7aDZsQiykSI8ws50ssJ5PsAg5VSl4CbCl
|
||||||
|
owWOdQVJiDUh7daP0jr+bt3X2FY5ORBb1TGlvfCHE+vLfEFDnTpLXouSCclP0cv8
|
||||||
|
Ci8zQFKSI5Pf6uSMpALgQZxBgNU/0IegAQbpuJI4nrQXTKHJcMqtw1LtnmcreESO
|
||||||
|
MsSiGCXnC1R+xGQjptfvbzXaQVrin7ctYA9zjN4CGbjNChzr+ywT8dht2RKoLYyP
|
||||||
|
OrEys7d8EIaw/ktRvRmyk6O7KmnvUhf0uuFlDq+eTiBIpQoUEovCow1YYKaWkIRB
|
||||||
|
r4JBJJ34AB+XC2hgi5jpJNub/wKgVBm0iy79zZOSILP3ymbn3iJGg4ifUF0YeZCU
|
||||||
|
ufYkYi3iTJDpwYr0tylZmBiwsWNcbUhB+WTNX7ogCW70ZuhrF0PJQRPmhI34vsE/
|
||||||
|
qg3n0/hNNsypy0epRd33KSOvrSmaoTKLtCax9Osnt+F+yTYjD5EPqkQuzlJl+fDe
|
||||||
|
VvjWO5XHuaRvzijBrJQz6r5V4e/0ioNa8FTRqWmMTO1wHmxF5glpozyKycv9+bsB
|
||||||
|
IL9F1IQjhPkSVI7Hw8bsURpfH4mV+9eZJJDIvBf1/0gDctsBdsI5+5jxZjup769Q
|
||||||
|
AmMsGeZoplm/eUofQ9hItWcVitPhisDmC3wDR71UKM0b9FF6IUY=
|
||||||
|
=YUjt
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
@@ -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,9 +15,10 @@ from basicswap.chainparams import (
|
|||||||
Coins,
|
Coins,
|
||||||
)
|
)
|
||||||
from basicswap.basicswap_util import (
|
from basicswap.basicswap_util import (
|
||||||
|
EventLogTypes,
|
||||||
KeyTypes,
|
KeyTypes,
|
||||||
SwapTypes,
|
SwapTypes,
|
||||||
EventLogTypes,
|
TxTypes,
|
||||||
)
|
)
|
||||||
from . import ProtocolInterface
|
from . import ProtocolInterface
|
||||||
from basicswap.contrib.test_framework.script import CScript, CScriptOp, OP_CHECKMULTISIG
|
from basicswap.contrib.test_framework.script import CScript, CScriptOp, OP_CHECKMULTISIG
|
||||||
@@ -43,7 +44,7 @@ def addLockRefundSigs(self, xmr_swap, ci):
|
|||||||
|
|
||||||
|
|
||||||
def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
||||||
self.log.info(f"Manually recovering {bid_id.hex()}")
|
self.log.info(f"Manually recovering {self.log.id(bid_id)}")
|
||||||
# Manually recover txn if other key is known
|
# Manually recover txn if other key is known
|
||||||
try:
|
try:
|
||||||
use_cursor = self.openDB(cursor)
|
use_cursor = self.openDB(cursor)
|
||||||
@@ -55,7 +56,7 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
|||||||
ensure(xmr_offer, "Adaptor-sig offer not found: {}.".format(bid.offer_id.hex()))
|
ensure(xmr_offer, "Adaptor-sig offer not found: {}.".format(bid.offer_id.hex()))
|
||||||
|
|
||||||
# The no-script coin is always the follower
|
# The no-script coin is always the follower
|
||||||
reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from)
|
reverse_bid: bool = self.is_reverse_ads_bid(offer.coin_from, offer.coin_to)
|
||||||
ci_from = self.ci(Coins(offer.coin_from))
|
ci_from = self.ci(Coins(offer.coin_from))
|
||||||
ci_to = self.ci(Coins(offer.coin_to))
|
ci_to = self.ci(Coins(offer.coin_to))
|
||||||
ci_follower = ci_from if reverse_bid else ci_to
|
ci_follower = ci_from if reverse_bid else ci_to
|
||||||
@@ -89,16 +90,20 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
|||||||
summed_pkbs = ci_follower.getPubkey(vkbs)
|
summed_pkbs = ci_follower.getPubkey(vkbs)
|
||||||
if summed_pkbs != xmr_swap.pkbs:
|
if summed_pkbs != xmr_swap.pkbs:
|
||||||
err_msg: str = "Summed key does not match expected wallet spend pubkey"
|
err_msg: str = "Summed key does not match expected wallet spend pubkey"
|
||||||
have_pk = summed_pkbs.hex()
|
self.log.error(
|
||||||
expect_pk = xmr_swap.pkbs.hex()
|
f"{err_msg}. Got: {summed_pkbs.hex()}, Expect: {xmr_swap.pkbs.hex()}"
|
||||||
self.log.error(f"{err_msg}. Got: {have_pk}, Expect: {expect_pk}")
|
)
|
||||||
raise ValueError(err_msg)
|
raise ValueError(err_msg)
|
||||||
|
|
||||||
if ci_follower.coin_type() in (Coins.XMR, Coins.WOW):
|
coin_to: int = ci_follower.interface_type()
|
||||||
|
base_coin_to: int = ci_follower.coin_type()
|
||||||
|
if coin_to in (Coins.XMR, Coins.WOW):
|
||||||
address_to = self.getCachedMainWalletAddress(ci_follower, use_cursor)
|
address_to = self.getCachedMainWalletAddress(ci_follower, use_cursor)
|
||||||
|
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
|
||||||
|
address_to = self.getCachedStealthAddressForCoin(base_coin_to, use_cursor)
|
||||||
else:
|
else:
|
||||||
address_to = self.getCachedStealthAddressForCoin(
|
address_to = self.getReceiveAddressFromPool(
|
||||||
ci_follower.coin_type(), use_cursor
|
base_coin_to, bid_id, TxTypes.XMR_SWAP_B_LOCK_SPEND, use_cursor
|
||||||
)
|
)
|
||||||
amount = bid.amount_to
|
amount = bid.amount_to
|
||||||
lock_tx_vout = bid.getLockTXBVout()
|
lock_tx_vout = bid.getLockTXBVout()
|
||||||
@@ -114,10 +119,7 @@ def recoverNoScriptTxnWithKey(self, bid_id: bytes, encoded_key, cursor=None):
|
|||||||
lock_tx_vout=lock_tx_vout,
|
lock_tx_vout=lock_tx_vout,
|
||||||
)
|
)
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"Submitted lock B spend txn %s to %s chain for bid %s",
|
f"Submitted lock B spend txn {self.log.id(txid)} to {ci_follower.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||||
txid.hex(),
|
|
||||||
ci_follower.coin_name(),
|
|
||||||
bid_id.hex(),
|
|
||||||
)
|
)
|
||||||
self.logBidEvent(
|
self.logBidEvent(
|
||||||
bid.bid_id,
|
bid.bid_id,
|
||||||
@@ -145,10 +147,11 @@ def getChainBSplitKey(swap_client, bid, xmr_swap, offer):
|
|||||||
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
|
was_sent: bool = bid.was_received if reverse_bid else bid.was_sent
|
||||||
|
|
||||||
key_type = KeyTypes.KBSF if was_sent else KeyTypes.KBSL
|
key_type = KeyTypes.KBSF if was_sent else KeyTypes.KBSL
|
||||||
|
|
||||||
return ci_follower.encodeKey(
|
return ci_follower.encodeKey(
|
||||||
swap_client.getPathKey(
|
swap_client.getPathKey(
|
||||||
ci_leader.coin_type(),
|
ci_leader.interface_type(),
|
||||||
ci_follower.coin_type(),
|
ci_follower.interface_type(),
|
||||||
bid.created_at,
|
bid.created_at,
|
||||||
xmr_swap.contract_count,
|
xmr_swap.contract_count,
|
||||||
key_type,
|
key_type,
|
||||||
|
|||||||
@@ -1,153 +1,157 @@
|
|||||||
/* General Styles */
|
/* General Styles */
|
||||||
.bold {
|
.bold {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.monospace {
|
.monospace {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floatright {
|
.floatright {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 1.25rem;
|
top: 1.25rem;
|
||||||
right: 1.25rem;
|
right: 1.25rem;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Table Styles */
|
/* Table Styles */
|
||||||
.padded_row td {
|
.padded_row td {
|
||||||
padding-top: 1.5em;
|
padding-top: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Modal Styles */
|
/* Modal Styles */
|
||||||
.modal-highest {
|
.modal-highest {
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animation */
|
/* Animation */
|
||||||
#hide {
|
#hide {
|
||||||
-moz-animation: cssAnimation 0s ease-in 15s forwards;
|
-moz-animation: cssAnimation 0s ease-in 15s forwards;
|
||||||
-webkit-animation: cssAnimation 0s ease-in 15s forwards;
|
-webkit-animation: cssAnimation 0s ease-in 15s forwards;
|
||||||
-o-animation: cssAnimation 0s ease-in 15s forwards;
|
-o-animation: cssAnimation 0s ease-in 15s forwards;
|
||||||
animation: cssAnimation 0s ease-in 15s forwards;
|
animation: cssAnimation 0s ease-in 15s forwards;
|
||||||
-webkit-animation-fill-mode: forwards;
|
-webkit-animation-fill-mode: forwards;
|
||||||
animation-fill-mode: forwards;
|
animation-fill-mode: forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes cssAnimation {
|
@keyframes cssAnimation {
|
||||||
to {
|
to {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@-webkit-keyframes cssAnimation {
|
@-webkit-keyframes cssAnimation {
|
||||||
to {
|
to {
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Custom Select Styles */
|
/* Custom Select Styles */
|
||||||
.custom-select .select {
|
.custom-select .select {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
background-image: url('/static/images/other/coin.png');
|
background-image: url('/static/images/other/coin.png');
|
||||||
background-position: 10px center;
|
background-position: 10px center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select select::-webkit-scrollbar {
|
.custom-select select::-webkit-scrollbar {
|
||||||
width: 0;
|
width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select .select option {
|
.custom-select .select option {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
text-indent: 0;
|
text-indent: 0;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: 0 50%;
|
background-position: 0 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select .select option.no-space {
|
.custom-select .select option.no-space {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select .select option[data-image] {
|
.custom-select .select option[data-image] {
|
||||||
background-image: url('');
|
background-image: url('');
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select .select-icon {
|
.custom-select .select-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select .select-image {
|
.custom-select .select-image {
|
||||||
display: none;
|
display: none;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-select .select:focus + .select-dropdown .select-image {
|
.custom-select .select:focus + .select-dropdown .select-image {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Blur and Overlay Styles */
|
/* Blur and Overlay Styles */
|
||||||
.blurred {
|
.blurred {
|
||||||
filter: blur(3px);
|
filter: blur(3px);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-overlay.non-blurred {
|
.error-overlay.non-blurred {
|
||||||
filter: none;
|
filter: none;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
user-select: auto;
|
user-select: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Form Element Styles */
|
/* Form Element Styles */
|
||||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
||||||
select:disabled,
|
select:disabled,
|
||||||
input:disabled,
|
input:disabled,
|
||||||
textarea:disabled {
|
textarea:disabled {
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
border: 1px solid red !important;
|
border: 1px solid red !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Active Container Styles */
|
/* Active Container Styles */
|
||||||
.active-container {
|
.active-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.active-container::before {
|
.active-container::before {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
border: 1px solid rgb(77, 132, 240);
|
border: 1px solid rgb(77, 132, 240);
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Center Spin Animation */
|
/* Center Spin Animation */
|
||||||
.center-spin {
|
.center-spin {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% { transform: rotate(0deg); }
|
0% {
|
||||||
100% { transform: rotate(360deg); }
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hover Container Styles */
|
/* Hover Container Styles */
|
||||||
@@ -155,205 +159,209 @@
|
|||||||
.hover-container:hover #coin_to,
|
.hover-container:hover #coin_to,
|
||||||
.hover-container:hover #coin_from_button,
|
.hover-container:hover #coin_from_button,
|
||||||
.hover-container:hover #coin_from {
|
.hover-container:hover #coin_from {
|
||||||
border-color: #3b82f6;
|
border-color: #3b82f6;
|
||||||
}
|
}
|
||||||
|
|
||||||
#coin_to_button, #coin_from_button {
|
#coin_to_button,
|
||||||
background-repeat: no-repeat;
|
#coin_from_button {
|
||||||
background-position: center;
|
background-repeat: no-repeat;
|
||||||
background-size: 20px 20px;
|
background-position: center;
|
||||||
|
background-size: 20px 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Input-like Container Styles */
|
/* Input-like Container Styles */
|
||||||
.input-like-container {
|
.input-like-container {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
width: 360px;
|
width: 360px;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
color: #374151;
|
color: #374151;
|
||||||
border-radius: 0.375rem;
|
border-radius: 0.375rem;
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
line-height: 1.25rem;
|
line-height: 1.25rem;
|
||||||
outline: none;
|
outline: none;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: 90px;
|
min-height: 90px;
|
||||||
max-height: 150px;
|
max-height: 150px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-like-container.dark {
|
.input-like-container.dark {
|
||||||
background-color: #374151;
|
background-color: #374151;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-like-container.copying {
|
.input-like-container.copying {
|
||||||
width: inherit;
|
width: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* QR Code Styles */
|
/* QR Code Styles */
|
||||||
.qrcode {
|
.qrcode {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qrcode-border {
|
.qrcode-border {
|
||||||
border: 2px solid;
|
border: 2px solid;
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qrcode img {
|
.qrcode img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
border-radius: 0px;
|
border-radius: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#showQR {
|
#showQR {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
height: 25px;
|
height: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qrcode-container {
|
.qrcode-container {
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disabled Element Styles */
|
/* Disabled Element Styles */
|
||||||
select.select-disabled,
|
select.select-disabled,
|
||||||
.disabled-input-enabled,
|
.disabled-input-enabled,
|
||||||
select.disabled-select-enabled {
|
select.disabled-select-enabled {
|
||||||
opacity: 0.40 !important;
|
opacity: 0.40 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shutdown Modal Styles */
|
/* Shutdown Modal Styles */
|
||||||
#shutdownModal {
|
#shutdownModal {
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
#shutdownModal > div:first-child {
|
#shutdownModal > div:first-child {
|
||||||
z-index: 40;
|
z-index: 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
#shutdownModal > div:last-child {
|
#shutdownModal > div:last-child {
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
#shutdownModal > div {
|
#shutdownModal > div {
|
||||||
transition: opacity 0.3s ease-out;
|
transition: opacity 0.3s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
#shutdownModal.hidden > div {
|
#shutdownModal.hidden > div {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#shutdownModal:not(.hidden) > div {
|
#shutdownModal:not(.hidden) > div {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shutdown-button {
|
.shutdown-button {
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shutdown-button.shutdown-disabled {
|
.shutdown-button.shutdown-disabled {
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
color: #a0aec0;
|
color: #a0aec0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shutdown-button.shutdown-disabled:hover {
|
.shutdown-button.shutdown-disabled:hover {
|
||||||
background-color: #4a5568;
|
background-color: #4a5568;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shutdown-button.shutdown-disabled svg {
|
.shutdown-button.shutdown-disabled svg {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Loading Line Animation */
|
||||||
/* Loading line animation */
|
|
||||||
.loading-line {
|
.loading-line {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background-color: #ccc;
|
background-color: #ccc;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.loading-line::before {
|
.loading-line::before {
|
||||||
content: '';
|
content: '';
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: linear-gradient(to right, transparent, #007bff, transparent);
|
background: linear-gradient(to right, transparent, #007bff, transparent);
|
||||||
animation: loading 1.5s infinite;
|
animation: loading 1.5s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes loading {
|
@keyframes loading {
|
||||||
0% {
|
0% {
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: translateX(100%);
|
transform: translateX(100%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Hide the loading line once data is loaded */
|
|
||||||
.usd-value:not(.loading) .loading-line,
|
.usd-value:not(.loading) .loading-line,
|
||||||
.profit-loss:not(.loading) .loading-line {
|
.profit-loss:not(.loading) .loading-line {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resolution-button {
|
/* Resolution Button Styles */
|
||||||
|
.resolution-button {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
color: #4B5563; /* gray-600 */
|
color: #4B5563;
|
||||||
font-size: 0.875rem; /* text-sm */
|
font-size: 0.875rem;
|
||||||
font-weight: 500; /* font-medium */
|
font-weight: 500;
|
||||||
padding: 0.25rem 0.5rem;
|
padding: 0.25rem 0.5rem;
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resolution-button:hover {
|
.resolution-button:hover {
|
||||||
color: #1F2937; /* gray-800 */
|
color: #1F2937;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resolution-button:focus {
|
.resolution-button:focus {
|
||||||
outline: 2px solid #3B82F6; /* blue-500 */
|
outline: 2px solid #3B82F6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.resolution-button.active {
|
.resolution-button.active {
|
||||||
color: #3B82F6; /* blue-500 */
|
color: #3B82F6;
|
||||||
outline: 2px solid #3B82F6; /* blue-500 */
|
outline: 2px solid #3B82F6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .resolution-button {
|
.dark .resolution-button {
|
||||||
color: #9CA3AF; /* gray-400 */
|
color: #9CA3AF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .resolution-button:hover {
|
.dark .resolution-button:hover {
|
||||||
color: #F3F4F6; /* gray-100 */
|
color: #F3F4F6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark .resolution-button.active {
|
.dark .resolution-button.active {
|
||||||
color: #60A5FA; /* blue-400 */
|
color: #60A5FA;
|
||||||
outline-color: #60A5FA; /* blue-400 */
|
outline-color: #60A5FA;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
#toggle-volume.active {
|
|
||||||
@apply bg-green-500 hover:bg-green-600 focus:ring-green-300;
|
|
||||||
}
|
|
||||||
#toggle-auto-refresh[data-enabled="true"] {
|
|
||||||
@apply bg-green-500 hover:bg-green-600 focus:ring-green-300;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Toggle Button Styles */
|
||||||
|
#toggle-volume.active {
|
||||||
|
@apply bg-green-500 hover:bg-green-600 focus:ring-green-300;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toggle-auto-refresh[data-enabled="true"] {
|
||||||
|
@apply bg-green-500 hover:bg-green-600 focus:ring-green-300;
|
||||||
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 9.0 KiB |
872
basicswap/static/js/active.js
Normal file
@@ -0,0 +1,872 @@
|
|||||||
|
// Constants and State
|
||||||
|
const PAGE_SIZE = 50;
|
||||||
|
const COIN_NAME_TO_SYMBOL = {
|
||||||
|
'Bitcoin': 'BTC',
|
||||||
|
'Litecoin': 'LTC',
|
||||||
|
'Monero': 'XMR',
|
||||||
|
'Particl': 'PART',
|
||||||
|
'Particl Blind': 'PART',
|
||||||
|
'Particl Anon': 'PART',
|
||||||
|
'PIVX': 'PIVX',
|
||||||
|
'Firo': 'FIRO',
|
||||||
|
'Dash': 'DASH',
|
||||||
|
'Decred': 'DCR',
|
||||||
|
'Wownero': 'WOW',
|
||||||
|
'Bitcoin Cash': 'BCH',
|
||||||
|
'Dogecoin': 'DOGE'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global state
|
||||||
|
const state = {
|
||||||
|
identities: new Map(),
|
||||||
|
currentPage: 1,
|
||||||
|
wsConnected: false,
|
||||||
|
swapsData: [],
|
||||||
|
isLoading: false,
|
||||||
|
isRefreshing: false,
|
||||||
|
refreshPromise: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// DOM
|
||||||
|
const elements = {
|
||||||
|
swapsBody: document.getElementById('active-swaps-body'),
|
||||||
|
prevPageButton: document.getElementById('prevPage'),
|
||||||
|
nextPageButton: document.getElementById('nextPage'),
|
||||||
|
currentPageSpan: document.getElementById('currentPage'),
|
||||||
|
paginationControls: document.getElementById('pagination-controls'),
|
||||||
|
activeSwapsCount: document.getElementById('activeSwapsCount'),
|
||||||
|
refreshSwapsButton: document.getElementById('refreshSwaps'),
|
||||||
|
statusDot: document.getElementById('status-dot'),
|
||||||
|
statusText: document.getElementById('status-text')
|
||||||
|
};
|
||||||
|
|
||||||
|
// Identity Manager
|
||||||
|
const IdentityManager = {
|
||||||
|
cache: new Map(),
|
||||||
|
pendingRequests: new Map(),
|
||||||
|
retryDelay: 2000,
|
||||||
|
maxRetries: 3,
|
||||||
|
cacheTimeout: 5 * 60 * 1000, // 5 minutes
|
||||||
|
|
||||||
|
async getIdentityData(address) {
|
||||||
|
if (!address) {
|
||||||
|
return { address: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedData = this.getCachedIdentity(address);
|
||||||
|
if (cachedData) {
|
||||||
|
return { ...cachedData, address };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pendingRequests.has(address)) {
|
||||||
|
const pendingData = await this.pendingRequests.get(address);
|
||||||
|
return { ...pendingData, address };
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = this.fetchWithRetry(address);
|
||||||
|
this.pendingRequests.set(address, request);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await request;
|
||||||
|
this.cache.set(address, {
|
||||||
|
data,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
return { ...data, address };
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Error fetching identity for ${address}:`, error);
|
||||||
|
return { address };
|
||||||
|
} finally {
|
||||||
|
this.pendingRequests.delete(address);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getCachedIdentity(address) {
|
||||||
|
const cached = this.cache.get(address);
|
||||||
|
if (cached && (Date.now() - cached.timestamp) < this.cacheTimeout) {
|
||||||
|
return cached.data;
|
||||||
|
}
|
||||||
|
if (cached) {
|
||||||
|
this.cache.delete(address);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchWithRetry(address, attempt = 1) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/json/identities/${address}`, {
|
||||||
|
signal: AbortSignal.timeout(5000)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
address,
|
||||||
|
num_sent_bids_successful: safeParseInt(data.num_sent_bids_successful),
|
||||||
|
num_recv_bids_successful: safeParseInt(data.num_recv_bids_successful),
|
||||||
|
num_sent_bids_failed: safeParseInt(data.num_sent_bids_failed),
|
||||||
|
num_recv_bids_failed: safeParseInt(data.num_recv_bids_failed),
|
||||||
|
num_sent_bids_rejected: safeParseInt(data.num_sent_bids_rejected),
|
||||||
|
num_recv_bids_rejected: safeParseInt(data.num_recv_bids_rejected),
|
||||||
|
label: data.label || '',
|
||||||
|
note: data.note || '',
|
||||||
|
automation_override: safeParseInt(data.automation_override)
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (attempt >= this.maxRetries) {
|
||||||
|
console.warn(`Failed to fetch identity for ${address} after ${attempt} attempts`);
|
||||||
|
return {
|
||||||
|
address,
|
||||||
|
num_sent_bids_successful: 0,
|
||||||
|
num_recv_bids_successful: 0,
|
||||||
|
num_sent_bids_failed: 0,
|
||||||
|
num_recv_bids_failed: 0,
|
||||||
|
num_sent_bids_rejected: 0,
|
||||||
|
num_recv_bids_rejected: 0,
|
||||||
|
label: '',
|
||||||
|
note: '',
|
||||||
|
automation_override: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, this.retryDelay * attempt));
|
||||||
|
return this.fetchWithRetry(address, attempt + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const safeParseInt = (value) => {
|
||||||
|
const parsed = parseInt(value);
|
||||||
|
return isNaN(parsed) ? 0 : parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusClass = (status, tx_a, tx_b) => {
|
||||||
|
switch (status) {
|
||||||
|
case 'Completed':
|
||||||
|
return 'bg-green-300 text-black dark:bg-green-600 dark:text-white';
|
||||||
|
case 'Expired':
|
||||||
|
case 'Timed-out':
|
||||||
|
return 'bg-gray-200 text-black dark:bg-gray-400 dark:text-white';
|
||||||
|
case 'Error':
|
||||||
|
case 'Failed':
|
||||||
|
return 'bg-red-300 text-black dark:bg-red-600 dark:text-white';
|
||||||
|
case 'Failed, swiped':
|
||||||
|
case 'Failed, refunded':
|
||||||
|
return 'bg-gray-200 text-black dark:bg-gray-400 dark:text-red-500';
|
||||||
|
case 'InProgress':
|
||||||
|
case 'Script coin locked':
|
||||||
|
case 'Scriptless coin locked':
|
||||||
|
case 'Script coin lock released':
|
||||||
|
case 'SendingInitialTx':
|
||||||
|
case 'SendingPaymentTx':
|
||||||
|
return 'bg-blue-300 text-black dark:bg-blue-500 dark:text-white';
|
||||||
|
case 'Received':
|
||||||
|
case 'Exchanged script lock tx sigs msg':
|
||||||
|
case 'Exchanged script lock spend tx msg':
|
||||||
|
case 'Script tx redeemed':
|
||||||
|
case 'Scriptless tx redeemed':
|
||||||
|
case 'Scriptless tx recovered':
|
||||||
|
return 'bg-blue-300 text-black dark:bg-blue-500 dark:text-white';
|
||||||
|
case 'Accepted':
|
||||||
|
case 'Request accepted':
|
||||||
|
return 'bg-green-300 text-black dark:bg-green-600 dark:text-white';
|
||||||
|
case 'Delaying':
|
||||||
|
case 'Auto accept delay':
|
||||||
|
return 'bg-blue-300 text-black dark:bg-blue-500 dark:text-white';
|
||||||
|
case 'Abandoned':
|
||||||
|
case 'Rejected':
|
||||||
|
return 'bg-red-300 text-black dark:bg-red-600 dark:text-white';
|
||||||
|
default:
|
||||||
|
return 'bg-blue-300 text-black dark:bg-blue-500 dark:text-white';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTxStatusClass = (status) => {
|
||||||
|
if (!status || status === 'None') return 'text-gray-400';
|
||||||
|
|
||||||
|
if (status.includes('Complete') || status.includes('Confirmed')) {
|
||||||
|
return 'text-green-500';
|
||||||
|
}
|
||||||
|
if (status.includes('Error') || status.includes('Failed')) {
|
||||||
|
return 'text-red-500';
|
||||||
|
}
|
||||||
|
if (status.includes('Progress') || status.includes('Sending')) {
|
||||||
|
return 'text-yellow-500';
|
||||||
|
}
|
||||||
|
return 'text-blue-500';
|
||||||
|
};
|
||||||
|
|
||||||
|
// Util
|
||||||
|
const formatTimeAgo = (timestamp) => {
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const diff = now - timestamp;
|
||||||
|
|
||||||
|
if (diff < 60) return `${diff} seconds ago`;
|
||||||
|
if (diff < 3600) return `${Math.floor(diff / 60)} minutes ago`;
|
||||||
|
if (diff < 86400) return `${Math.floor(diff / 3600)} hours ago`;
|
||||||
|
return `${Math.floor(diff / 86400)} days ago`;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const formatTime = (timestamp) => {
|
||||||
|
if (!timestamp) return '';
|
||||||
|
const date = new Date(timestamp * 1000);
|
||||||
|
return date.toLocaleString('en-US', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'short',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatAddress = (address, displayLength = 15) => {
|
||||||
|
if (!address) return '';
|
||||||
|
if (address.length <= displayLength) return address;
|
||||||
|
return `${address.slice(0, displayLength)}...`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStatusColor = (status) => {
|
||||||
|
const statusColors = {
|
||||||
|
'Received': 'text-blue-500',
|
||||||
|
'Accepted': 'text-green-500',
|
||||||
|
'InProgress': 'text-yellow-500',
|
||||||
|
'Complete': 'text-green-600',
|
||||||
|
'Failed': 'text-red-500',
|
||||||
|
'Expired': 'text-gray-500'
|
||||||
|
};
|
||||||
|
return statusColors[status] || 'text-gray-500';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTimeStrokeColor = (expireTime) => {
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const timeLeft = expireTime - now;
|
||||||
|
|
||||||
|
if (timeLeft <= 300) return '#9CA3AF'; // 5 minutes or less
|
||||||
|
if (timeLeft <= 1800) return '#3B82F6'; // 30 minutes or less
|
||||||
|
return '#10B981'; // More than 30 minutes
|
||||||
|
};
|
||||||
|
|
||||||
|
// WebSocket Manager
|
||||||
|
const WebSocketManager = {
|
||||||
|
ws: null,
|
||||||
|
processingQueue: false,
|
||||||
|
reconnectTimeout: null,
|
||||||
|
maxReconnectAttempts: 5,
|
||||||
|
reconnectAttempts: 0,
|
||||||
|
reconnectDelay: 5000,
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.connect();
|
||||||
|
this.startHealthCheck();
|
||||||
|
},
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
if (this.ws?.readyState === WebSocket.OPEN) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const wsPort = window.ws_port || '11700';
|
||||||
|
this.ws = new WebSocket(`ws://${window.location.hostname}:${wsPort}`);
|
||||||
|
this.setupEventHandlers();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('WebSocket connection error:', error);
|
||||||
|
this.handleReconnect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setupEventHandlers() {
|
||||||
|
this.ws.onopen = () => {
|
||||||
|
state.wsConnected = true;
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
updateConnectionStatus('connected');
|
||||||
|
console.log('🟢 WebSocket connection established for Swaps in Progress');
|
||||||
|
updateSwapsTable({ resetPage: true, refreshData: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onmessage = () => {
|
||||||
|
if (!this.processingQueue) {
|
||||||
|
this.processingQueue = true;
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
if (!state.isRefreshing) {
|
||||||
|
await updateSwapsTable({ resetPage: false, refreshData: true });
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.processingQueue = false;
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onclose = () => {
|
||||||
|
state.wsConnected = false;
|
||||||
|
updateConnectionStatus('disconnected');
|
||||||
|
this.handleReconnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onerror = () => {
|
||||||
|
updateConnectionStatus('error');
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
startHealthCheck() {
|
||||||
|
setInterval(() => {
|
||||||
|
if (this.ws?.readyState !== WebSocket.OPEN) {
|
||||||
|
this.handleReconnect();
|
||||||
|
}
|
||||||
|
}, 30000);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleReconnect() {
|
||||||
|
if (this.reconnectTimeout) {
|
||||||
|
clearTimeout(this.reconnectTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reconnectAttempts++;
|
||||||
|
if (this.reconnectAttempts <= this.maxReconnectAttempts) {
|
||||||
|
const delay = this.reconnectDelay * Math.pow(1.5, this.reconnectAttempts - 1);
|
||||||
|
this.reconnectTimeout = setTimeout(() => this.connect(), delay);
|
||||||
|
} else {
|
||||||
|
updateConnectionStatus('error');
|
||||||
|
setTimeout(() => {
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
this.connect();
|
||||||
|
}, 60000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// UI
|
||||||
|
const updateConnectionStatus = (status) => {
|
||||||
|
const { statusDot, statusText } = elements;
|
||||||
|
if (!statusDot || !statusText) return;
|
||||||
|
|
||||||
|
const statusConfig = {
|
||||||
|
connected: {
|
||||||
|
dotClass: 'w-2.5 h-2.5 rounded-full bg-green-500 mr-2',
|
||||||
|
textClass: 'text-sm text-green-500',
|
||||||
|
message: 'Connected'
|
||||||
|
},
|
||||||
|
disconnected: {
|
||||||
|
dotClass: 'w-2.5 h-2.5 rounded-full bg-red-500 mr-2',
|
||||||
|
textClass: 'text-sm text-red-500',
|
||||||
|
message: 'Disconnected - Reconnecting...'
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
dotClass: 'w-2.5 h-2.5 rounded-full bg-yellow-500 mr-2',
|
||||||
|
textClass: 'text-sm text-yellow-500',
|
||||||
|
message: 'Connection Error'
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
dotClass: 'w-2.5 h-2.5 rounded-full bg-gray-500 mr-2',
|
||||||
|
textClass: 'text-sm text-gray-500',
|
||||||
|
message: 'Connecting...'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = statusConfig[status] || statusConfig.default;
|
||||||
|
statusDot.className = config.dotClass;
|
||||||
|
statusText.className = config.textClass;
|
||||||
|
statusText.textContent = config.message;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLoadingState = (isLoading) => {
|
||||||
|
state.isLoading = isLoading;
|
||||||
|
if (elements.refreshSwapsButton) {
|
||||||
|
elements.refreshSwapsButton.disabled = isLoading;
|
||||||
|
elements.refreshSwapsButton.classList.toggle('opacity-75', isLoading);
|
||||||
|
elements.refreshSwapsButton.classList.toggle('cursor-wait', isLoading);
|
||||||
|
|
||||||
|
const refreshIcon = elements.refreshSwapsButton.querySelector('svg');
|
||||||
|
const refreshText = elements.refreshSwapsButton.querySelector('#refreshText');
|
||||||
|
|
||||||
|
if (refreshIcon) {
|
||||||
|
refreshIcon.style.transition = 'transform 0.3s ease';
|
||||||
|
refreshIcon.classList.toggle('animate-spin', isLoading);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshText) {
|
||||||
|
refreshText.textContent = isLoading ? 'Refreshing...' : 'Refresh';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const processIdentityStats = (identity) => {
|
||||||
|
if (!identity) return null;
|
||||||
|
|
||||||
|
const stats = {
|
||||||
|
sentSuccessful: safeParseInt(identity.num_sent_bids_successful),
|
||||||
|
recvSuccessful: safeParseInt(identity.num_recv_bids_successful),
|
||||||
|
sentFailed: safeParseInt(identity.num_sent_bids_failed),
|
||||||
|
recvFailed: safeParseInt(identity.num_recv_bids_failed),
|
||||||
|
sentRejected: safeParseInt(identity.num_sent_bids_rejected),
|
||||||
|
recvRejected: safeParseInt(identity.num_recv_bids_rejected)
|
||||||
|
};
|
||||||
|
|
||||||
|
stats.totalSuccessful = stats.sentSuccessful + stats.recvSuccessful;
|
||||||
|
stats.totalFailed = stats.sentFailed + stats.recvFailed;
|
||||||
|
stats.totalRejected = stats.sentRejected + stats.recvRejected;
|
||||||
|
stats.totalBids = stats.totalSuccessful + stats.totalFailed + stats.totalRejected;
|
||||||
|
|
||||||
|
stats.successRate = stats.totalBids > 0
|
||||||
|
? ((stats.totalSuccessful / stats.totalBids) * 100).toFixed(1)
|
||||||
|
: '0.0';
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createIdentityTooltip = (identity) => {
|
||||||
|
if (!identity) return '';
|
||||||
|
|
||||||
|
const stats = processIdentityStats(identity);
|
||||||
|
if (!stats) return '';
|
||||||
|
|
||||||
|
const getSuccessRateColor = (rate) => {
|
||||||
|
const numRate = parseFloat(rate);
|
||||||
|
if (numRate >= 80) return 'text-green-600';
|
||||||
|
if (numRate >= 60) return 'text-yellow-600';
|
||||||
|
return 'text-red-600';
|
||||||
|
};
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="identity-info space-y-2">
|
||||||
|
${identity.label ? `
|
||||||
|
<div class="border-b border-gray-400 pb-2">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Label:</div>
|
||||||
|
<div class="text-white">${identity.label}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Address:</div>
|
||||||
|
<div class="monospace text-xs break-all bg-gray-500 p-2 rounded-md text-white">
|
||||||
|
${identity.address || ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${identity.note ? `
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Note:</div>
|
||||||
|
<div class="text-white text-sm italic">${identity.note}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<div class="pt-2 mt-2">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold mb-2">Swap History:</div>
|
||||||
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
<div class="text-center p-2 bg-gray-500 rounded-md">
|
||||||
|
<div class="text-lg font-bold ${getSuccessRateColor(stats.successRate)}">
|
||||||
|
${stats.successRate}%
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-white">Success Rate</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center p-2 bg-gray-500 rounded-md">
|
||||||
|
<div class="text-lg font-bold text-blue-500">${stats.totalBids}</div>
|
||||||
|
<div class="text-xs text-white">Total Trades</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-3 gap-2 mt-2 text-center text-xs">
|
||||||
|
<div>
|
||||||
|
<div class="text-green-600 font-semibold">
|
||||||
|
${stats.totalSuccessful}
|
||||||
|
</div>
|
||||||
|
<div class="text-white">Successful</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-yellow-600 font-semibold">
|
||||||
|
${stats.totalRejected}
|
||||||
|
</div>
|
||||||
|
<div class="text-white">Rejected</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-red-600 font-semibold">
|
||||||
|
${stats.totalFailed}
|
||||||
|
</div>
|
||||||
|
<div class="text-white">Failed</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSwapTableRow = async (swap) => {
|
||||||
|
if (!swap || !swap.bid_id) {
|
||||||
|
console.warn('Invalid swap data:', swap);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const identity = await IdentityManager.getIdentityData(swap.addr_from);
|
||||||
|
const uniqueId = `${swap.bid_id}_${swap.created_at}`;
|
||||||
|
const fromSymbol = COIN_NAME_TO_SYMBOL[swap.coin_from] || swap.coin_from;
|
||||||
|
const toSymbol = COIN_NAME_TO_SYMBOL[swap.coin_to] || swap.coin_to;
|
||||||
|
const timeColor = getTimeStrokeColor(swap.expire_at);
|
||||||
|
const fromAmount = parseFloat(swap.amount_from) || 0;
|
||||||
|
const toAmount = parseFloat(swap.amount_to) || 0;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr class="relative opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600" data-bid-id="${swap.bid_id}">
|
||||||
|
<td class="relative w-0 p-0 m-0">
|
||||||
|
<div class="absolute top-0 bottom-0 left-0 w-1"></div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Time Column -->
|
||||||
|
<td class="py-3 pl-1 pr-2 text-xs whitespace-nowrap">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="relative" data-tooltip-target="tooltip-time-${uniqueId}">
|
||||||
|
<svg class="w-5 h-5 rounded-full mr-4 cursor-pointer" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="${timeColor}" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col hidden xl:block">
|
||||||
|
<div class="text-xs whitespace-nowrap">
|
||||||
|
<span class="bold">Posted:</span> ${formatTimeAgo(swap.created_at)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Details Column -->
|
||||||
|
<td class="py-8 px-4 text-xs text-left hidden xl:block">
|
||||||
|
<div class="flex flex-col gap-2 relative">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a href="/identity/${swap.addr_from}" data-tooltip-target="tooltip-identity-${uniqueId}" class="flex items-center">
|
||||||
|
<svg class="w-4 h-4 mr-2 text-gray-400 dark:text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="monospace ${identity?.label ? 'dark:text-white' : 'dark:text-white'}">
|
||||||
|
${identity?.label || formatAddress(swap.addr_from)}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||||
|
<span class="font-semibold">Bid ID:</span>
|
||||||
|
<a href="/bid/${swap.bid_id}" data-tooltip-target="tooltip-bid-${uniqueId}" class="hover:underline">
|
||||||
|
${formatAddress(swap.bid_id)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||||
|
<span class="font-semibold">Offer ID:</span>
|
||||||
|
<a href="/offer/${swap.offer_id}" data-tooltip-target="tooltip-offer-${uniqueId}" class="hover:underline">
|
||||||
|
${formatAddress(swap.offer_id)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<!-- You Send Column -->
|
||||||
|
<td class="py-0">
|
||||||
|
<div class="py-3 px-4 text-left">
|
||||||
|
<div class="items-center monospace">
|
||||||
|
<div class="pr-2">
|
||||||
|
<div class="text-sm font-semibold">${fromAmount.toFixed(8)}</div>
|
||||||
|
<div class="text-sm text-gray-500 dark:text-gray-400">${fromSymbol}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Swap Column -->
|
||||||
|
<td class="py-0">
|
||||||
|
<div class="py-3 px-4 text-center">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<span class="inline-flex mr-3 align-middle items-center justify-center w-18 h-20 rounded">
|
||||||
|
<img class="h-12"
|
||||||
|
src="/static/images/coins/${swap.coin_from.replace(' ', '-')}.png"
|
||||||
|
alt="${swap.coin_from}"
|
||||||
|
onerror="this.src='/static/images/coins/default.png'">
|
||||||
|
</span>
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="inline-flex ml-3 align-middle items-center justify-center w-18 h-20 rounded">
|
||||||
|
<img class="h-12"
|
||||||
|
src="/static/images/coins/${swap.coin_to.replace(' ', '-')}.png"
|
||||||
|
alt="${swap.coin_to}"
|
||||||
|
onerror="this.src='/static/images/coins/default.png'">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- You Receive Column -->
|
||||||
|
<td class="py-0">
|
||||||
|
<div class="py-3 px-4 text-right">
|
||||||
|
<div class="items-center monospace">
|
||||||
|
<div class="text-sm font-semibold">${toAmount.toFixed(8)}</div>
|
||||||
|
<div class="text-sm text-gray-500 dark:text-gray-400">${toSymbol}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Status Column -->
|
||||||
|
<td class="py-3 px-4 text-center">
|
||||||
|
<div data-tooltip-target="tooltip-status-${uniqueId}" class="flex justify-center">
|
||||||
|
<span class="px-2.5 py-1 text-xs font-medium rounded-full ${getStatusClass(swap.bid_state, swap.tx_state_a, swap.tx_state_b)}">
|
||||||
|
${swap.bid_state}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<td class="py-3 px-4 text-center">
|
||||||
|
<a href="/bid/${swap.bid_id}"
|
||||||
|
class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200">
|
||||||
|
Details
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Tooltips -->
|
||||||
|
<div id="tooltip-time-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||||
|
<div class="active-revoked-expired">
|
||||||
|
<span class="bold">
|
||||||
|
<div class="text-xs"><span class="bold">Posted:</span> ${formatTimeAgo(swap.created_at)}</div>
|
||||||
|
<div class="text-xs"><span class="bold">Expires in:</span> ${formatTime(swap.expire_at)}</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 text-xs">
|
||||||
|
<p class="font-bold mb-3">Time Indicator Colors:</p>
|
||||||
|
<p class="flex items-center">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#10B981" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#10B981"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Green: More than 30 minutes left
|
||||||
|
</p>
|
||||||
|
<p class="flex items-center mt-3">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#3B82F6" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#3B82F6"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Blue: Between 5 and 30 minutes left
|
||||||
|
</p>
|
||||||
|
<p class="flex items-center mt-3 mb-3">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#9CA3AF" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#9CA3AF"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Grey: Less than 5 minutes left or expired
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tooltip-identity-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||||
|
${createIdentityTooltip(identity)}
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tooltip-offer-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Offer ID:</div>
|
||||||
|
<div class="monospace text-xs break-all">
|
||||||
|
${swap.offer_id}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tooltip-bid-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Bid ID:</div>
|
||||||
|
<div class="monospace text-xs break-all">
|
||||||
|
${swap.bid_id}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tooltip-status-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||||
|
<div class="text-white">
|
||||||
|
<p class="font-bold mb-2">Transaction Status</p>
|
||||||
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
<div class="bg-gray-500 p-2 rounded">
|
||||||
|
<p class="text-xs font-bold">ITX:</p>
|
||||||
|
<p>${swap.tx_state_a || 'N/A'}</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-500 p-2 rounded">
|
||||||
|
<p class="text-xs font-bold">PTX:</p>
|
||||||
|
<p>${swap.tx_state_b || 'N/A'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function updateSwapsTable(options = {}) {
|
||||||
|
const { resetPage = false, refreshData = true } = options;
|
||||||
|
|
||||||
|
if (state.refreshPromise) {
|
||||||
|
await state.refreshPromise;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
updateLoadingState(true);
|
||||||
|
|
||||||
|
if (refreshData) {
|
||||||
|
state.refreshPromise = (async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/json/active', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
sort_by: "created_at",
|
||||||
|
sort_dir: "desc"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
state.swapsData = Array.isArray(data) ? data : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching swap data:', error);
|
||||||
|
state.swapsData = [];
|
||||||
|
} finally {
|
||||||
|
state.refreshPromise = null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
await state.refreshPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.activeSwapsCount) {
|
||||||
|
elements.activeSwapsCount.textContent = state.swapsData.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(state.swapsData.length / PAGE_SIZE);
|
||||||
|
|
||||||
|
if (resetPage && state.swapsData.length > 0) {
|
||||||
|
state.currentPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.currentPage = Math.min(Math.max(1, state.currentPage), Math.max(1, totalPages));
|
||||||
|
|
||||||
|
const startIndex = (state.currentPage - 1) * PAGE_SIZE;
|
||||||
|
const endIndex = startIndex + PAGE_SIZE;
|
||||||
|
const currentPageSwaps = state.swapsData.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
if (elements.swapsBody) {
|
||||||
|
if (currentPageSwaps.length > 0) {
|
||||||
|
const rowPromises = currentPageSwaps.map(swap => createSwapTableRow(swap));
|
||||||
|
const rows = await Promise.all(rowPromises);
|
||||||
|
elements.swapsBody.innerHTML = rows.join('');
|
||||||
|
|
||||||
|
// Initialize tooltips
|
||||||
|
if (window.TooltipManager) {
|
||||||
|
window.TooltipManager.cleanup();
|
||||||
|
const tooltipTriggers = document.querySelectorAll('[data-tooltip-target]');
|
||||||
|
tooltipTriggers.forEach(trigger => {
|
||||||
|
const targetId = trigger.getAttribute('data-tooltip-target');
|
||||||
|
const tooltipContent = document.getElementById(targetId);
|
||||||
|
if (tooltipContent) {
|
||||||
|
window.TooltipManager.create(trigger, tooltipContent.innerHTML, {
|
||||||
|
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
elements.swapsBody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="text-center py-4 text-gray-500 dark:text-white">
|
||||||
|
No active swaps found
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.paginationControls) {
|
||||||
|
elements.paginationControls.style.display = totalPages > 1 ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.currentPageSpan) {
|
||||||
|
elements.currentPageSpan.textContent = state.currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.prevPageButton) {
|
||||||
|
elements.prevPageButton.style.display = state.currentPage > 1 ? 'inline-flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.nextPageButton) {
|
||||||
|
elements.nextPageButton.style.display = state.currentPage < totalPages ? 'inline-flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating swaps table:', error);
|
||||||
|
if (elements.swapsBody) {
|
||||||
|
elements.swapsBody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="text-center py-4 text-red-500">
|
||||||
|
Error loading active swaps. Please try again later.
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
updateLoadingState(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event
|
||||||
|
const setupEventListeners = () => {
|
||||||
|
if (elements.refreshSwapsButton) {
|
||||||
|
elements.refreshSwapsButton.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (state.isRefreshing) return;
|
||||||
|
|
||||||
|
updateLoadingState(true);
|
||||||
|
try {
|
||||||
|
await updateSwapsTable({ resetPage: true, refreshData: true });
|
||||||
|
} finally {
|
||||||
|
updateLoadingState(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.prevPageButton) {
|
||||||
|
elements.prevPageButton.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (state.isLoading) return;
|
||||||
|
if (state.currentPage > 1) {
|
||||||
|
state.currentPage--;
|
||||||
|
await updateSwapsTable({ resetPage: false, refreshData: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.nextPageButton) {
|
||||||
|
elements.nextPageButton.addEventListener('click', async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (state.isLoading) return;
|
||||||
|
const totalPages = Math.ceil(state.swapsData.length / PAGE_SIZE);
|
||||||
|
if (state.currentPage < totalPages) {
|
||||||
|
state.currentPage++;
|
||||||
|
await updateSwapsTable({ resetPage: false, refreshData: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Init
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
WebSocketManager.initialize();
|
||||||
|
setupEventListeners();
|
||||||
|
});
|
||||||
899
basicswap/static/js/bids_available.js
Normal file
@@ -0,0 +1,899 @@
|
|||||||
|
// Constants and State
|
||||||
|
const PAGE_SIZE = 50;
|
||||||
|
const COIN_NAME_TO_SYMBOL = {
|
||||||
|
'Bitcoin': 'BTC',
|
||||||
|
'Litecoin': 'LTC',
|
||||||
|
'Monero': 'XMR',
|
||||||
|
'Particl': 'PART',
|
||||||
|
'Particl Blind': 'PART',
|
||||||
|
'Particl Anon': 'PART',
|
||||||
|
'PIVX': 'PIVX',
|
||||||
|
'Firo': 'FIRO',
|
||||||
|
'Dash': 'DASH',
|
||||||
|
'Decred': 'DCR',
|
||||||
|
'Wownero': 'WOW',
|
||||||
|
'Bitcoin Cash': 'BCH',
|
||||||
|
'Dogecoin': 'DOGE'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global state
|
||||||
|
const state = {
|
||||||
|
dentities: new Map(),
|
||||||
|
currentPage: 1,
|
||||||
|
wsConnected: false,
|
||||||
|
jsonData: [],
|
||||||
|
isLoading: false,
|
||||||
|
isRefreshing: false,
|
||||||
|
refreshPromise: null
|
||||||
|
};
|
||||||
|
|
||||||
|
// DOM
|
||||||
|
const elements = {
|
||||||
|
bidsBody: document.getElementById('bids-body'),
|
||||||
|
prevPageButton: document.getElementById('prevPage'),
|
||||||
|
nextPageButton: document.getElementById('nextPage'),
|
||||||
|
currentPageSpan: document.getElementById('currentPage'),
|
||||||
|
paginationControls: document.getElementById('pagination-controls'),
|
||||||
|
availableBidsCount: document.getElementById('availableBidsCount'),
|
||||||
|
refreshBidsButton: document.getElementById('refreshBids'),
|
||||||
|
statusDot: document.getElementById('status-dot'),
|
||||||
|
statusText: document.getElementById('status-text')
|
||||||
|
};
|
||||||
|
|
||||||
|
// Identity Manager
|
||||||
|
const IdentityManager = {
|
||||||
|
cache: new Map(),
|
||||||
|
pendingRequests: new Map(),
|
||||||
|
retryDelay: 2000,
|
||||||
|
maxRetries: 3,
|
||||||
|
cacheTimeout: 5 * 60 * 1000, // 5 minutes
|
||||||
|
|
||||||
|
async getIdentityData(address) {
|
||||||
|
if (!address) {
|
||||||
|
return { address: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
const cachedData = this.getCachedIdentity(address);
|
||||||
|
if (cachedData) {
|
||||||
|
return { ...cachedData, address };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pendingRequests.has(address)) {
|
||||||
|
const pendingData = await this.pendingRequests.get(address);
|
||||||
|
return { ...pendingData, address };
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = this.fetchWithRetry(address);
|
||||||
|
this.pendingRequests.set(address, request);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await request;
|
||||||
|
this.cache.set(address, {
|
||||||
|
data,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
return { ...data, address };
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Error fetching identity for ${address}:`, error);
|
||||||
|
return { address };
|
||||||
|
} finally {
|
||||||
|
this.pendingRequests.delete(address);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getCachedIdentity(address) {
|
||||||
|
const cached = this.cache.get(address);
|
||||||
|
if (cached && (Date.now() - cached.timestamp) < this.cacheTimeout) {
|
||||||
|
return cached.data;
|
||||||
|
}
|
||||||
|
if (cached) {
|
||||||
|
this.cache.delete(address);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
async fetchWithRetry(address, attempt = 1) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/json/identities/${address}`, {
|
||||||
|
signal: AbortSignal.timeout(5000)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
address,
|
||||||
|
num_sent_bids_successful: safeParseInt(data.num_sent_bids_successful),
|
||||||
|
num_recv_bids_successful: safeParseInt(data.num_recv_bids_successful),
|
||||||
|
num_sent_bids_failed: safeParseInt(data.num_sent_bids_failed),
|
||||||
|
num_recv_bids_failed: safeParseInt(data.num_recv_bids_failed),
|
||||||
|
num_sent_bids_rejected: safeParseInt(data.num_sent_bids_rejected),
|
||||||
|
num_recv_bids_rejected: safeParseInt(data.num_recv_bids_rejected),
|
||||||
|
label: data.label || '',
|
||||||
|
note: data.note || '',
|
||||||
|
automation_override: safeParseInt(data.automation_override)
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
if (attempt >= this.maxRetries) {
|
||||||
|
console.warn(`Failed to fetch identity for ${address} after ${attempt} attempts`);
|
||||||
|
return {
|
||||||
|
address,
|
||||||
|
num_sent_bids_successful: 0,
|
||||||
|
num_recv_bids_successful: 0,
|
||||||
|
num_sent_bids_failed: 0,
|
||||||
|
num_recv_bids_failed: 0,
|
||||||
|
num_sent_bids_rejected: 0,
|
||||||
|
num_recv_bids_rejected: 0,
|
||||||
|
label: '',
|
||||||
|
note: '',
|
||||||
|
automation_override: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, this.retryDelay * attempt));
|
||||||
|
return this.fetchWithRetry(address, attempt + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clearCache() {
|
||||||
|
this.cache.clear();
|
||||||
|
this.pendingRequests.clear();
|
||||||
|
},
|
||||||
|
|
||||||
|
removeFromCache(address) {
|
||||||
|
this.cache.delete(address);
|
||||||
|
this.pendingRequests.delete(address);
|
||||||
|
},
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
const now = Date.now();
|
||||||
|
for (const [address, cached] of this.cache.entries()) {
|
||||||
|
if (now - cached.timestamp >= this.cacheTimeout) {
|
||||||
|
this.cache.delete(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Util
|
||||||
|
const formatTimeAgo = (timestamp) => {
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const diff = now - timestamp;
|
||||||
|
|
||||||
|
if (diff < 60) return `${diff} seconds ago`;
|
||||||
|
if (diff < 3600) return `${Math.floor(diff / 60)} minutes ago`;
|
||||||
|
if (diff < 86400) return `${Math.floor(diff / 3600)} hours ago`;
|
||||||
|
return `${Math.floor(diff / 86400)} days ago`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatTime = (timestamp) => {
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const diff = timestamp - now;
|
||||||
|
|
||||||
|
if (diff <= 0) return "Expired";
|
||||||
|
if (diff < 60) return `${diff} seconds`;
|
||||||
|
if (diff < 3600) return `${Math.floor(diff / 60)} minutes`;
|
||||||
|
if (diff < 86400) return `${Math.floor(diff / 3600)} hours`;
|
||||||
|
return `${Math.floor(diff / 86400)} days`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatAddress = (address, displayLength = 15) => {
|
||||||
|
if (!address) return '';
|
||||||
|
if (address.length <= displayLength) return address;
|
||||||
|
return `${address.slice(0, displayLength)}...`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTimeStrokeColor = (expireTime) => {
|
||||||
|
const now = Math.floor(Date.now() / 1000);
|
||||||
|
const timeLeft = expireTime - now;
|
||||||
|
|
||||||
|
if (timeLeft <= 300) return '#9CA3AF'; // 5 minutes or less
|
||||||
|
if (timeLeft <= 1800) return '#3B82F6'; // 30 minutes or less
|
||||||
|
return '#10B981'; // More than 30 minutes
|
||||||
|
};
|
||||||
|
|
||||||
|
const createTimeTooltip = (bid) => {
|
||||||
|
const postedTime = formatTimeAgo(bid.created_at);
|
||||||
|
const expiresIn = formatTime(bid.expire_at);
|
||||||
|
return `
|
||||||
|
<div class="active-revoked-expired">
|
||||||
|
<span class="bold">
|
||||||
|
<div class="text-xs"><span class="bold">Posted:</span> ${postedTime}</div>
|
||||||
|
<div class="text-xs"><span class="bold">Expires in:</span> ${expiresIn}</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 text-xs">
|
||||||
|
<p class="font-bold mb-3">Time Indicator Colors:</p>
|
||||||
|
<p class="flex items-center">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#10B981" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#10B981"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Green: More than 30 minutes left
|
||||||
|
</p>
|
||||||
|
<p class="flex items-center mt-3">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#3B82F6" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#3B82F6"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Blue: Between 5 and 30 minutes left
|
||||||
|
</p>
|
||||||
|
<p class="flex items-center mt-3 mb-3">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#9CA3AF" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#9CA3AF"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Grey: Less than 5 minutes left or expired
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const safeParseInt = (value) => {
|
||||||
|
const parsed = parseInt(value);
|
||||||
|
return isNaN(parsed) ? 0 : parsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
const processIdentityStats = (identity) => {
|
||||||
|
if (!identity) return null;
|
||||||
|
|
||||||
|
const stats = {
|
||||||
|
sentSuccessful: safeParseInt(identity.num_sent_bids_successful),
|
||||||
|
recvSuccessful: safeParseInt(identity.num_recv_bids_successful),
|
||||||
|
sentFailed: safeParseInt(identity.num_sent_bids_failed),
|
||||||
|
recvFailed: safeParseInt(identity.num_recv_bids_failed),
|
||||||
|
sentRejected: safeParseInt(identity.num_sent_bids_rejected),
|
||||||
|
recvRejected: safeParseInt(identity.num_recv_bids_rejected)
|
||||||
|
};
|
||||||
|
|
||||||
|
stats.totalSuccessful = stats.sentSuccessful + stats.recvSuccessful;
|
||||||
|
stats.totalFailed = stats.sentFailed + stats.recvFailed;
|
||||||
|
stats.totalRejected = stats.sentRejected + stats.recvRejected;
|
||||||
|
stats.totalBids = stats.totalSuccessful + stats.totalFailed + stats.totalRejected;
|
||||||
|
|
||||||
|
stats.successRate = stats.totalBids > 0
|
||||||
|
? ((stats.totalSuccessful / stats.totalBids) * 100).toFixed(1)
|
||||||
|
: '0.0';
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createIdentityTooltip = (identity) => {
|
||||||
|
if (!identity) return '';
|
||||||
|
|
||||||
|
const stats = processIdentityStats(identity);
|
||||||
|
if (!stats) return '';
|
||||||
|
|
||||||
|
const getSuccessRateColor = (rate) => {
|
||||||
|
const numRate = parseFloat(rate);
|
||||||
|
if (numRate >= 80) return 'text-green-600';
|
||||||
|
if (numRate >= 60) return 'text-yellow-600';
|
||||||
|
return 'text-red-600';
|
||||||
|
};
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="identity-info space-y-2">
|
||||||
|
${identity.label ? `
|
||||||
|
<div class="border-b border-gray-400 pb-2">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Label:</div>
|
||||||
|
<div class="text-white">${identity.label}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Bid From Address:</div>
|
||||||
|
<div class="monospace text-xs break-all bg-gray-500 p-2 rounded-md text-white">
|
||||||
|
${identity.address || ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${identity.note ? `
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Note:</div>
|
||||||
|
<div class="text-white text-sm italic">${identity.note}</div>
|
||||||
|
</div>
|
||||||
|
` : ''}
|
||||||
|
|
||||||
|
<div class="pt-2 mt-2">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold mb-2">Swap History:</div>
|
||||||
|
<div class="grid grid-cols-2 gap-2">
|
||||||
|
<div class="text-center p-2 bg-gray-500 rounded-md">
|
||||||
|
<div class="text-lg font-bold ${getSuccessRateColor(stats.successRate)}">
|
||||||
|
${stats.successRate}%
|
||||||
|
</div>
|
||||||
|
<div class="text-xs text-white">Success Rate</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center p-2 bg-gray-500 rounded-md">
|
||||||
|
<div class="text-lg font-bold text-blue-500">${stats.totalBids}</div>
|
||||||
|
<div class="text-xs text-white">Total Trades</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-3 gap-2 mt-2 text-center text-xs">
|
||||||
|
<div>
|
||||||
|
<div class="text-green-600 font-semibold">
|
||||||
|
${stats.totalSuccessful}
|
||||||
|
</div>
|
||||||
|
<div class="text-white">Successful</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-yellow-600 font-semibold">
|
||||||
|
${stats.totalRejected}
|
||||||
|
</div>
|
||||||
|
<div class="text-white">Rejected</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-red-600 font-semibold">
|
||||||
|
${stats.totalFailed}
|
||||||
|
</div>
|
||||||
|
<div class="text-white">Failed</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// WebSocket Manager
|
||||||
|
const WebSocketManager = {
|
||||||
|
ws: null,
|
||||||
|
processingQueue: false,
|
||||||
|
reconnectTimeout: null,
|
||||||
|
maxReconnectAttempts: 5,
|
||||||
|
reconnectAttempts: 0,
|
||||||
|
reconnectDelay: 5000,
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
this.connect();
|
||||||
|
this.startHealthCheck();
|
||||||
|
},
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
if (this.ws?.readyState === WebSocket.OPEN) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const wsPort = window.ws_port || '11700';
|
||||||
|
this.ws = new WebSocket(`ws://${window.location.hostname}:${wsPort}`);
|
||||||
|
this.setupEventHandlers();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('WebSocket connection error:', error);
|
||||||
|
this.handleReconnect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setupEventHandlers() {
|
||||||
|
this.ws.onopen = () => {
|
||||||
|
state.wsConnected = true;
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
updateConnectionStatus('connected');
|
||||||
|
console.log('🟢 WebSocket connection established for Bid Requests');
|
||||||
|
updateBidsTable({ resetPage: true, refreshData: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onmessage = () => {
|
||||||
|
if (!this.processingQueue) {
|
||||||
|
this.processingQueue = true;
|
||||||
|
setTimeout(async () => {
|
||||||
|
try {
|
||||||
|
if (!state.isRefreshing) {
|
||||||
|
await updateBidsTable({ resetPage: false, refreshData: true });
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.processingQueue = false;
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onclose = () => {
|
||||||
|
state.wsConnected = false;
|
||||||
|
updateConnectionStatus('disconnected');
|
||||||
|
this.handleReconnect();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.ws.onerror = () => {
|
||||||
|
updateConnectionStatus('error');
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
startHealthCheck() {
|
||||||
|
setInterval(() => {
|
||||||
|
if (this.ws?.readyState !== WebSocket.OPEN) {
|
||||||
|
this.handleReconnect();
|
||||||
|
}
|
||||||
|
}, 30000);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleReconnect() {
|
||||||
|
if (this.reconnectTimeout) {
|
||||||
|
clearTimeout(this.reconnectTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reconnectAttempts++;
|
||||||
|
if (this.reconnectAttempts <= this.maxReconnectAttempts) {
|
||||||
|
const delay = this.reconnectDelay * Math.pow(1.5, this.reconnectAttempts - 1);
|
||||||
|
this.reconnectTimeout = setTimeout(() => this.connect(), delay);
|
||||||
|
} else {
|
||||||
|
updateConnectionStatus('error');
|
||||||
|
setTimeout(() => {
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
this.connect();
|
||||||
|
}, 60000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// UI
|
||||||
|
const updateConnectionStatus = (status) => {
|
||||||
|
const { statusDot, statusText } = elements;
|
||||||
|
if (!statusDot || !statusText) return;
|
||||||
|
|
||||||
|
const statusConfig = {
|
||||||
|
connected: {
|
||||||
|
dotClass: 'w-2.5 h-2.5 rounded-full bg-green-500 mr-2',
|
||||||
|
textClass: 'text-sm text-green-500',
|
||||||
|
message: 'Connected'
|
||||||
|
},
|
||||||
|
disconnected: {
|
||||||
|
dotClass: 'w-2.5 h-2.5 rounded-full bg-red-500 mr-2',
|
||||||
|
textClass: 'text-sm text-red-500',
|
||||||
|
message: 'Disconnected - Reconnecting...'
|
||||||
|
},
|
||||||
|
error: {
|
||||||
|
dotClass: 'w-2.5 h-2.5 rounded-full bg-yellow-500 mr-2',
|
||||||
|
textClass: 'text-sm text-yellow-500',
|
||||||
|
message: 'Connection Error'
|
||||||
|
},
|
||||||
|
default: {
|
||||||
|
dotClass: 'w-2.5 h-2.5 rounded-full bg-gray-500 mr-2',
|
||||||
|
textClass: 'text-sm text-gray-500',
|
||||||
|
message: 'Connecting...'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = statusConfig[status] || statusConfig.default;
|
||||||
|
statusDot.className = config.dotClass;
|
||||||
|
statusText.className = config.textClass;
|
||||||
|
statusText.textContent = config.message;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLoadingState = (isLoading) => {
|
||||||
|
state.isLoading = isLoading;
|
||||||
|
if (elements.refreshBidsButton) {
|
||||||
|
elements.refreshBidsButton.disabled = isLoading;
|
||||||
|
elements.refreshBidsButton.classList.toggle('opacity-75', isLoading);
|
||||||
|
elements.refreshBidsButton.classList.toggle('cursor-wait', isLoading);
|
||||||
|
|
||||||
|
const refreshIcon = elements.refreshBidsButton.querySelector('svg');
|
||||||
|
const refreshText = elements.refreshBidsButton.querySelector('#refreshText');
|
||||||
|
|
||||||
|
if (refreshIcon) {
|
||||||
|
// Add CSS transition for smoother animation
|
||||||
|
refreshIcon.style.transition = 'transform 0.3s ease';
|
||||||
|
refreshIcon.classList.toggle('animate-spin', isLoading);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (refreshText) {
|
||||||
|
refreshText.textContent = isLoading ? 'Refreshing...' : 'Refresh';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const createBidTableRow = async (bid) => {
|
||||||
|
if (!bid || !bid.bid_id) {
|
||||||
|
console.error('Invalid bid data:', bid);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const identity = await IdentityManager.getIdentityData(bid.addr_from);
|
||||||
|
const fromAmount = parseFloat(bid.amount_from) || 0;
|
||||||
|
const toAmount = parseFloat(bid.amount_to) || 0;
|
||||||
|
const rate = toAmount > 0 ? toAmount / fromAmount : 0;
|
||||||
|
const inverseRate = fromAmount > 0 ? fromAmount / toAmount : 0;
|
||||||
|
|
||||||
|
const fromSymbol = COIN_NAME_TO_SYMBOL[bid.coin_from] || bid.coin_from;
|
||||||
|
const toSymbol = COIN_NAME_TO_SYMBOL[bid.coin_to] || bid.coin_to;
|
||||||
|
|
||||||
|
const timeColor = getTimeStrokeColor(bid.expire_at);
|
||||||
|
const uniqueId = `${bid.bid_id}_${bid.created_at}`;
|
||||||
|
|
||||||
|
return `
|
||||||
|
<tr class="relative opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600" data-bid-id="${bid.bid_id}">
|
||||||
|
<td class="relative w-0 p-0 m-0">
|
||||||
|
<div class="absolute top-0 bottom-0 left-0 w-1"></div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Time Column -->
|
||||||
|
<td class="py-3 pl-1 pr-2 text-xs whitespace-nowrap">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="relative" data-tooltip-target="tooltip-time-${uniqueId}">
|
||||||
|
<svg class="w-5 h-5 rounded-full mr-4 cursor-pointer" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="${timeColor}" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col hidden xl:block">
|
||||||
|
<div class="text-xs whitespace-nowrap">
|
||||||
|
<span class="bold">Posted:</span> ${formatTimeAgo(bid.created_at)}
|
||||||
|
</div>
|
||||||
|
<div class="text-xs whitespace-nowrap">
|
||||||
|
<span class="bold">Expires in:</span> ${formatTime(bid.expire_at)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Details Column -->
|
||||||
|
<td class="py-8 px-4 text-xs text-left hidden xl:block">
|
||||||
|
<div class="flex flex-col gap-2 relative">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a href="/identity/${bid.addr_from}" data-tooltip-target="tooltip-identity-${uniqueId}" class="flex items-center">
|
||||||
|
<svg class="w-4 h-4 mr-2 text-gray-400 dark:text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="monospace ${identity?.label ? 'dark:text-white' : 'dark:text-white'}">
|
||||||
|
${identity?.label || formatAddress(bid.addr_from)}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||||
|
<span class="font-semibold">Offer ID:</span>
|
||||||
|
<a href="/offer/${bid.offer_id}" data-tooltip-target="tooltip-offer-${uniqueId}" class="hover:underline">
|
||||||
|
${formatAddress(bid.offer_id)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||||
|
<span class="font-semibold">Bid ID:</span>
|
||||||
|
<a href="/bid/${bid.bid_id}" data-tooltip-target="tooltip-bid-${uniqueId}" class="hover:underline">
|
||||||
|
${formatAddress(bid.bid_id)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- You Send Column -->
|
||||||
|
<td class="py-0">
|
||||||
|
<div class="py-3 px-4 text-left">
|
||||||
|
<div class="items-center monospace">
|
||||||
|
<div class="pr-2">
|
||||||
|
<div class="text-sm font-semibold">${fromAmount.toFixed(8)}</div>
|
||||||
|
<div class="text-sm text-gray-500 dark:text-gray-400">${bid.coin_from}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Swap Column -->
|
||||||
|
<td class="py-0">
|
||||||
|
<div class="py-3 px-4 text-center">
|
||||||
|
<div class="flex items-center justify-center">
|
||||||
|
<span class="inline-flex mr-3 align-middle items-center justify-center w-18 h-20 rounded">
|
||||||
|
<img class="h-12"
|
||||||
|
src="/static/images/coins/${bid.coin_from.replace(' ', '-')}.png"
|
||||||
|
alt="${bid.coin_from}"
|
||||||
|
onerror="this.src='/static/images/coins/default.png'">
|
||||||
|
</span>
|
||||||
|
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M12.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L14.586 11H3a1 1 0 110-2h11.586l-2.293-2.293a1 1 0 010-1.414z"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="inline-flex ml-3 align-middle items-center justify-center w-18 h-20 rounded">
|
||||||
|
<img class="h-12"
|
||||||
|
src="/static/images/coins/${bid.coin_to.replace(' ', '-')}.png"
|
||||||
|
alt="${bid.coin_to}"
|
||||||
|
onerror="this.src='/static/images/coins/default.png'">
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- You Get Column -->
|
||||||
|
<td class="py-0">
|
||||||
|
<div class="py-3 px-4 text-right">
|
||||||
|
<div class="items-center monospace">
|
||||||
|
<div class="text-sm font-semibold">${toAmount.toFixed(8)}</div>
|
||||||
|
<div class="text-sm text-gray-500 dark:text-gray-400">${bid.coin_to}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Rate Column -->
|
||||||
|
<td class="py-3 px-4 text-right semibold monospace item-center text-xs">
|
||||||
|
<div class="relative">
|
||||||
|
<div class="flex flex-col items-end">
|
||||||
|
<span class="bold text-gray-700 dark:text-white">
|
||||||
|
${rate.toFixed(8)} ${toSymbol}/${fromSymbol}
|
||||||
|
</span>
|
||||||
|
<span class="semibold text-gray-400 dark:text-gray-300">
|
||||||
|
${inverseRate.toFixed(8)} ${fromSymbol}/${toSymbol}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<!-- Actions Column -->
|
||||||
|
<td class="py-3 px-4 text-center">
|
||||||
|
<a href="/bid/${bid.bid_id}/accept"
|
||||||
|
class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200">
|
||||||
|
Accept
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Tooltips -->
|
||||||
|
<div id="tooltip-time-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||||
|
<div class="active-revoked-expired">
|
||||||
|
<span class="bold">
|
||||||
|
<div class="text-xs"><span class="bold">Posted:</span> ${formatTimeAgo(bid.created_at)}</div>
|
||||||
|
<div class="text-xs"><span class="bold">Expires in:</span> ${formatTime(bid.expire_at)}</div>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-5 text-xs">
|
||||||
|
<p class="font-bold mb-3">Time Indicator Colors:</p>
|
||||||
|
<p class="flex items-center">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#10B981" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#10B981"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Green: More than 30 minutes left
|
||||||
|
</p>
|
||||||
|
<p class="flex items-center mt-3">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#3B82F6" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#3B82F6"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Blue: Between 5 and 30 minutes left
|
||||||
|
</p>
|
||||||
|
<p class="flex items-center mt-3 mb-3">
|
||||||
|
<svg class="w-5 h-5 mr-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#9CA3AF" stroke-linejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="11"></circle>
|
||||||
|
<polyline points="12,6 12,12 18,12" stroke="#9CA3AF"></polyline>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
Grey: Less than 5 minutes left or expired
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tooltip-identity-${uniqueId}" role="tooltip" class="fixed z-50 py-3 px-4 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600 max-w-sm pointer-events-none">
|
||||||
|
${createIdentityTooltip(identity)}
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tooltip-offer-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Offer ID:</div>
|
||||||
|
<div class="monospace text-xs break-all">
|
||||||
|
${bid.offer_id}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tooltip-bid-${uniqueId}" role="tooltip" class="inline-block absolute z-50 py-2 px-3 text-sm font-medium text-white bg-gray-400 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip dark:bg-gray-600">
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="text-white text-xs tracking-wide font-semibold">Bid ID:</div>
|
||||||
|
<div class="monospace text-xs break-all">
|
||||||
|
${bid.bid_id}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDisplayText = (identity, address) => {
|
||||||
|
if (identity?.label) {
|
||||||
|
return identity.label;
|
||||||
|
}
|
||||||
|
return formatAddress(address);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createDetailsColumn = (bid, identity, uniqueId) => `
|
||||||
|
<td class="py-8 px-4 text-xs text-left hidden xl:block">
|
||||||
|
<div class="flex flex-col gap-2 relative">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<a href="/identity/${bid.addr_from}" data-tooltip-target="tooltip-identity-${uniqueId}" class="flex items-center">
|
||||||
|
<svg class="w-4 h-4 mr-2 text-gray-400 dark:text-white" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path fill-rule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clip-rule="evenodd"></path>
|
||||||
|
</svg>
|
||||||
|
<span class="monospace ${identity?.label ? 'dark:text-white' : 'dark:text-white'}">
|
||||||
|
${getDisplayText(identity, bid.addr_from)}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||||
|
<span class="font-semibold">Offer ID:</span>
|
||||||
|
<a href="/offer/${bid.offer_id}" data-tooltip-target="tooltip-offer-${uniqueId}" class="hover:underline">
|
||||||
|
${formatAddress(bid.offer_id)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="monospace text-xs text-gray-500 dark:text-gray-300">
|
||||||
|
<span class="font-semibold">Bid ID:</span>
|
||||||
|
<a href="/bid/${bid.bid_id}" data-tooltip-target="tooltip-bid-${uniqueId}" class="hover:underline">
|
||||||
|
${formatAddress(bid.bid_id)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
`;
|
||||||
|
|
||||||
|
async function updateBidsTable(options = {}) {
|
||||||
|
const { resetPage = false, refreshData = true } = options;
|
||||||
|
|
||||||
|
if (state.refreshPromise) {
|
||||||
|
await state.refreshPromise;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
updateLoadingState(true);
|
||||||
|
|
||||||
|
if (refreshData) {
|
||||||
|
state.refreshPromise = (async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/json/bids', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
sort_by: "created_at",
|
||||||
|
sort_dir: "desc",
|
||||||
|
with_available_or_active: true
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const allBids = await response.json();
|
||||||
|
if (!Array.isArray(allBids)) {
|
||||||
|
throw new Error('Invalid response format');
|
||||||
|
}
|
||||||
|
|
||||||
|
state.jsonData = allBids.filter(bid => bid.bid_state === "Received");
|
||||||
|
state.originalJsonData = [...state.jsonData];
|
||||||
|
} finally {
|
||||||
|
state.refreshPromise = null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
await state.refreshPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.availableBidsCount) {
|
||||||
|
elements.availableBidsCount.textContent = state.jsonData.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(state.jsonData.length / PAGE_SIZE);
|
||||||
|
|
||||||
|
if (resetPage && state.jsonData.length > 0) {
|
||||||
|
state.currentPage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.currentPage = Math.min(Math.max(1, state.currentPage), Math.max(1, totalPages));
|
||||||
|
|
||||||
|
const startIndex = (state.currentPage - 1) * PAGE_SIZE;
|
||||||
|
const endIndex = startIndex + PAGE_SIZE;
|
||||||
|
const currentPageBids = state.jsonData.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
if (elements.bidsBody) {
|
||||||
|
if (currentPageBids.length > 0) {
|
||||||
|
const rowPromises = currentPageBids.map(bid => createBidTableRow(bid));
|
||||||
|
const rows = await Promise.all(rowPromises);
|
||||||
|
elements.bidsBody.innerHTML = rows.join('');
|
||||||
|
|
||||||
|
if (window.TooltipManager) {
|
||||||
|
window.TooltipManager.cleanup();
|
||||||
|
const tooltipTriggers = document.querySelectorAll('[data-tooltip-target]');
|
||||||
|
tooltipTriggers.forEach(trigger => {
|
||||||
|
const targetId = trigger.getAttribute('data-tooltip-target');
|
||||||
|
const tooltipContent = document.getElementById(targetId);
|
||||||
|
if (tooltipContent) {
|
||||||
|
window.TooltipManager.create(trigger, tooltipContent.innerHTML, {
|
||||||
|
placement: trigger.getAttribute('data-tooltip-placement') || 'top'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
elements.bidsBody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="text-center py-4 text-gray-500 dark:text-white">
|
||||||
|
No available bids requests found
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.paginationControls) {
|
||||||
|
elements.paginationControls.style.display = totalPages > 1 ? 'flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.currentPageSpan) {
|
||||||
|
elements.currentPageSpan.textContent = state.currentPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.prevPageButton) {
|
||||||
|
elements.prevPageButton.style.display = state.currentPage > 1 ? 'inline-flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.nextPageButton) {
|
||||||
|
elements.nextPageButton.style.display = state.currentPage < totalPages ? 'inline-flex' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating bids table:', error);
|
||||||
|
if (elements.bidsBody) {
|
||||||
|
elements.bidsBody.innerHTML = `
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="text-center py-4 text-red-500">
|
||||||
|
Error loading bids. Please try again later.
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
updateLoadingState(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event
|
||||||
|
const setupEventListeners = () => {
|
||||||
|
if (elements.refreshBidsButton) {
|
||||||
|
elements.refreshBidsButton.addEventListener('click', async () => {
|
||||||
|
if (state.isRefreshing) return;
|
||||||
|
|
||||||
|
updateLoadingState(true);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateBidsTable({ resetPage: true, refreshData: true });
|
||||||
|
} finally {
|
||||||
|
updateLoadingState(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.prevPageButton) {
|
||||||
|
elements.prevPageButton.addEventListener('click', async () => {
|
||||||
|
if (state.isLoading) return;
|
||||||
|
if (state.currentPage > 1) {
|
||||||
|
state.currentPage--;
|
||||||
|
await updateBidsTable({ resetPage: false, refreshData: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elements.nextPageButton) {
|
||||||
|
elements.nextPageButton.addEventListener('click', async () => {
|
||||||
|
if (state.isLoading) return;
|
||||||
|
const totalPages = Math.ceil(state.jsonData.length / PAGE_SIZE);
|
||||||
|
if (state.currentPage < totalPages) {
|
||||||
|
state.currentPage++;
|
||||||
|
await updateBidsTable({ resetPage: false, refreshData: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Init
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
WebSocketManager.initialize();
|
||||||
|
setupEventListeners();
|
||||||
|
});
|
||||||
141
basicswap/static/js/bids_export.js
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
const BidExporter = {
|
||||||
|
toCSV(bids, type) {
|
||||||
|
if (!bids || !bids.length) {
|
||||||
|
return 'No data to export';
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSent = type === 'sent';
|
||||||
|
|
||||||
|
const headers = [
|
||||||
|
'Date/Time',
|
||||||
|
'Bid ID',
|
||||||
|
'Offer ID',
|
||||||
|
'From Address',
|
||||||
|
isSent ? 'You Send Amount' : 'You Receive Amount',
|
||||||
|
isSent ? 'You Send Coin' : 'You Receive Coin',
|
||||||
|
isSent ? 'You Receive Amount' : 'You Send Amount',
|
||||||
|
isSent ? 'You Receive Coin' : 'You Send Coin',
|
||||||
|
'Status',
|
||||||
|
'Created At',
|
||||||
|
'Expires At'
|
||||||
|
];
|
||||||
|
|
||||||
|
let csvContent = headers.join(',') + '\n';
|
||||||
|
|
||||||
|
bids.forEach(bid => {
|
||||||
|
const row = [
|
||||||
|
`"${formatTime(bid.created_at)}"`,
|
||||||
|
`"${bid.bid_id}"`,
|
||||||
|
`"${bid.offer_id}"`,
|
||||||
|
`"${bid.addr_from}"`,
|
||||||
|
isSent ? bid.amount_from : bid.amount_to,
|
||||||
|
`"${isSent ? bid.coin_from : bid.coin_to}"`,
|
||||||
|
isSent ? bid.amount_to : bid.amount_from,
|
||||||
|
`"${isSent ? bid.coin_to : bid.coin_from}"`,
|
||||||
|
`"${bid.bid_state}"`,
|
||||||
|
bid.created_at,
|
||||||
|
bid.expire_at
|
||||||
|
];
|
||||||
|
|
||||||
|
csvContent += row.join(',') + '\n';
|
||||||
|
});
|
||||||
|
|
||||||
|
return csvContent;
|
||||||
|
},
|
||||||
|
|
||||||
|
download(content, filename) {
|
||||||
|
try {
|
||||||
|
const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });
|
||||||
|
|
||||||
|
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
|
||||||
|
window.navigator.msSaveOrOpenBlob(blob, filename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
|
||||||
|
link.href = url;
|
||||||
|
link.download = filename;
|
||||||
|
link.style.display = 'none';
|
||||||
|
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}, 100);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error downloading CSV:', error);
|
||||||
|
|
||||||
|
const csvData = 'data:text/csv;charset=utf-8,' + encodeURIComponent(content);
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.setAttribute('href', csvData);
|
||||||
|
link.setAttribute('download', filename);
|
||||||
|
link.style.display = 'none';
|
||||||
|
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
document.body.removeChild(link);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
exportCurrentView() {
|
||||||
|
const type = state.currentTab;
|
||||||
|
const data = state.data[type];
|
||||||
|
|
||||||
|
if (!data || !data.length) {
|
||||||
|
alert('No data to export');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const csvContent = this.toCSV(data, type);
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
const dateStr = now.toISOString().split('T')[0];
|
||||||
|
const filename = `bsx_${type}_bids_${dateStr}.csv`;
|
||||||
|
|
||||||
|
this.download(csvContent, filename);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
if (typeof state !== 'undefined' && typeof EventManager !== 'undefined') {
|
||||||
|
const exportSentButton = document.getElementById('exportSentBids');
|
||||||
|
if (exportSentButton) {
|
||||||
|
EventManager.add(exportSentButton, 'click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
state.currentTab = 'sent';
|
||||||
|
BidExporter.exportCurrentView();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportReceivedButton = document.getElementById('exportReceivedBids');
|
||||||
|
if (exportReceivedButton) {
|
||||||
|
EventManager.add(exportReceivedButton, 'click', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
state.currentTab = 'received';
|
||||||
|
BidExporter.exportCurrentView();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
const originalCleanup = window.cleanup || function(){};
|
||||||
|
window.cleanup = function() {
|
||||||
|
originalCleanup();
|
||||||
|
|
||||||
|
const exportSentButton = document.getElementById('exportSentBids');
|
||||||
|
const exportReceivedButton = document.getElementById('exportReceivedBids');
|
||||||
|
|
||||||
|
if (exportSentButton && typeof EventManager !== 'undefined') {
|
||||||
|
EventManager.remove(exportSentButton, 'click');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exportReceivedButton && typeof EventManager !== 'undefined') {
|
||||||
|
EventManager.remove(exportReceivedButton, 'click');
|
||||||
|
}
|
||||||
|
};
|
||||||
1988
basicswap/static/js/bids_sentreceived.js
Normal file
@@ -14,7 +14,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const image = selectedOption.getAttribute('data-image') || '';
|
const image = selectedOption.getAttribute('data-image') || '';
|
||||||
const name = selectedOption.textContent.trim();
|
const name = selectedOption.textContent.trim();
|
||||||
select.style.backgroundImage = image ? `url(${image}?${new Date().getTime()})` : '';
|
select.style.backgroundImage = image ? `url(${image}?${new Date().getTime()})` : '';
|
||||||
|
|
||||||
const selectImage = select.nextElementSibling.querySelector('.select-image');
|
const selectImage = select.nextElementSibling.querySelector('.select-image');
|
||||||
if (selectImage) {
|
if (selectImage) {
|
||||||
selectImage.src = image;
|
selectImage.src = image;
|
||||||
|
|||||||
190
basicswap/static/js/dropdown.js
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function positionElement(targetEl, triggerEl, placement = 'bottom', offsetDistance = 8) {
|
||||||
|
targetEl.style.visibility = 'hidden';
|
||||||
|
targetEl.style.display = 'block';
|
||||||
|
|
||||||
|
const triggerRect = triggerEl.getBoundingClientRect();
|
||||||
|
const targetRect = targetEl.getBoundingClientRect();
|
||||||
|
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
||||||
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
|
|
||||||
|
let top, left;
|
||||||
|
|
||||||
|
top = triggerRect.bottom + offsetDistance;
|
||||||
|
left = triggerRect.left + (triggerRect.width - targetRect.width) / 2;
|
||||||
|
|
||||||
|
switch (placement) {
|
||||||
|
case 'bottom-start':
|
||||||
|
left = triggerRect.left;
|
||||||
|
break;
|
||||||
|
case 'bottom-end':
|
||||||
|
left = triggerRect.right - targetRect.width;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewport = {
|
||||||
|
width: window.innerWidth,
|
||||||
|
height: window.innerHeight
|
||||||
|
};
|
||||||
|
|
||||||
|
if (left < 10) left = 10;
|
||||||
|
if (left + targetRect.width > viewport.width - 10) {
|
||||||
|
left = viewport.width - targetRect.width - 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetEl.style.position = 'fixed';
|
||||||
|
targetEl.style.top = `${Math.round(top)}px`;
|
||||||
|
targetEl.style.left = `${Math.round(left)}px`;
|
||||||
|
targetEl.style.margin = '0';
|
||||||
|
targetEl.style.maxHeight = `${viewport.height - top - 10}px`;
|
||||||
|
targetEl.style.overflow = 'auto';
|
||||||
|
targetEl.style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dropdown {
|
||||||
|
constructor(targetEl, triggerEl, options = {}) {
|
||||||
|
this._targetEl = targetEl;
|
||||||
|
this._triggerEl = triggerEl;
|
||||||
|
this._options = {
|
||||||
|
placement: options.placement || 'bottom',
|
||||||
|
offset: options.offset || 5,
|
||||||
|
onShow: options.onShow || function() {},
|
||||||
|
onHide: options.onHide || function() {}
|
||||||
|
};
|
||||||
|
this._visible = false;
|
||||||
|
this._initialized = false;
|
||||||
|
this._handleScroll = this._handleScroll.bind(this);
|
||||||
|
this._handleResize = this._handleResize.bind(this);
|
||||||
|
this._handleOutsideClick = this._handleOutsideClick.bind(this);
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if (!this._initialized) {
|
||||||
|
this._targetEl.style.margin = '0';
|
||||||
|
this._targetEl.style.display = 'none';
|
||||||
|
this._targetEl.style.position = 'fixed';
|
||||||
|
this._targetEl.style.zIndex = '50';
|
||||||
|
|
||||||
|
this._setupEventListeners();
|
||||||
|
this._initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupEventListeners() {
|
||||||
|
this._triggerEl.addEventListener('click', (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
this.toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', this._handleOutsideClick);
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') this.hide();
|
||||||
|
});
|
||||||
|
window.addEventListener('scroll', this._handleScroll, true);
|
||||||
|
window.addEventListener('resize', this._handleResize);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleScroll() {
|
||||||
|
if (this._visible) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
positionElement(
|
||||||
|
this._targetEl,
|
||||||
|
this._triggerEl,
|
||||||
|
this._options.placement,
|
||||||
|
this._options.offset
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleResize() {
|
||||||
|
if (this._visible) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
positionElement(
|
||||||
|
this._targetEl,
|
||||||
|
this._triggerEl,
|
||||||
|
this._options.placement,
|
||||||
|
this._options.offset
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleOutsideClick(e) {
|
||||||
|
if (this._visible &&
|
||||||
|
!this._targetEl.contains(e.target) &&
|
||||||
|
!this._triggerEl.contains(e.target)) {
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
if (!this._visible) {
|
||||||
|
this._targetEl.style.display = 'block';
|
||||||
|
this._targetEl.style.visibility = 'hidden';
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
positionElement(
|
||||||
|
this._targetEl,
|
||||||
|
this._triggerEl,
|
||||||
|
this._options.placement,
|
||||||
|
this._options.offset
|
||||||
|
);
|
||||||
|
|
||||||
|
this._visible = true;
|
||||||
|
this._options.onShow();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
if (this._visible) {
|
||||||
|
this._targetEl.style.display = 'none';
|
||||||
|
this._visible = false;
|
||||||
|
this._options.onHide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle() {
|
||||||
|
if (this._visible) {
|
||||||
|
this.hide();
|
||||||
|
} else {
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
document.removeEventListener('click', this._handleOutsideClick);
|
||||||
|
window.removeEventListener('scroll', this._handleScroll, true);
|
||||||
|
window.removeEventListener('resize', this._handleResize);
|
||||||
|
this._initialized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initDropdowns() {
|
||||||
|
document.querySelectorAll('[data-dropdown-toggle]').forEach(triggerEl => {
|
||||||
|
const targetId = triggerEl.getAttribute('data-dropdown-toggle');
|
||||||
|
const targetEl = document.getElementById(targetId);
|
||||||
|
|
||||||
|
if (targetEl) {
|
||||||
|
const placement = triggerEl.getAttribute('data-dropdown-placement');
|
||||||
|
new Dropdown(targetEl, triggerEl, {
|
||||||
|
placement: placement || 'bottom-start'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initDropdowns);
|
||||||
|
} else {
|
||||||
|
initDropdowns();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Dropdown = Dropdown;
|
||||||
|
window.initDropdowns = initDropdowns;
|
||||||
|
|
||||||
|
})(window);
|
||||||
1825
basicswap/static/js/libs/popper.js
Normal file
2516
basicswap/static/js/libs/tippy.js
Normal file
@@ -19,8 +19,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const backdrop = document.querySelectorAll('.navbar-backdrop');
|
const backdrop = document.querySelectorAll('.navbar-backdrop');
|
||||||
|
|
||||||
if (close.length) {
|
if (close.length) {
|
||||||
for (var i = 0; i < close.length; i++) {
|
for (var k = 0; k < close.length; k++) {
|
||||||
close[i].addEventListener('click', function() {
|
close[k].addEventListener('click', function() {
|
||||||
for (var j = 0; j < menu.length; j++) {
|
for (var j = 0; j < menu.length; j++) {
|
||||||
menu[j].classList.toggle('hidden');
|
menu[j].classList.toggle('hidden');
|
||||||
}
|
}
|
||||||
@@ -29,12 +29,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (backdrop.length) {
|
if (backdrop.length) {
|
||||||
for (var i = 0; i < backdrop.length; i++) {
|
for (var l = 0; l < backdrop.length; l++) {
|
||||||
backdrop[i].addEventListener('click', function() {
|
backdrop[l].addEventListener('click', function() {
|
||||||
for (var j = 0; j < menu.length; j++) {
|
for (var j = 0; j < menu.length; j++) {
|
||||||
menu[j].classList.toggle('hidden');
|
menu[j].classList.toggle('hidden');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
window.addEventListener('DOMContentLoaded', (event) => {
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
let err_msgs = document.querySelectorAll('p.error_msg');
|
const err_msgs = document.querySelectorAll('p.error_msg');
|
||||||
for (let i = 0; i < err_msgs.length; i++) {
|
for (let i = 0; i < err_msgs.length; i++) {
|
||||||
err_msg = err_msgs[i].innerText;
|
err_msg = err_msgs[i].innerText;
|
||||||
if (err_msg.indexOf('coin_to') >= 0 || err_msg.indexOf('Coin To') >= 0) {
|
if (err_msg.indexOf('coin_to') >= 0 || err_msg.indexOf('Coin To') >= 0) {
|
||||||
@@ -29,9 +29,9 @@ window.addEventListener('DOMContentLoaded', (event) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// remove error class on input or select focus
|
// remove error class on input or select focus
|
||||||
let inputs = document.querySelectorAll('input.error');
|
const inputs = document.querySelectorAll('input.error');
|
||||||
let selects = document.querySelectorAll('select.error');
|
const selects = document.querySelectorAll('select.error');
|
||||||
let elements = [...inputs, ...selects];
|
const elements = [...inputs, ...selects];
|
||||||
elements.forEach((element) => {
|
elements.forEach((element) => {
|
||||||
element.addEventListener('focus', (event) => {
|
element.addEventListener('focus', (event) => {
|
||||||
event.target.classList.remove('error');
|
event.target.classList.remove('error');
|
||||||
|
|||||||
2921
basicswap/static/js/offers.js
Normal file
115
basicswap/static/js/tabs.js
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
class Tabs {
|
||||||
|
constructor(tabsEl, items = [], options = {}) {
|
||||||
|
this._tabsEl = tabsEl;
|
||||||
|
this._items = items;
|
||||||
|
this._activeTab = options.defaultTabId ? this.getTab(options.defaultTabId) : null;
|
||||||
|
this._options = {
|
||||||
|
defaultTabId: options.defaultTabId || null,
|
||||||
|
activeClasses: options.activeClasses || 'text-blue-600 hover:text-blue-600 dark:text-blue-500 dark:hover:text-blue-500 border-blue-600 dark:border-blue-500',
|
||||||
|
inactiveClasses: options.inactiveClasses || 'dark:border-transparent text-gray-500 hover:text-gray-600 dark:text-gray-400 border-gray-100 hover:border-gray-300 dark:border-gray-700 dark:hover:text-gray-300',
|
||||||
|
onShow: options.onShow || function() {}
|
||||||
|
};
|
||||||
|
this._initialized = false;
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if (this._items.length && !this._initialized) {
|
||||||
|
if (!this._activeTab) {
|
||||||
|
this.setActiveTab(this._items[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.show(this._activeTab.id, true);
|
||||||
|
|
||||||
|
this._items.forEach(tab => {
|
||||||
|
tab.triggerEl.addEventListener('click', () => {
|
||||||
|
this.show(tab.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this._initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show(tabId, force = false) {
|
||||||
|
const tab = this.getTab(tabId);
|
||||||
|
|
||||||
|
if ((tab !== this._activeTab) || force) {
|
||||||
|
this._items.forEach(t => {
|
||||||
|
if (t !== tab) {
|
||||||
|
t.triggerEl.classList.remove(...this._options.activeClasses.split(' '));
|
||||||
|
t.triggerEl.classList.add(...this._options.inactiveClasses.split(' '));
|
||||||
|
t.targetEl.classList.add('hidden');
|
||||||
|
t.triggerEl.setAttribute('aria-selected', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
tab.triggerEl.classList.add(...this._options.activeClasses.split(' '));
|
||||||
|
tab.triggerEl.classList.remove(...this._options.inactiveClasses.split(' '));
|
||||||
|
tab.triggerEl.setAttribute('aria-selected', true);
|
||||||
|
tab.targetEl.classList.remove('hidden');
|
||||||
|
|
||||||
|
this.setActiveTab(tab);
|
||||||
|
this._options.onShow(this, tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTab(id) {
|
||||||
|
return this._items.find(t => t.id === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
getActiveTab() {
|
||||||
|
return this._activeTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveTab(tab) {
|
||||||
|
this._activeTab = tab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTabs() {
|
||||||
|
document.querySelectorAll('[data-tabs-toggle]').forEach(tabsEl => {
|
||||||
|
const items = [];
|
||||||
|
let defaultTabId = null;
|
||||||
|
|
||||||
|
tabsEl.querySelectorAll('[role="tab"]').forEach(triggerEl => {
|
||||||
|
const isActive = triggerEl.getAttribute('aria-selected') === 'true';
|
||||||
|
const tab = {
|
||||||
|
id: triggerEl.getAttribute('data-tabs-target'),
|
||||||
|
triggerEl: triggerEl,
|
||||||
|
targetEl: document.querySelector(triggerEl.getAttribute('data-tabs-target'))
|
||||||
|
};
|
||||||
|
items.push(tab);
|
||||||
|
|
||||||
|
if (isActive) {
|
||||||
|
defaultTabId = tab.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
new Tabs(tabsEl, items, {
|
||||||
|
defaultTabId: defaultTabId
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = `
|
||||||
|
[data-tabs-toggle] [role="tab"] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', initTabs);
|
||||||
|
} else {
|
||||||
|
initTabs();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.Tabs = Tabs;
|
||||||
|
window.initTabs = initTabs;
|
||||||
|
|
||||||
|
})(window);
|
||||||
306
basicswap/static/js/tooltips.js
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
class TooltipManager {
|
||||||
|
constructor() {
|
||||||
|
this.activeTooltips = new Map();
|
||||||
|
this.sizeCheckIntervals = new Map();
|
||||||
|
this.setupStyles();
|
||||||
|
this.setupCleanupEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
static initialize() {
|
||||||
|
if (!window.TooltipManager) {
|
||||||
|
window.TooltipManager = new TooltipManager();
|
||||||
|
}
|
||||||
|
return window.TooltipManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
create(element, content, options = {}) {
|
||||||
|
if (!element) return null;
|
||||||
|
|
||||||
|
this.destroy(element);
|
||||||
|
|
||||||
|
const checkSize = () => {
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
if (rect.width && rect.height) {
|
||||||
|
clearInterval(this.sizeCheckIntervals.get(element));
|
||||||
|
this.sizeCheckIntervals.delete(element);
|
||||||
|
this.createTooltip(element, content, options, rect);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sizeCheckIntervals.set(element, setInterval(checkSize, 50));
|
||||||
|
checkSize();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
createTooltip(element, content, options, rect) {
|
||||||
|
const targetId = element.getAttribute('data-tooltip-target');
|
||||||
|
let bgClass = 'bg-gray-400';
|
||||||
|
let arrowColor = 'rgb(156 163 175)';
|
||||||
|
|
||||||
|
if (targetId?.includes('tooltip-offer-')) {
|
||||||
|
const offerId = targetId.split('tooltip-offer-')[1];
|
||||||
|
const [actualOfferId] = offerId.split('_');
|
||||||
|
|
||||||
|
if (window.jsonData) {
|
||||||
|
const offer = window.jsonData.find(o =>
|
||||||
|
o.unique_id === offerId ||
|
||||||
|
o.offer_id === actualOfferId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (offer) {
|
||||||
|
if (offer.is_revoked) {
|
||||||
|
bgClass = 'bg-red-500';
|
||||||
|
arrowColor = 'rgb(239 68 68)';
|
||||||
|
} else if (offer.is_own_offer) {
|
||||||
|
bgClass = 'bg-gray-300';
|
||||||
|
arrowColor = 'rgb(209 213 219)';
|
||||||
|
} else {
|
||||||
|
bgClass = 'bg-green-700';
|
||||||
|
arrowColor = 'rgb(21 128 61)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const instance = tippy(element, {
|
||||||
|
content,
|
||||||
|
allowHTML: true,
|
||||||
|
placement: options.placement || 'top',
|
||||||
|
appendTo: document.body,
|
||||||
|
animation: false,
|
||||||
|
duration: 0,
|
||||||
|
delay: 0,
|
||||||
|
interactive: true,
|
||||||
|
arrow: false,
|
||||||
|
theme: '',
|
||||||
|
moveTransition: 'none',
|
||||||
|
offset: [0, 10],
|
||||||
|
popperOptions: {
|
||||||
|
strategy: 'fixed',
|
||||||
|
modifiers: [
|
||||||
|
{
|
||||||
|
name: 'preventOverflow',
|
||||||
|
options: {
|
||||||
|
boundary: 'viewport',
|
||||||
|
padding: 10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'flip',
|
||||||
|
options: {
|
||||||
|
padding: 10,
|
||||||
|
fallbackPlacements: ['top', 'bottom', 'right', 'left']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
onCreate(instance) {
|
||||||
|
instance._originalPlacement = instance.props.placement;
|
||||||
|
},
|
||||||
|
onShow(instance) {
|
||||||
|
if (!document.body.contains(element)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
if (!rect.width || !rect.height) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.setProps({
|
||||||
|
placement: instance._originalPlacement
|
||||||
|
});
|
||||||
|
|
||||||
|
if (instance.popper.firstElementChild) {
|
||||||
|
instance.popper.firstElementChild.classList.add(bgClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
onMount(instance) {
|
||||||
|
if (instance.popper.firstElementChild) {
|
||||||
|
instance.popper.firstElementChild.classList.add(bgClass);
|
||||||
|
}
|
||||||
|
const arrow = instance.popper.querySelector('.tippy-arrow');
|
||||||
|
if (arrow) {
|
||||||
|
arrow.style.setProperty('color', arrowColor, 'important');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const id = element.getAttribute('data-tooltip-trigger-id') ||
|
||||||
|
`tooltip-${Math.random().toString(36).substring(7)}`;
|
||||||
|
element.setAttribute('data-tooltip-trigger-id', id);
|
||||||
|
this.activeTooltips.set(id, instance);
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy(element) {
|
||||||
|
if (!element) return;
|
||||||
|
|
||||||
|
if (this.sizeCheckIntervals.has(element)) {
|
||||||
|
clearInterval(this.sizeCheckIntervals.get(element));
|
||||||
|
this.sizeCheckIntervals.delete(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = element.getAttribute('data-tooltip-trigger-id');
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
const instance = this.activeTooltips.get(id);
|
||||||
|
if (instance?.[0]) {
|
||||||
|
try {
|
||||||
|
instance[0].destroy();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error destroying tooltip:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.activeTooltips.delete(id);
|
||||||
|
element.removeAttribute('data-tooltip-trigger-id');
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
this.sizeCheckIntervals.forEach((interval) => clearInterval(interval));
|
||||||
|
this.sizeCheckIntervals.clear();
|
||||||
|
|
||||||
|
this.activeTooltips.forEach((instance, id) => {
|
||||||
|
if (instance?.[0]) {
|
||||||
|
try {
|
||||||
|
instance[0].destroy();
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error cleaning up tooltip:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.activeTooltips.clear();
|
||||||
|
|
||||||
|
document.querySelectorAll('[data-tippy-root]').forEach(element => {
|
||||||
|
if (element.parentNode) {
|
||||||
|
element.parentNode.removeChild(element);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setupStyles() {
|
||||||
|
if (document.getElementById('tooltip-styles')) return;
|
||||||
|
|
||||||
|
document.head.insertAdjacentHTML('beforeend', `
|
||||||
|
<style id="tooltip-styles">
|
||||||
|
[data-tippy-root] {
|
||||||
|
position: fixed !important;
|
||||||
|
z-index: 9999 !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
color: white;
|
||||||
|
position: relative !important;
|
||||||
|
pointer-events: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-content {
|
||||||
|
padding: 0.5rem 0.75rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box .bg-gray-400 {
|
||||||
|
background-color: rgb(156 163 175);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
.tippy-box:has(.bg-gray-400) .tippy-arrow {
|
||||||
|
color: rgb(156 163 175);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box .bg-red-500 {
|
||||||
|
background-color: rgb(239 68 68);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
.tippy-box:has(.bg-red-500) .tippy-arrow {
|
||||||
|
color: rgb(239 68 68);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box .bg-gray-300 {
|
||||||
|
background-color: rgb(209 213 219);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
.tippy-box:has(.bg-gray-300) .tippy-arrow {
|
||||||
|
color: rgb(209 213 219);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box .bg-green-700 {
|
||||||
|
background-color: rgb(21 128 61);
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
}
|
||||||
|
.tippy-box:has(.bg-green-700) .tippy-arrow {
|
||||||
|
color: rgb(21 128 61);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^='top'] > .tippy-arrow::before {
|
||||||
|
border-top-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^='bottom'] > .tippy-arrow::before {
|
||||||
|
border-bottom-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^='left'] > .tippy-arrow::before {
|
||||||
|
border-left-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^='right'] > .tippy-arrow::before {
|
||||||
|
border-right-color: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^='top'] > .tippy-arrow {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^='bottom'] > .tippy-arrow {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^='left'] > .tippy-arrow {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tippy-box[data-placement^='right'] > .tippy-arrow {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupCleanupEvents() {
|
||||||
|
window.addEventListener('beforeunload', () => this.cleanup());
|
||||||
|
window.addEventListener('unload', () => this.cleanup());
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (document.hidden) {
|
||||||
|
this.cleanup();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeTooltips(selector = '[data-tooltip-target]') {
|
||||||
|
document.querySelectorAll(selector).forEach(element => {
|
||||||
|
const targetId = element.getAttribute('data-tooltip-target');
|
||||||
|
const tooltipContent = document.getElementById(targetId);
|
||||||
|
|
||||||
|
if (tooltipContent) {
|
||||||
|
this.create(element, tooltipContent.innerHTML, {
|
||||||
|
placement: element.getAttribute('data-tooltip-placement') || 'top'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = TooltipManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
TooltipManager.initialize();
|
||||||
|
});
|
||||||
@@ -1,115 +1,118 @@
|
|||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
{% from 'style.html' import breadcrumb_line_svg, circular_arrows_svg %}
|
{% from 'style.html' import breadcrumb_line_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, input_arrow_down_svg %}
|
||||||
<div class="container mx-auto">
|
|
||||||
<section class="p-5 mt-5">
|
<section class="py-3 px-4 mt-6">
|
||||||
<div class="flex flex-wrap items-center -m-2">
|
<div class="lg:container mx-auto">
|
||||||
<div class="w-full md:w-1/2 p-2">
|
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||||
<li>
|
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">
|
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||||
<p>Home</p>
|
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||||
</a>
|
<div class="w-full md:w-1/2 p-3">
|
||||||
</li>
|
<h2 class="mb-3 text-2xl font-bold text-white tracking-tighter">Swaps in Progress</h2>
|
||||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
<p class="font-normal text-coolGray-200 dark:text-white">Monitor your currently active swap transactions.</p>
|
||||||
<li>
|
|
||||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/active">Swaps In Progress</a>
|
|
||||||
</li>
|
|
||||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="py-4">
|
|
||||||
<div class="container px-4 mx-auto">
|
|
||||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
|
||||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
|
||||||
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
|
||||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
|
||||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
|
||||||
<div class="w-full md:w-1/2 p-3">
|
|
||||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">Swaps in Progress</h2>
|
|
||||||
<p class="font-normal text-coolGray-200 dark:text-white">Your swaps that are currently in progress.</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto">
|
|
||||||
{% if refresh %}
|
|
||||||
<a id="refresh" href="/active" class="rounded-full flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
|
||||||
{{ circular_arrows_svg | safe }}
|
|
||||||
<span>Refresh 30 seconds</span>
|
|
||||||
</a>
|
|
||||||
{% else %}
|
|
||||||
<a id="refresh" href="/active" class="flex flex-wrap justify-center px-5 py-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white borderdark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
|
||||||
{{ circular_arrows_svg | safe }}
|
|
||||||
<span>Refresh</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
<section>
|
</section>
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
|
||||||
<div class="pb-6 border-coolGray-100">
|
{% include 'inc_messages.html' %}
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
|
||||||
<div class="w-full pt-2">
|
<section>
|
||||||
<div class="container mt-5 mx-auto">
|
<div class="mt-5 lg:container mx-auto lg:px-0 px-6">
|
||||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
<div class="px-6">
|
<div class="px-0">
|
||||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
<div class="w-auto mt-6 overflow-auto lg:overflow-hidden">
|
||||||
<table class="w-full min-w-max text-sm">
|
<table class="w-full min-w-max">
|
||||||
<thead class="uppercase">
|
<thead class="uppercase">
|
||||||
<tr class="text-left">
|
<tr>
|
||||||
<th class="p-0">
|
<th class="p-0" data-sortable="true" data-column-index="0">
|
||||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
<div class="py-3 pl-4 justify-center rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid ID</span>
|
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold"></span>
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
|
||||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Offer ID</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
|
||||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid Status</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600">
|
|
||||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">ITX Status</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
|
||||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">PTX Status</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
{% for s in active_swaps %}
|
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
|
||||||
<td class="py-3 px-6 monospace">
|
|
||||||
<a href=/bid/{{ s[0] }}>{{ s[0]|truncate(50,true,'...',0) }}</a>
|
|
||||||
</td>
|
|
||||||
<td class="py-3 px-6 monospace">
|
|
||||||
<a href=/offer/{{ s[1] }}>{{ s[1]|truncate(50,true,'...',0) }}</a>
|
|
||||||
</td>
|
|
||||||
<td class="py-3 px-6 w-52 whitespace-normal break-words">{{ s[2] }}</td>
|
|
||||||
<td class="py-3 px-6">{{ s[3] }}</td>
|
|
||||||
<td class="py-3 px-6">{{ s[4] }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 pl-6 pr-3 justify-center bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold">Time</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0 hidden xl:block">
|
||||||
|
<div class="py-3 px-4 text-left bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-left">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Send</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Swap</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Receive</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Status</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 rounded-tr-xl">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="active-swaps-body"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-b-md">
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="flex flex-wrap justify-between items-center pl-6 pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex items-center mr-4">
|
||||||
|
<span id="status-dot" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
|
||||||
|
<span id="status-text" class="text-sm text-gray-500">Connecting...</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm font-heading dark:text-gray-400 mr-4">Active Swaps: <span id="activeSwapsCount">0</span></p>
|
||||||
|
{% if debug_ui_mode == true %}
|
||||||
|
<button type="button" id="refreshSwaps" class="inline-flex items-center px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||||||
|
</svg>
|
||||||
|
<span id="refreshText">Refresh</span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
<div id="pagination-controls" class="flex items-center space-x-2" style="display: none;">
|
||||||
|
<button id="prevPage" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200">
|
||||||
|
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||||
|
</svg>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<p class="text-sm font-heading dark:text-white">Page <span id="currentPage">1</span></p>
|
||||||
|
<button id="nextPage" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200">
|
||||||
|
Next
|
||||||
|
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
|
<script src="/static/js/active.js"></script>
|
||||||
|
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,220 +1,368 @@
|
|||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
{% from 'style.html' import breadcrumb_line_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, circular_arrows_svg, input_arrow_down_svg %}
|
{% from 'style.html' import breadcrumb_line_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, circular_arrows_svg, input_arrow_down_svg, arrow_right_svg %}
|
||||||
<div class="container mx-auto">
|
|
||||||
<section class="p-5 mt-5">
|
|
||||||
<div class="flex flex-wrap items-center -m-2">
|
<section class="py-3 px-4 mt-6">
|
||||||
<div class="w-full md:w-1/2 p-2">
|
<div class="lg:container mx-auto">
|
||||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||||
<li>
|
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/"><p>Home</p></a>
|
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||||
</li>
|
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||||
<li> {{ breadcrumb_line_svg | safe }} </li>
|
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||||
<li> <a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="#">{{ page_type_available }} {{ page_type_received }} {{ page_type_sent }}</a> </li>
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<li> {{ breadcrumb_line_svg | safe }} </li>
|
<h2 class="mb-3 text-2xl font-bold text-white tracking-tighter">Sent Bids / Received Bids</h2>
|
||||||
</ul>
|
<p class="font-normal text-coolGray-200 dark:text-white">View, and manage bids.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="py-4">
|
|
||||||
<div class="container px-4 mx-auto">
|
|
||||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
|
||||||
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt=""> <img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
|
||||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
|
||||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
|
||||||
<div class="w-full md:w-1/2 p-3">
|
|
||||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">{{ page_type_available }} {{ page_type_received }} {{ page_type_sent }}</h2>
|
|
||||||
<p class="font-normal text-coolGray-200 dark:text-white">{{ page_type_available_description }} {{ page_type_received_description }} {{ page_type_sent_description }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto">
|
|
||||||
{% if refresh %}
|
|
||||||
<a id="refresh" href="/bid/{{ bid_id }}" class="rounded-full mr-5 flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
|
||||||
{{ circular_arrows_svg | safe }}
|
|
||||||
<span>Refresh {{ refresh }} seconds</span>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
{% include 'inc_messages.html' %}
|
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
|
||||||
<div class="pb-6 border-coolGray-100">
|
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
|
||||||
<div class="w-full mx-auto pt-2">
|
|
||||||
<form method="post">
|
|
||||||
<div class="flex items-center justify-center pb-4 dark:text-white">
|
|
||||||
<div class="rounded-b-md">
|
|
||||||
<div class="w-full md:w-0/12">
|
|
||||||
<div class="flex flex-wrap justify-center -m-1.5">
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<div class="relative">
|
|
||||||
{{ input_arrow_down_svg | safe }}
|
|
||||||
<select name="sort_by" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
|
||||||
<option value="created_at" {% if filters.sort_by=='created_at' %} selected{% endif %}>Time At</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<div class="relative">
|
|
||||||
{{ input_arrow_down_svg | safe }}
|
|
||||||
<select name="sort_dir" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
|
||||||
<option value="asc" {% if filters.sort_dir=='asc' %} selected{% endif %}>Ascending</option>
|
|
||||||
<option value="desc" {% if filters.sort_dir=='desc' %} selected{% endif %}>Descending</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<p class="text-sm font-heading bold">State:</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<div class="relative">
|
|
||||||
{{ input_arrow_down_svg | safe }}
|
|
||||||
<select name="state" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
|
||||||
<option value="-1" {% if filters.bid_state_ind==-1 %} selected{% endif %}>Any</option>
|
|
||||||
{% for s in data.bid_states %}
|
|
||||||
<option value="{{ s[0] }}" {% if filters.bid_state_ind==s[0] %} selected{% endif %}>{{ s[1] }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<p class="text-sm font-heading bold">Include Expired:</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<div class="relative">
|
|
||||||
{{ input_arrow_down_svg | safe }}
|
|
||||||
<select name="with_expired" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
|
||||||
<option value="true" {% if filters.with_expired==true %} selected{% endif %}>Include</option>
|
|
||||||
<option value="false" {% if filters.with_expired==false %} selected{% endif %}>Exclude</option>
|
|
||||||
</select> </div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<div class="relative">
|
|
||||||
<button type="submit" name='clearfilters' value="Clear Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm hover:text-white dark:text-white dark:bg-gray-500 bg-coolGray-200 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-coolGray-200 dark:border-gray-400 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
|
||||||
<span>Clear Filters</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<div class="relative"> <button type="submit" name='applyfilters' value="Apply Filters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
|
||||||
{{ filter_apply_svg | safe }}
|
|
||||||
<span>Apply Filters</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="container mt-5 mx-auto">
|
|
||||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
|
||||||
<div class="px-6">
|
|
||||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
|
||||||
<table class="w-full min-w-max">
|
|
||||||
<thead class="uppercase">
|
|
||||||
<tr class="text-left">
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Date/Time at</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid ID</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Offer ID</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid From</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Bid Status</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">ITX Status</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">PTX Status</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for b in bids %}
|
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
|
||||||
<th scope="row" class="flex items-center py-7 px-46 text-gray-900 whitespace-nowrap"> <svg class="w-5 h-5 rounded-full ml-5" xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 24 24">
|
|
||||||
<g stroke-linecap="round" stroke-width="2" fill="none" stroke="#6b7280" stroke-linejoin="round">
|
|
||||||
<circle cx="12" cy="12" r="11"></circle>
|
|
||||||
<polyline points=" 12,6 12,12 18,12 " stroke="#6b7280"></polyline>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
<div class="pl-3">
|
|
||||||
<div class="font-semibold text-xs dark:text-white">{{ b[0] }}</div>
|
|
||||||
</div>
|
|
||||||
</th>
|
|
||||||
<td class="py-3 px-6 text-xs monospace"> <a href=/bid/{{ b[1] }}>{{ b[1]|truncate(20, True) }}</a> </td>
|
|
||||||
<td class="py-3 px-6 text-xs monospace"> <a href=/offer/{{ b[2] }}>{{ b[2]|truncate(20, True) }}</a> </td>
|
|
||||||
<td class="py-3 px-6 text-xs monospace"> <a href=/identity/{{ b[6] }}>{{ b[6] }}</a> </td>
|
|
||||||
<td class="py-3 px-6 text-xs">{{ b[3] }}</td>
|
|
||||||
<td class="py-3 px-6 text-xs">{{ b[4] }}</td>
|
|
||||||
<td class="py-3 px-6 text-xs">{{ b[5] }}</td>
|
|
||||||
{% if page_type_received or page_type_sent %}
|
|
||||||
<td class="py-3 px-6 text-xs"> <a class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200 bg-blue-500 text-white hover:bg-blue-600 transition duration-200" href="/bid/{{ b[1] }}">Details</a>
|
|
||||||
</td> {% elif page_type_available %}
|
|
||||||
<td class="py-3 px-6 text-xs"> <a class="inline-block w-20 py-1 px-2 font-medium text-center text-sm rounded-md bg-blue-500 text-white border border-blue-500 hover:bg-blue-600 transition duration-200 bg-blue-500 text-white hover:bg-blue-600 transition duration-200" href="/bid/{{ b[1] }}">Accept</a>
|
|
||||||
</td>
|
|
||||||
{% endif %}
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
{% endfor %}
|
|
||||||
</table> <input type="hidden" name="formid" value="{{ form_id }}"> <input type="hidden" name="pageno" value="{{ filters.page_no }}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="rounded-b-md">
|
|
||||||
<div class="w-full md:w-0/12">
|
|
||||||
<div class="flex flex-wrap justify-end pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
|
||||||
{% if filters.page_no > 1 %} <div class="w-full md:w-auto p-1.5">
|
|
||||||
<button type="submit" name='pageback' value="Previous" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
|
||||||
{{ page_back_svg | safe }}
|
|
||||||
<span>Previous</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
<div class="flex items-center">
|
|
||||||
<div class="w-full md:w-auto p-1.5">
|
|
||||||
<p class="text-sm font-heading dark:text-white">Page: {{ filters.page_no }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% if bids_count > 20 %}
|
|
||||||
<div class="w-full md:w-auto p-1.5"> <button type="submit" name='pageforwards' value="Next" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-blue-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
|
||||||
<span>Next</span>
|
|
||||||
{{ page_forwards_svg | safe }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{% include 'inc_messages.html' %}
|
||||||
|
|
||||||
|
<div class="xl:container mx-auto">
|
||||||
|
<section>
|
||||||
|
<div class="pl-6 pr-6 pt-0 mt-5 h-full overflow-hidden">
|
||||||
|
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||||
|
<div class="w-full pt-2">
|
||||||
|
<div class="mb-4 border-b pb-5 border-gray-200 dark:border-gray-500">
|
||||||
|
<ul class="flex flex-wrap text-sm font-medium text-center text-gray-500 dark:text-gray-400" id="myTab" data-tabs-toggle="#bidstab" role="tablist">
|
||||||
|
<li class="mr-2">
|
||||||
|
<button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="sent-tab" data-tabs-target="#sent" type="button" role="tab" aria-controls="sent" aria-selected="true">
|
||||||
|
Sent Bids <span class="text-gray-500 dark:text-gray-400">({{ sent_bids_count }})</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li class="mr-2">
|
||||||
|
<button class="inline-block px-4 py-3 rounded-lg hover:text-gray-900 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white focus:outline-none focus:ring-0" id="received-tab" data-tabs-target="#received" type="button" role="tab" aria-controls="received" aria-selected="false">
|
||||||
|
Received Bids <span class="text-gray-500 dark:text-gray-400">({{ received_bids_count }})</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="px-6 py-0 h-full overflow-hidden">
|
||||||
|
<div class="pb-6 mt-6 border-coolGray-100">
|
||||||
|
<div class="flex flex-wrap justify-center -m-1.5">
|
||||||
|
<div class="w-full md:w-auto p-1.5">
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text"
|
||||||
|
id="searchInput"
|
||||||
|
name="search" autocomplete="off" placeholder="Search bid ID, offer ID, address or label..."
|
||||||
|
class="w-full md:w-96 hover:border-blue-500 dark:hover:bg-gray-50 text-gray-900 pl-4 pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none block p-2.5 focus:ring-blue-500 focus:border-blue-500 focus:ring-0 dark:focus:bg-gray-500 dark:focus:text-white">
|
||||||
|
<div class="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none">
|
||||||
|
<svg class="w-5 h-5 text-gray-500 dark:text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-1.5 md:w-auto hover-container">
|
||||||
|
<div class="flex">
|
||||||
|
<button id="coin_from_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-l-lg flex items-center" disabled></button>
|
||||||
|
<div class="relative">
|
||||||
|
{{ input_arrow_down_svg | safe }}
|
||||||
|
<select name="coin_from" id="coin_from" class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-r-lg outline-none block w-full p-2.5 focus:ring-0 border-l-0">
|
||||||
|
<option value="any" {% if filters.coin_from==-1 %} selected{% endif %}>You Send</option>
|
||||||
|
{% for c in coins_from %}
|
||||||
|
<option class="text-sm" value="{{ c[0] }}" {% if filters.coin_from==c[0] %} selected{% endif %} data-image="/static/images/coins/{{ c[1]|replace(" ", "-") }}.png">{{ c[1] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="w-full md:w-auto p-1.5">
|
||||||
|
<p class="text-sm font-heading text-gray-500 dark:text-white">{{ arrow_right_svg | safe }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button id="coin_to_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-l-lg flex items-center" disabled></button>
|
||||||
|
<div class="relative">
|
||||||
|
{{ input_arrow_down_svg | safe }}
|
||||||
|
<select name="coin_to" id="coin_to" class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-r-lg outline-none block w-full p-2.5 focus:ring-0 border-l-0">
|
||||||
|
<option value="any" {% if filters.coin_to==-1 %} selected{% endif %}>You Receive</option>
|
||||||
|
{% for c in coins %}
|
||||||
|
<option class="text-sm" value="{{ c[0] }}" {% if filters.coin_to==c[0] %} selected{% endif %} data-image="/static/images/coins/{{ c[1]|replace(" ", "-") }}.png">{{ c[1] }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="w-full md:w-auto p-1.5">
|
||||||
|
<div class="relative">
|
||||||
|
{{ input_arrow_down_svg | safe }}
|
||||||
|
<select name="state" id="state" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||||
|
<option value="-1" selected="">Any State</option>
|
||||||
|
<optgroup label="Active States">
|
||||||
|
<option value="1">Sent</option>
|
||||||
|
<option value="2">Receiving</option>
|
||||||
|
<option value="3">Received</option>
|
||||||
|
<option value="4">Receiving accept</option>
|
||||||
|
<option value="5">Accepted</option>
|
||||||
|
<option value="6">Initiated</option>
|
||||||
|
<option value="7">Participating</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Completed States">
|
||||||
|
<option value="8">Completed</option>
|
||||||
|
<option value="15">Scriptless tx redeemed</option>
|
||||||
|
<option value="13">Script tx redeemed</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Failed States">
|
||||||
|
<option value="17">Failed, refunded</option>
|
||||||
|
<option value="18">Failed, swiped</option>
|
||||||
|
<option value="19">Failed</option>
|
||||||
|
<option value="22">Abandoned</option>
|
||||||
|
<option value="23">Error</option>
|
||||||
|
<option value="31">Expired</option>
|
||||||
|
</optgroup>
|
||||||
|
<optgroup label="Other States">
|
||||||
|
<option value="9">Script coin locked</option>
|
||||||
|
<option value="10">Script coin spend tx valid</option>
|
||||||
|
<option value="11">Scriptless coin locked</option>
|
||||||
|
<option value="12">Script coin lock released</option>
|
||||||
|
<option value="14">Script pre-refund tx in chain</option>
|
||||||
|
<option value="16">Scriptless tx recovered</option>
|
||||||
|
<option value="20">Delaying</option>
|
||||||
|
<option value="21">Timed-out</option>
|
||||||
|
<option value="24">Stalled (debug)</option>
|
||||||
|
<option value="25">Rejected</option>
|
||||||
|
<option value="26">Unknown bid state</option>
|
||||||
|
<option value="27">Exchanged script lock tx sigs msg</option>
|
||||||
|
<option value="28">Exchanged script lock spend tx msg</option>
|
||||||
|
<option value="29">Request sent</option>
|
||||||
|
<option value="30">Request accepted</option>
|
||||||
|
<option value="32">Auto accept delay</option>
|
||||||
|
<option value="33">Auto accept failed</option>
|
||||||
|
</optgroup>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- todo
|
||||||
|
<div class="w-full md:w-auto p-1.5">
|
||||||
|
<div class="relative">
|
||||||
|
{{ input_arrow_down_svg | safe }}
|
||||||
|
<select name="with_expired" id="with_expired" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0">
|
||||||
|
<option value="true">Include Expired</option>
|
||||||
|
<option value="false">Exclude Expired</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
||||||
|
|
||||||
|
<div class="w-full md:w-auto p-1.5">
|
||||||
|
<div class="relative">
|
||||||
|
<button type="button" id="clearFilters" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm hover:text-white dark:text-white dark:bg-gray-500 bg-coolGray-200 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-coolGray-200 dark:border-gray-400 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
|
<span>Clear Filters</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div id="bidstab">
|
||||||
|
<div class="rounded-lg lg:px-6" id="sent" role="tabpanel" aria-labelledby="sent-tab">
|
||||||
|
<div id="sent-content">
|
||||||
|
<div class="xl:container mx-auto lg:px-0">
|
||||||
|
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
|
<div class="px-0">
|
||||||
|
<div class="w-auto overflow-auto lg:overflow-hidden">
|
||||||
|
<table class="w-full lg:min-w-max">
|
||||||
|
<thead class="uppercase">
|
||||||
|
<tr class="text-left">
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 pl-16 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Date/Time</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0 hidden lg:block">
|
||||||
|
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Send</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Receive</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 text-center bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Status</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 pr-6 text-center rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-b-md">
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="flex flex-wrap justify-between items-center pl-6 pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex items-center mr-4">
|
||||||
|
<span id="status-dot-sent" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
|
||||||
|
<span id="status-text-sent" class="text-sm text-gray-500">Connecting...</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm font-heading dark:text-gray-400">
|
||||||
|
Sent Bids: <span id="sentBidsCount">0</span>
|
||||||
|
</p>
|
||||||
|
{% if debug_ui_mode == true %}
|
||||||
|
<button id="refreshSentBids" class="ml-4 inline-flex items-center px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||||||
|
</svg>
|
||||||
|
<span id="refreshSentText">Refresh</span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<button id="exportSentBids" class="ml-4 inline-flex items-center px-4 py-2.5 font-medium text-sm text-white bg-green-600 hover:bg-green-700 hover:border-green-700 rounded-lg transition duration-200 border border-green-600 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||||
|
</svg>
|
||||||
|
<span>Export CSV</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="pagination-controls-sent" class="flex items-center space-x-2" style="display: none;">
|
||||||
|
<button id="prevPageSent" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||||
|
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||||
|
</svg>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<p class="text-sm font-heading dark:text-white">Page <span id="currentPageSent">1</span></p>
|
||||||
|
<button id="nextPageSent" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||||
|
Next
|
||||||
|
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hidden rounded-lg lg:px-6" id="received" role="tabpanel" aria-labelledby="received-tab">
|
||||||
|
<div id="received-content">
|
||||||
|
<div class="xl:container mx-auto lg:px-0">
|
||||||
|
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
|
<div class="px-0">
|
||||||
|
<div class="w-auto overflow-auto lg:overflow-hidden">
|
||||||
|
<table class="w-full lg:min-w-max">
|
||||||
|
<thead class="uppercase">
|
||||||
|
<tr class="text-left">
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 pl-16 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Date/Time</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Send</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Receive</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 text-center bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Status</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="p-3 pr-6 text-center rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-b-md">
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="flex flex-wrap justify-between items-center pl-6 pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex items-center mr-4">
|
||||||
|
<span id="status-dot-received" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
|
||||||
|
<span id="status-text-received" class="text-sm text-gray-500">Connecting...</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm font-heading dark:text-gray-400">
|
||||||
|
Received Bids: <span id="receivedBidsCount">0</span>
|
||||||
|
</p>
|
||||||
|
{% if debug_ui_mode == true %}
|
||||||
|
<button id="refreshReceivedBids" class="ml-4 inline-flex items-center px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||||||
|
</svg>
|
||||||
|
<span id="refreshReceivedText">Refresh</span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<button id="exportReceivedBids" class="ml-4 inline-flex items-center px-4 py-2.5 font-medium text-sm text-white bg-green-600 hover:bg-green-700 hover:border-green-700 rounded-lg transition duration-200 border border-green-600 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
|
||||||
|
</svg>
|
||||||
|
<span>Export CSV</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="pagination-controls-received" class="flex items-center space-x-2" style="display: none;">
|
||||||
|
<button id="prevPageReceived" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||||
|
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||||
|
</svg>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<p class="text-sm font-heading dark:text-white">Page <span id="currentPageReceived">1</span></p>
|
||||||
|
<button id="nextPageReceived" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||||
|
Next
|
||||||
|
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/static/js/bids_sentreceived.js"></script>
|
||||||
|
<script src="/static/js/bids_export.js"></script>
|
||||||
|
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
118
basicswap/templates/bids_available.html
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
{% include 'header.html' %}
|
||||||
|
{% from 'style.html' import breadcrumb_line_svg, page_back_svg, page_forwards_svg, filter_clear_svg, filter_apply_svg, input_arrow_down_svg %}
|
||||||
|
|
||||||
|
<section class="py-3 px-4 mt-6">
|
||||||
|
<div class="lg:container mx-auto">
|
||||||
|
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden">
|
||||||
|
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||||
|
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||||
|
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="">
|
||||||
|
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||||
|
<div class="w-full md:w-1/2 p-3">
|
||||||
|
<h2 class="mb-3 text-2xl font-bold text-white tracking-tighter">Bid Requests</h2>
|
||||||
|
<p class="font-normal text-coolGray-200 dark:text-white">Review and accept bids from other users.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
{% include 'inc_messages.html' %}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="mt-5 lg:container mx-auto lg:px-0 px-6">
|
||||||
|
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
|
<div class="px-0">
|
||||||
|
<div class="w-auto mt-6 overflow-auto lg:overflow-hidden">
|
||||||
|
<table class="w-full min-w-max">
|
||||||
|
<thead class="uppercase">
|
||||||
|
<tr>
|
||||||
|
<th class="p-0" data-sortable="true" data-column-index="0">
|
||||||
|
<div class="py-3 pl-4 justify-center rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold"></span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 pl-4 justify-center bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold">Time</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0 hidden xl:block">
|
||||||
|
<div class="py-3 px-4 text-left bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-left">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Send</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Swap</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">You Get</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Rate</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0">
|
||||||
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 rounded-tr-xl">
|
||||||
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Actions</span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="bids-body"></tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="rounded-b-md">
|
||||||
|
<div class="w-full">
|
||||||
|
<div class="flex flex-wrap justify-between items-center pl-6 pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex items-center mr-4">
|
||||||
|
<span id="status-dot" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
|
||||||
|
<span id="status-text" class="text-sm text-gray-500">Connecting...</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm font-heading dark:text-gray-400 mr-4">Available Bids: <span id="availableBidsCount">0</span></p>
|
||||||
|
{% if debug_ui_mode == true %}
|
||||||
|
<button id="refreshBids" class="inline-flex items-center px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path>
|
||||||
|
</svg>
|
||||||
|
<span id="refreshText">Refresh</span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
<div id="pagination-controls" class="flex items-center space-x-2" style="display: none;">
|
||||||
|
<button id="prevPage" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200">
|
||||||
|
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
||||||
|
</svg>
|
||||||
|
Previous
|
||||||
|
</button>
|
||||||
|
<p class="text-sm font-heading dark:text-white">Page <span id="currentPage">1</span></p>
|
||||||
|
<button id="nextPage" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200">
|
||||||
|
Next
|
||||||
|
<svg class="w-4 h-4 ml-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<script src="/static/js/bids_available.js"></script>
|
||||||
|
|
||||||
|
{% include 'footer.html' %}
|
||||||
@@ -23,9 +23,9 @@
|
|||||||
<div class="w-full md:w-1/2 mb-6 md:mb-0">
|
<div class="w-full md:w-1/2 mb-6 md:mb-0">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<p class="text-sm text-gray-90 dark:text-white font-medium">© 2024~ (BSX) BasicSwap</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
<p class="text-sm text-gray-90 dark:text-white font-medium">© 2025~ (BSX) BasicSwap</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||||
<p class="text-sm text-coolGray-400 font-medium">BSX: v{{ version }}</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
<p class="text-sm text-coolGray-400 font-medium">BSX: v{{ version }}</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||||
<p class="text-sm text-coolGray-400 font-medium">GUI: v3.1.1</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
<p class="text-sm text-coolGray-400 font-medium">GUI: v3.2.0</p> <span class="w-1 h-1 mx-1.5 bg-gray-500 dark:bg-white rounded-full"></span>
|
||||||
<p class="mr-2 text-sm font-bold dark:text-white text-gray-90 ">Made with </p>
|
<p class="mr-2 text-sm font-bold dark:text-white text-gray-90 ">Made with </p>
|
||||||
{{ love_svg | safe }}
|
{{ love_svg | safe }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,73 +1,4 @@
|
|||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
{% from 'style.html' import index_wallet_svg, index_trading_svg, index_support_svg %}
|
|
||||||
<div class="container mx-auto">
|
|
||||||
<section class="relative py-24 overflow-hidden">
|
|
||||||
<div class="container px-4 mx-auto mb-16 md:mb-0">
|
|
||||||
<div class="md:w-1/2 pl-4">
|
|
||||||
<span class="inline-block py-1 px-3 mb-4 text-xs leading-5 bg-blue-500 text-white font-medium rounded-full shadow-sm">(BSX) BasicSwap v{{ version }} - (GUI) v.3.1.1</span>
|
|
||||||
<h3 class="mb-6 text-4xl md:text-5xl leading-tight text-coolGray-900 font-bold tracking-tighter dark:text-white">Welcome to BasicSwap DEX</h3>
|
|
||||||
<p class="mb-12 text-lg md:text-xl text-coolGray-500 dark:text-gray-300 font-medium">The World's Most Secure and Decentralized DEX, Safely swap cryptocurrencies without central points of failure.
|
|
||||||
It’s free, completely trustless, and highly secure.</p>
|
|
||||||
<div class="flex flex-wrap mb-10 text-center md:text-left">
|
|
||||||
<div class="w-full md:w-auto mb-6 md:mb-0 md:pr-6">
|
|
||||||
<a href="/wallets">
|
|
||||||
<div class="inline-flex h-14 w-14 mx-auto items-center justify-center text-white bg-blue-500 rounded-lg">
|
|
||||||
{{ index_wallet_svg | safe }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:flex-1 md:pt-3">
|
|
||||||
<div class="md:max-w-sm">
|
|
||||||
<h3 class="mb-4 text-xl md:text-2xl leading-tight text-coolGray-900 dark:text-white font-bold">Your Wallet</h3>
|
|
||||||
<p class="text-coolGray-500 dark:text-gray-300 font-medium">Manage your cryptocurrency wallets.</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-wrap mb-10 text-center md:text-left">
|
|
||||||
<div class="w-full md:w-auto mb-6 md:mb-0 md:pr-6">
|
|
||||||
<a href="/offers">
|
|
||||||
<div class="inline-flex h-14 w-14 mx-auto items-center justify-center text-white bg-blue-500 rounded-lg">
|
|
||||||
{{ index_trading_svg | safe }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:flex-1 md:pt-3">
|
|
||||||
<div class="md:max-w-sm">
|
|
||||||
<h3 class="mb-4 text-xl md:text-2xl leading-tight text-coolGray-900 dark:text-white font-bold">Start Trading</h3>
|
|
||||||
<p class="text-coolGray-500 dark:text-gray-300 font-medium">Browse available swap offers placed by others.</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-wrap text-center md:text-left">
|
|
||||||
<div class="w-full md:w-auto mb-6 md:mb-0 md:pr-6">
|
|
||||||
<a href="https://academy.particl.io/en/latest/faq/get_support.html" target="_blank">
|
|
||||||
<div class="inline-flex h-14 w-14 mx-auto items-center justify-center text-white bg-blue-500 rounded-lg">
|
|
||||||
{{ index_support_svg | safe }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:flex-1 md:pt-3">
|
|
||||||
<div class="md:max-w-sm">
|
|
||||||
<h3 class="mb-4 text-xl md:text-2xl leading-tight text-coolGray-900 dark:text-white font-bold">Help / Tutorials</h3>
|
|
||||||
<p class="text-coolGray-500 dark:text-gray-300 font-medium">Learn how to use BasicSwap with the Particl Academy.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="md:absolute md:top-28 lg:top-1/2 md:-right-96 xl:-right-80 md:-mr-56 lg:-mr-20 xl:-mr-0 md:transform lg:-translate-y-1/2 px-4 mb-16 md:mb-0">
|
|
||||||
<div class="relative max-w-max">
|
|
||||||
<img class="absolute p-7 -mt-1 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-10/12 z-20 imageshow light-image" src="/static/images/gfx/dashboard.jpg" alt="">
|
|
||||||
<img class="absolute p-7 -mt-1 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-10/12 z-20 imageshow dark-image" src="/static/images/gfx/dashboard2.jpg" alt="">
|
|
||||||
<img class="relative z-10 imageshow light-image" src="/static/images/gfx/macbook.png" alt="">
|
|
||||||
<img class="relative z-10 imageshow dark-image" src="/static/images/gfx/macbook2.png" alt="">
|
|
||||||
<img class="absolute -top-24 right-0 md:mt-px md:right-96 md:mr-52 lg:mr-16 xl:-mr-20 w-28 md:w-auto text-blue-500" src="/static/images/elements/dots2-red.svg">
|
|
||||||
<img class="absolute -bottom-24 left-0 md:left-auto md:mt-px md:right-96 md:mr-52 lg:mr-16 xl:-mr-20 w-28 md:w-auto text-red-500" src="/static/images/elements/dots2-red.svg">
|
|
||||||
<img class="absolute left-0 top-1/2 transform -translate-y-1/2 w-28 md:w-auto text-yellow-400" src="/static/images/elements/circle2-violet.svg">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -137,7 +137,7 @@
|
|||||||
<td class="py-3 px-6 bold">{{ data.amt_to }} {{ data.tla_to }}</td>
|
<td class="py-3 px-6 bold">{{ data.amt_to }} {{ data.tla_to }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Minimum Bid Amount</td>
|
<td class="py-3 px-6 bold">Minimum Purchase</td>
|
||||||
<td class="py-3 px-6">{{ data.amt_bid_min }} {{ data.tla_from }}</td>
|
<td class="py-3 px-6">{{ data.amt_bid_min }} {{ data.tla_from }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
@@ -392,21 +392,11 @@
|
|||||||
</th>
|
</th>
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600">
|
||||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Settings</span>
|
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Value</span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600 h-20">
|
|
||||||
<td class="py-3 px-6">Amount you will get <span class="bold" id="bid_amt_from">{{ data.amt_from }}</span> {{ data.tla_from }}
|
|
||||||
{% if data.xmr_type == true %}
|
|
||||||
(excluding estimated {{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }} in tx fees).
|
|
||||||
{% else %}
|
|
||||||
(excluding a tx fee).
|
|
||||||
{% endif %}</td>
|
|
||||||
<td class="py-3 px-6">Amount you will send <span class="bold" id="bid_amt_to">{{ data.amt_to }}</span> {{ data.tla_to }}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Send From Address</td>
|
<td class="py-3 px-6 bold">Send From Address</td>
|
||||||
<td class="py-3 px-6">
|
<td class="py-3 px-6">
|
||||||
@@ -475,21 +465,135 @@ if (document.readyState === 'loading') {
|
|||||||
handleBidsPageAddress();
|
handleBidsPageAddress();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{% if data.amount_negotiable == true %}
|
{% if data.amount_negotiable == true %}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Amount</td>
|
<td class="px-6">
|
||||||
<td class="py-3 px-6">
|
<span class="inline-flex bold align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||||
<input type="text" class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="bid_amount" name="bid_amount" value="{{ data.bid_amount }}" onchange="updateBidParams('amount');">
|
<img class="h-7" src="/static/images/coins/{{ data.coin_to }}.png" alt="{{ data.coin_to }}">
|
||||||
</td>
|
</span>
|
||||||
</tr>
|
<span class="bold">Sending ({{ data.tla_to }})</span>
|
||||||
{% endif %}
|
</td>
|
||||||
{% if data.rate_negotiable == true %}
|
<td class="py-3 px-6">
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<div class="relative">
|
||||||
<td class="py-3 px-6 bold">Rate</td>
|
<input type="text"
|
||||||
<td class="py-3 px-6"> <input type="text" class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-100 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="bid_rate" name="bid_rate" value="{{ data.bid_rate }}" onchange="updateBidParams('rate');">
|
class="bg-gray-50 text-gray-900 appearance-none pr-28 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0"
|
||||||
</td>
|
id="bid_amount_send"
|
||||||
</tr>
|
autocomplete="off"
|
||||||
{% endif %}
|
name="bid_amount_send"
|
||||||
|
value=""
|
||||||
|
max="{{ data.amt_to }}"
|
||||||
|
onchange="validateMaxAmount(this, {{ data.amt_to }}); updateBidParams('sending');">
|
||||||
|
<div class="absolute inset-y-0 right-3 flex items-center pointer-events-none text-gray-400 dark:text-gray-300 text-sm">
|
||||||
|
max {{ data.amt_to }} ({{ data.tla_to }})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
|
<td class="px-6">
|
||||||
|
<span class="inline-flex bold align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||||
|
<img class="h-7" src="/static/images/coins/{{ data.coin_from }}.png" alt="{{ data.coin_from }}">
|
||||||
|
</span>
|
||||||
|
<span class="bold">Receiving ({{ data.tla_from }})</span>
|
||||||
|
</td>
|
||||||
|
<td class="py-3 px-6">
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text"
|
||||||
|
class="bg-gray-50 text-gray-900 appearance-none pr-28 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0"
|
||||||
|
id="bid_amount"
|
||||||
|
autocomplete="off"
|
||||||
|
name="bid_amount"
|
||||||
|
value=""
|
||||||
|
max="{{ data.amt_from }}"
|
||||||
|
onchange="validateMaxAmount(this, {{ data.amt_from }}); updateBidParams('receiving');">
|
||||||
|
<div class="absolute inset-y-0 right-3 flex items-center pointer-events-none text-gray-400 dark:text-gray-300 text-sm">
|
||||||
|
max {{ data.amt_from }} ({{ data.tla_from }})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if data.xmr_type == true %}
|
||||||
|
<div class="text-sm mt-1 text-gray-400 dark:text-gray-300">(excluding estimated <span class="bold">{{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }}</span> in tx fees)</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-sm mt-1 text-gray-400 dark:text-gray-300">(excluding a tx fee)</div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
|
<td class="px-6">
|
||||||
|
<span class="inline-flex bold align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||||
|
<img class="h-7" src="/static/images/coins/{{ data.coin_to }}.png" alt="{{ data.coin_to }}">
|
||||||
|
</span>
|
||||||
|
<span class="bold">Sending ({{ data.tla_to }})</span>
|
||||||
|
</td>
|
||||||
|
<td class="py-3 px-6">
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text"
|
||||||
|
class="bg-gray-50 text-gray-900 appearance-none pr-28 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 cursor-not-allowed"
|
||||||
|
id="bid_amount_send"
|
||||||
|
autocomplete="off"
|
||||||
|
name="bid_amount_send"
|
||||||
|
value="{{ data.amt_to }}"
|
||||||
|
max="{{ data.amt_to }}"
|
||||||
|
disabled>
|
||||||
|
<div class="absolute inset-y-0 right-3 flex items-center pointer-events-none text-gray-400 dark:text-gray-300 text-sm">
|
||||||
|
max {{ data.amt_to }} ({{ data.tla_to }})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm mt-1 text-gray-400 dark:text-gray-300">(Amount not variable)</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
|
<td class="px-6">
|
||||||
|
<span class="inline-flex bold align-middle items-center justify-center w-9 h-10 bg-white-50 rounded">
|
||||||
|
<img class="h-7" src="/static/images/coins/{{ data.coin_from }}.png" alt="{{ data.coin_from }}">
|
||||||
|
</span>
|
||||||
|
<span class="bold">Receiving ({{ data.tla_from }})</span>
|
||||||
|
</td>
|
||||||
|
<td class="py-3 px-6">
|
||||||
|
<div class="relative">
|
||||||
|
<input type="text"
|
||||||
|
class="bg-gray-50 text-gray-900 appearance-none pr-28 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 cursor-not-allowed"
|
||||||
|
id="bid_amount"
|
||||||
|
autocomplete="off"
|
||||||
|
name="bid_amount"
|
||||||
|
value="{{ data.bid_amount }}"
|
||||||
|
max="{{ data.amt_from }}"
|
||||||
|
disabled>
|
||||||
|
<div class="absolute inset-y-0 right-3 flex items-center pointer-events-none text-gray-400 dark:text-gray-300 text-sm">
|
||||||
|
max {{ data.amt_from }} ({{ data.tla_from }})
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if data.xmr_type == true %}
|
||||||
|
<div class="text-sm mt-1 text-gray-400 dark:text-gray-300">(excluding estimated <span class="bold">{{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }}</span> in tx fees)</div>
|
||||||
|
{% else %}
|
||||||
|
<div class="text-sm mt-1 text-gray-400 dark:text-gray-300">(excluding a tx fee)</div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
|
<td class="py-3 px-6 bold">Rate </td>
|
||||||
|
<td class="py-3 px-6">
|
||||||
|
{% if data.rate_negotiable == true %}
|
||||||
|
<input type="text"
|
||||||
|
class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0"
|
||||||
|
id="bid_rate"
|
||||||
|
name="bid_rate"
|
||||||
|
value="{{ data.bid_rate }}"
|
||||||
|
placeholder="Current rate: {{ data.rate }}"
|
||||||
|
onchange="updateBidParams('rate')">
|
||||||
|
{% else %}
|
||||||
|
<input type="text"
|
||||||
|
class="bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0 cursor-not-allowed"
|
||||||
|
id="bid_rate"
|
||||||
|
name="bid_rate"
|
||||||
|
value="{{ data.rate }}"
|
||||||
|
title="Rate is not negotiable"
|
||||||
|
disabled>
|
||||||
|
<div class="text-sm mt-1 text-gray-400 dark:text-gray-300">(Rate is not negotiable)</div>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Minutes valid</td>
|
<td class="py-3 px-6 bold">Minutes valid</td>
|
||||||
<td class="py-3 px-6">
|
<td class="py-3 px-6">
|
||||||
@@ -519,15 +623,24 @@ if (document.readyState === 'loading') {
|
|||||||
</div>
|
</div>
|
||||||
<div class="rounded-b-md">
|
<div class="rounded-b-md">
|
||||||
<div class="w-full md:w-0/12">
|
<div class="w-full md:w-0/12">
|
||||||
<div class="flex flex-wrap justify-end pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
<div class="flex flex-wrap justify-end pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
<div class="w-full md:w-auto p-1.5">
|
||||||
|
<button type="button" onclick="resetForm()" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none dark:text-white dark:hover:text-white dark:bg-gray-600 dark:hover:bg-gray-700 dark:border-gray-600 dark:hover:border-gray-600">
|
||||||
|
Clear Form
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||||
<input type="hidden" name="confirm" value="true">
|
<input type="hidden" name="confirm" value="true">
|
||||||
<button name="sendbid" value="Send Bid" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Send Bid</button>
|
<button name="sendbid" value="Send Bid" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
</div>
|
Send Bid
|
||||||
<div class="w-full md:w-auto p-1.5">
|
</button>
|
||||||
<button name="cancel" value="Cancel" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white hover:text-red border border-red-500 hover:border-red-500 hover:bg-red-600 bg-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none">Cancel</button>
|
</div>
|
||||||
</div>
|
<div class="w-full md:w-auto p-1.5">
|
||||||
</div>
|
<button name="cancel" value="Cancel" type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white hover:text-red border border-red-500 hover:border-red-500 hover:bg-red-600 bg-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO:
|
<!-- TODO:
|
||||||
<div class="w-full md:w-auto p-1.5 ml-2"><button name="check_rates" value="Lookup Rates" type="button" onclick='lookup_rates();' class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none"><svg class="mr-2"
|
<div class="w-full md:w-auto p-1.5 ml-2"><button name="check_rates" value="Lookup Rates" type="button" onclick='lookup_rates();' class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-coolGray-500 hover:text-coolGray-600 border border-coolGray-200 hover:border-coolGray-300 bg-white rounded-md shadow-button focus:ring-0 focus:outline-none"><svg class="mr-2"
|
||||||
@@ -538,15 +651,12 @@ if (document.readyState === 'loading') {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div id="confirmModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
<div id="confirmModal" class="fixed inset-0 z-50 hidden overflow-y-auto">
|
||||||
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-out"></div>
|
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-out"></div>
|
||||||
|
|
||||||
<div class="relative z-50 min-h-screen px-4 flex items-center justify-center">
|
<div class="relative z-50 min-h-screen px-4 flex items-center justify-center">
|
||||||
<div class="bg-white dark:bg-gray-500 rounded-lg max-w-2xl w-full p-6 shadow-lg transition-opacity duration-300 ease-out">
|
<div class="bg-white dark:bg-gray-500 rounded-lg max-w-2xl w-full p-6 shadow-lg transition-opacity duration-300 ease-out">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">Confirm Bid</h2>
|
<h2 class="text-xl font-semibold text-gray-900 dark:text-white mb-6">Confirm Bid</h2>
|
||||||
|
|
||||||
<div class="space-y-4 text-left mb-8">
|
<div class="space-y-4 text-left mb-8">
|
||||||
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-4">
|
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-4">
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Amount you will get:</div>
|
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Amount you will get:</div>
|
||||||
@@ -556,7 +666,6 @@ if (document.readyState === 'loading') {
|
|||||||
<div class="text-sm text-gray-500 dark:text-gray-400 mt-1" id="modal-fee-info"></div>
|
<div class="text-sm text-gray-500 dark:text-gray-400 mt-1" id="modal-fee-info"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-4">
|
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-4">
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Amount you will send:</div>
|
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Amount you will send:</div>
|
||||||
<div class="font-medium text-gray-900 dark:text-white text-lg">
|
<div class="font-medium text-gray-900 dark:text-white text-lg">
|
||||||
@@ -564,20 +673,17 @@ if (document.readyState === 'loading') {
|
|||||||
<span id="modal-send-currency"></span>
|
<span id="modal-send-currency"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-4">
|
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-4">
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Send From Address:</div>
|
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Send From Address:</div>
|
||||||
<div class="font-mono text-sm p-2 bg-white dark:bg-gray-500 rounded border border-gray-300 dark:border-gray-400 overflow-x-auto text-gray-900 dark:text-white">
|
<div class="font-mono text-sm p-2 bg-white dark:bg-gray-500 rounded border border-gray-300 dark:border-gray-400 overflow-x-auto text-gray-900 dark:text-white">
|
||||||
<span id="modal-addr-from"></span>
|
<span id="modal-addr-from"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-4">
|
<div class="bg-gray-50 dark:bg-gray-600 rounded-lg p-4">
|
||||||
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Minutes valid:</div>
|
<div class="text-sm text-gray-600 dark:text-gray-400 mb-1">Minutes valid:</div>
|
||||||
<div class="font-medium text-gray-900 dark:text-white text-lg" id="modal-valid-mins"></div>
|
<div class="font-medium text-gray-900 dark:text-white text-lg" id="modal-valid-mins"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-center gap-4">
|
<div class="flex justify-center gap-4">
|
||||||
<button type="submit" name="sendbid" value="confirm"
|
<button type="submit" name="sendbid" value="confirm"
|
||||||
class="px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
class="px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
@@ -592,115 +698,317 @@ if (document.readyState === 'loading') {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function updateBidParams(value_changed) {
|
const xhr_rates = new XMLHttpRequest();
|
||||||
const coin_from = document.getElementById('coin_from').value;
|
xhr_rates.onload = () => {
|
||||||
const coin_to = document.getElementById('coin_to').value;
|
if (xhr_rates.status == 200) {
|
||||||
const amt_var = document.getElementById('amt_var').value;
|
const obj = JSON.parse(xhr_rates.response);
|
||||||
const rate_var = document.getElementById('rate_var').value;
|
const inner_html = '<h4 class="bold>Rates</h4><pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
||||||
|
const ratesDisplay = document.getElementById('rates_display');
|
||||||
let amt_from = '';
|
if (ratesDisplay) {
|
||||||
let rate = '';
|
ratesDisplay.innerHTML = inner_html;
|
||||||
|
}
|
||||||
if (amt_var == 'True') {
|
}
|
||||||
amt_from = document.getElementById('bid_amount').value;
|
};
|
||||||
} else {
|
|
||||||
amt_from = document.getElementById('amount_from').value;
|
const xhr_bid_params = new XMLHttpRequest();
|
||||||
}
|
xhr_bid_params.onload = () => {
|
||||||
|
if (xhr_bid_params.status == 200) {
|
||||||
if (rate_var == 'True') {
|
const obj = JSON.parse(xhr_bid_params.response);
|
||||||
rate = document.getElementById('bid_rate').value;
|
const bidAmountSendInput = document.getElementById('bid_amount_send');
|
||||||
} else {
|
if (bidAmountSendInput) {
|
||||||
rate = document.getElementById('offer_rate').value;
|
bidAmountSendInput.value = obj['amount_to'];
|
||||||
}
|
}
|
||||||
|
updateModalValues();
|
||||||
if (value_changed == 'amount') {
|
}
|
||||||
document.getElementById('bid_amt_from').innerHTML = amt_from;
|
};
|
||||||
}
|
|
||||||
|
function lookup_rates() {
|
||||||
xhr_bid_params.open('POST', '/json/rate');
|
const coin_from = document.getElementById('coin_from')?.value;
|
||||||
xhr_bid_params.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
const coin_to = document.getElementById('coin_to')?.value;
|
||||||
xhr_bid_params.send('coin_from=' + coin_from + '&coin_to=' + coin_to + '&rate=' + rate + '&amt_from=' + amt_from);
|
|
||||||
|
if (!coin_from || !coin_to || coin_from === '-1' || coin_to === '-1') {
|
||||||
|
alert('Coins from and to must be set first.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ratesDisplay = document.getElementById('rates_display');
|
||||||
|
if (ratesDisplay) {
|
||||||
|
ratesDisplay.innerHTML = '<h4>Rates</h4><p>Updating...</p>';
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr_rates.open('POST', '/json/rates');
|
||||||
|
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
|
xhr_rates.send(`coin_from=${coin_from}&coin_to=${coin_to}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateModalValues() {
|
function resetForm() {
|
||||||
document.getElementById('modal-amt-receive').textContent = document.getElementById('bid_amt_from').textContent;
|
const bidAmountSendInput = document.getElementById('bid_amount_send');
|
||||||
document.getElementById('modal-amt-send').textContent = document.getElementById('bid_amt_to').textContent;
|
const bidAmountInput = document.getElementById('bid_amount');
|
||||||
document.getElementById('modal-receive-currency').textContent = '{{ data.tla_from }}';
|
const bidRateInput = document.getElementById('bid_rate');
|
||||||
document.getElementById('modal-send-currency').textContent = '{{ data.tla_to }}';
|
const validMinsInput = document.querySelector('input[name="validmins"]');
|
||||||
|
const amtVar = document.getElementById('amt_var')?.value === 'True';
|
||||||
{% if data.xmr_type == true %}
|
if (bidAmountSendInput) {
|
||||||
document.getElementById('modal-fee-info').textContent = `(excluding estimated {{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }} in tx fees)`;
|
bidAmountSendInput.value = amtVar ? '' : bidAmountSendInput.getAttribute('max');
|
||||||
{% else %}
|
}
|
||||||
document.getElementById('modal-fee-info').textContent = '(excluding a tx fee)';
|
if (bidAmountInput) {
|
||||||
{% endif %}
|
bidAmountInput.value = amtVar ? '' : bidAmountInput.getAttribute('max');
|
||||||
|
}
|
||||||
const addrSelect = document.querySelector('select[name="addr_from"]');
|
if (bidRateInput && !bidRateInput.disabled) {
|
||||||
const selectedText = addrSelect.options[addrSelect.selectedIndex].text;
|
const defaultRate = document.getElementById('offer_rate')?.value || '';
|
||||||
const addrText = selectedText === 'New Address' ? selectedText : selectedText.split(' ')[0];
|
bidRateInput.value = defaultRate;
|
||||||
document.getElementById('modal-addr-from').textContent = addrText;
|
}
|
||||||
|
if (validMinsInput) {
|
||||||
document.getElementById('modal-valid-mins').textContent = document.querySelector('input[name="validmins"]').value;
|
validMinsInput.value = "60";
|
||||||
|
}
|
||||||
|
if (!amtVar) {
|
||||||
|
updateBidParams('rate');
|
||||||
|
}
|
||||||
|
updateModalValues();
|
||||||
|
const errorMessages = document.querySelectorAll('.error-message');
|
||||||
|
errorMessages.forEach(msg => msg.remove());
|
||||||
|
|
||||||
|
const inputs = document.querySelectorAll('input');
|
||||||
|
inputs.forEach(input => {
|
||||||
|
input.classList.remove('border-red-500', 'focus:border-red-500');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateBidParams(value_changed) {
|
||||||
|
const coin_from = document.getElementById('coin_from')?.value;
|
||||||
|
const coin_to = document.getElementById('coin_to')?.value;
|
||||||
|
const amt_var = document.getElementById('amt_var')?.value;
|
||||||
|
const rate_var = document.getElementById('rate_var')?.value;
|
||||||
|
const bidAmountInput = document.getElementById('bid_amount');
|
||||||
|
const bidAmountSendInput = document.getElementById('bid_amount_send');
|
||||||
|
const amountFromInput = document.getElementById('amount_from');
|
||||||
|
const bidRateInput = document.getElementById('bid_rate');
|
||||||
|
const offerRateInput = document.getElementById('offer_rate');
|
||||||
|
|
||||||
|
if (!coin_from || !coin_to || !amt_var || !rate_var) return;
|
||||||
|
|
||||||
|
const rate = rate_var === 'True' && bidRateInput ?
|
||||||
|
parseFloat(bidRateInput.value) || 0 :
|
||||||
|
parseFloat(offerRateInput?.value || '0');
|
||||||
|
|
||||||
|
if (!rate) return;
|
||||||
|
|
||||||
|
if (value_changed === 'rate') {
|
||||||
|
if (bidAmountSendInput && bidAmountInput) {
|
||||||
|
const sendAmount = parseFloat(bidAmountSendInput.value) || 0;
|
||||||
|
const receiveAmount = (sendAmount / rate).toFixed(8);
|
||||||
|
bidAmountInput.value = receiveAmount;
|
||||||
|
}
|
||||||
|
} else if (value_changed === 'sending') {
|
||||||
|
if (bidAmountSendInput && bidAmountInput) {
|
||||||
|
const sendAmount = parseFloat(bidAmountSendInput.value) || 0;
|
||||||
|
const receiveAmount = (sendAmount / rate).toFixed(8);
|
||||||
|
bidAmountInput.value = receiveAmount;
|
||||||
|
}
|
||||||
|
} else if (value_changed === 'receiving') {
|
||||||
|
if (bidAmountInput && bidAmountSendInput) {
|
||||||
|
const receiveAmount = parseFloat(bidAmountInput.value) || 0;
|
||||||
|
const sendAmount = (receiveAmount * rate).toFixed(8);
|
||||||
|
bidAmountSendInput.value = sendAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validateAmountsAfterChange();
|
||||||
|
|
||||||
|
xhr_bid_params.open('POST', '/json/rate');
|
||||||
|
xhr_bid_params.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
|
xhr_bid_params.send(`coin_from=${coin_from}&coin_to=${coin_to}&rate=${rate}&amt_from=${bidAmountInput?.value || '0'}`);
|
||||||
|
|
||||||
|
updateModalValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateAmountsAfterChange() {
|
||||||
|
const bidAmountSendInput = document.getElementById('bid_amount_send');
|
||||||
|
const bidAmountInput = document.getElementById('bid_amount');
|
||||||
|
|
||||||
|
if (bidAmountSendInput) {
|
||||||
|
const maxSend = parseFloat(bidAmountSendInput.getAttribute('max'));
|
||||||
|
validateMaxAmount(bidAmountSendInput, maxSend);
|
||||||
|
}
|
||||||
|
if (bidAmountInput) {
|
||||||
|
const maxReceive = parseFloat(bidAmountInput.getAttribute('max'));
|
||||||
|
validateMaxAmount(bidAmountInput, maxReceive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateMaxAmount(input, maxAmount) {
|
||||||
|
if (!input) return;
|
||||||
|
const value = parseFloat(input.value) || 0;
|
||||||
|
if (value > maxAmount) {
|
||||||
|
input.value = maxAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function showConfirmModal() {
|
function showConfirmModal() {
|
||||||
updateModalValues();
|
updateModalValues();
|
||||||
document.getElementById('confirmModal').classList.remove('hidden');
|
const modal = document.getElementById('confirmModal');
|
||||||
return false;
|
if (modal) {
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function hideConfirmModal() {
|
function hideConfirmModal() {
|
||||||
document.getElementById('confirmModal').classList.add('hidden');
|
const modal = document.getElementById('confirmModal');
|
||||||
return false;
|
if (modal) {
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateModalValues() {
|
||||||
|
const bidAmountInput = document.getElementById('bid_amount');
|
||||||
|
const bidAmountSendInput = document.getElementById('bid_amount_send');
|
||||||
|
|
||||||
|
if (bidAmountInput) {
|
||||||
|
const modalAmtReceive = document.getElementById('modal-amt-receive');
|
||||||
|
if (modalAmtReceive) {
|
||||||
|
modalAmtReceive.textContent = bidAmountInput.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalReceiveCurrency = document.getElementById('modal-receive-currency');
|
||||||
|
if (modalReceiveCurrency) {
|
||||||
|
modalReceiveCurrency.textContent = ' {{ data.tla_from }}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bidAmountSendInput) {
|
||||||
|
const modalAmtSend = document.getElementById('modal-amt-send');
|
||||||
|
if (modalAmtSend) {
|
||||||
|
modalAmtSend.textContent = bidAmountSendInput.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalSendCurrency = document.getElementById('modal-send-currency');
|
||||||
|
if (modalSendCurrency) {
|
||||||
|
modalSendCurrency.textContent = ' {{ data.tla_to }}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalFeeInfo = document.getElementById('modal-fee-info');
|
||||||
|
if (modalFeeInfo) {
|
||||||
|
{% if data.xmr_type == true %}
|
||||||
|
modalFeeInfo.textContent = `(excluding estimated {{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }} in tx fees)`;
|
||||||
|
{% else %}
|
||||||
|
modalFeeInfo.textContent = '(excluding a tx fee)';
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
|
||||||
|
const addrSelect = document.querySelector('select[name="addr_from"]');
|
||||||
|
if (addrSelect) {
|
||||||
|
const modalAddrFrom = document.getElementById('modal-addr-from');
|
||||||
|
if (modalAddrFrom) {
|
||||||
|
const selectedOption = addrSelect.options[addrSelect.selectedIndex];
|
||||||
|
const addrText = selectedOption.value === '-1' ? 'New Address' : selectedOption.text.split(' ')[0];
|
||||||
|
modalAddrFrom.textContent = addrText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const validMinsInput = document.querySelector('input[name="validmins"]');
|
||||||
|
if (validMinsInput) {
|
||||||
|
const modalValidMins = document.getElementById('modal-valid-mins');
|
||||||
|
if (modalValidMins) {
|
||||||
|
modalValidMins.textContent = validMinsInput.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleBidsPageAddress() {
|
||||||
|
const selectElement = document.querySelector('select[name="addr_from"]');
|
||||||
|
const STORAGE_KEY = 'lastUsedAddressBids';
|
||||||
|
|
||||||
|
if (!selectElement) return;
|
||||||
|
|
||||||
|
function loadInitialAddress() {
|
||||||
|
try {
|
||||||
|
const savedAddressJSON = localStorage.getItem(STORAGE_KEY);
|
||||||
|
if (savedAddressJSON) {
|
||||||
|
const savedAddress = JSON.parse(savedAddressJSON);
|
||||||
|
if (savedAddress && savedAddress.value) {
|
||||||
|
selectElement.value = savedAddress.value;
|
||||||
|
} else {
|
||||||
|
selectFirstAddress();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectFirstAddress();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error loading saved address:', e);
|
||||||
|
selectFirstAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectFirstAddress() {
|
||||||
|
if (selectElement.options.length > 1) {
|
||||||
|
const firstOption = selectElement.options[1];
|
||||||
|
if (firstOption) {
|
||||||
|
selectElement.value = firstOption.value;
|
||||||
|
saveAddress(firstOption.value, firstOption.text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectElement.addEventListener('change', (event) => {
|
||||||
|
if (event.target.selectedOptions[0]) {
|
||||||
|
saveAddress(event.target.value, event.target.selectedOptions[0].text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
loadInitialAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAddress(value, text) {
|
||||||
|
try {
|
||||||
|
const addressData = { value, text };
|
||||||
|
localStorage.setItem('lastUsedAddressBids', JSON.stringify(addressData));
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error saving address:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmPopup() {
|
||||||
|
return confirm("Are you sure?");
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCancelClick(event) {
|
function handleCancelClick(event) {
|
||||||
event.preventDefault();
|
if (event) event.preventDefault();
|
||||||
window.location.href = `/offer/${window.location.pathname.split('/')[2]}`;
|
const pathParts = window.location.pathname.split('/');
|
||||||
|
const offerId = pathParts[pathParts.indexOf('offer') + 1];
|
||||||
|
window.location.href = `/offer/${offerId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const sendBidBtn = document.querySelector('button[name="sendbid"][value="Send Bid"]');
|
|
||||||
if (sendBidBtn) {
|
|
||||||
sendBidBtn.onclick = showConfirmModal;
|
|
||||||
}
|
|
||||||
|
|
||||||
const modalCancelBtn = document.querySelector('#confirmModal .flex button:last-child');
|
const sendBidBtn = document.querySelector('button[name="sendbid"][value="Send Bid"]');
|
||||||
if (modalCancelBtn) {
|
if (sendBidBtn) {
|
||||||
modalCancelBtn.onclick = hideConfirmModal;
|
sendBidBtn.onclick = showConfirmModal;
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainCancelBtn = document.querySelector('button[name="cancel"]');
|
const modalCancelBtn = document.querySelector('#confirmModal .flex button:last-child');
|
||||||
if (mainCancelBtn) {
|
if (modalCancelBtn) {
|
||||||
mainCancelBtn.onclick = handleCancelClick;
|
modalCancelBtn.onclick = hideConfirmModal;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input change listeners
|
|
||||||
const validMinsInput = document.querySelector('input[name="validmins"]');
|
|
||||||
if (validMinsInput) {
|
|
||||||
validMinsInput.addEventListener('input', updateModalValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
const addrFromSelect = document.querySelector('select[name="addr_from"]');
|
|
||||||
if (addrFromSelect) {
|
|
||||||
addrFromSelect.addEventListener('change', updateModalValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bidAmountInput = document.getElementById('bid_amount');
|
const mainCancelBtn = document.querySelector('button[name="cancel"]');
|
||||||
if (bidAmountInput) {
|
if (mainCancelBtn) {
|
||||||
bidAmountInput.addEventListener('change', () => {
|
mainCancelBtn.onclick = handleCancelClick;
|
||||||
updateBidParams('amount');
|
}
|
||||||
updateModalValues();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const bidRateInput = document.getElementById('bid_rate');
|
const validMinsInput = document.querySelector('input[name="validmins"]');
|
||||||
if (bidRateInput) {
|
if (validMinsInput) {
|
||||||
bidRateInput.addEventListener('change', () => {
|
validMinsInput.addEventListener('input', updateModalValues);
|
||||||
updateBidParams('rate');
|
}
|
||||||
updateModalValues();
|
|
||||||
});
|
const addrFromSelect = document.querySelector('select[name="addr_from"]');
|
||||||
}
|
if (addrFromSelect) {
|
||||||
|
addrFromSelect.addEventListener('change', updateModalValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleBidsPageAddress();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -768,67 +1076,5 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
<script>
|
|
||||||
const xhr_rates = new XMLHttpRequest();
|
|
||||||
xhr_rates.onload = () => {
|
|
||||||
if (xhr_rates.status == 200) {
|
|
||||||
const obj = JSON.parse(xhr_rates.response);
|
|
||||||
inner_html = '<h4 class="bold>Rates</h4><pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
|
||||||
document.getElementById('rates_display').innerHTML = inner_html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function lookup_rates() {
|
|
||||||
const coin_from = document.getElementById('coin_from').value;
|
|
||||||
const coin_to = document.getElementById('coin_to').value;
|
|
||||||
if (coin_from == '-1' || coin_to == '-1') {
|
|
||||||
alert('Coins from and to must be set first.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
inner_html = '<h4>Rates</h4><p>Updating...</p>';
|
|
||||||
document.getElementById('rates_display').innerHTML = inner_html;
|
|
||||||
xhr_rates.open('POST', '/json/rates');
|
|
||||||
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
|
||||||
xhr_rates.send('coin_from=' + coin_from + '&coin_to=' + coin_to);
|
|
||||||
}
|
|
||||||
const xhr_bid_params = new XMLHttpRequest();
|
|
||||||
xhr_bid_params.onload = () => {
|
|
||||||
if (xhr_bid_params.status == 200) {
|
|
||||||
const obj = JSON.parse(xhr_bid_params.response);
|
|
||||||
document.getElementById('bid_amt_to').innerHTML = obj['amount_to'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateBidParams(value_changed) {
|
|
||||||
const coin_from = document.getElementById('coin_from').value;
|
|
||||||
const coin_to = document.getElementById('coin_to').value;
|
|
||||||
const amt_var = document.getElementById('amt_var').value;
|
|
||||||
const rate_var = document.getElementById('rate_var').value;
|
|
||||||
let amt_from = '';
|
|
||||||
let rate = '';
|
|
||||||
if (amt_var == 'True') {
|
|
||||||
amt_from = document.getElementById('bid_amount').value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
amt_from = document.getElementById('amount_from').value;
|
|
||||||
}
|
|
||||||
if (rate_var == 'True') {
|
|
||||||
rate = document.getElementById('bid_rate').value;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
rate = document.getElementById('offer_rate').value;
|
|
||||||
}
|
|
||||||
if (value_changed == 'amount') {
|
|
||||||
document.getElementById('bid_amt_from').innerHTML = amt_from;
|
|
||||||
}
|
|
||||||
xhr_bid_params.open('POST', '/json/rate');
|
|
||||||
xhr_bid_params.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
|
||||||
xhr_bid_params.send('coin_from=' + coin_from + '&coin_to=' + coin_to + '&rate=' + rate + '&amt_from=' + amt_from);
|
|
||||||
}
|
|
||||||
|
|
||||||
function confirmPopup() {
|
|
||||||
return confirm("Are you sure?");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -168,6 +168,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if data.swap_style == 'xmr' %}
|
{% if data.swap_style == 'xmr' %}
|
||||||
|
{% if data.coin_from | int in (6, 9) %} {# Not XMR or WOW #}
|
||||||
|
{% else %}
|
||||||
<div class="w-full md:w-1/2 p-3">
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee From Confirm Target</p>
|
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee From Confirm Target</p>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@@ -202,6 +204,7 @@
|
|||||||
</div> <span class="text-sm mt-2 block dark:text-white"> <b>Lock Tx Spend Fee:</b> {{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }} </span>
|
</div> <span class="text-sm mt-2 block dark:text-white"> <b>Lock Tx Spend Fee:</b> {{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }} </span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -238,7 +241,9 @@
|
|||||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"> </div> <input class="cursor-not-allowed disabled-input hover:border-blue-500 pr-10 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-white text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-5 focus:ring-0" type="text" id="amt_to" name="amt_to" value="{{ data.amt_to }}" readonly>
|
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"> </div> <input class="cursor-not-allowed disabled-input hover:border-blue-500 pr-10 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-white text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-5 focus:ring-0" type="text" id="amt_to" name="amt_to" value="{{ data.amt_to }}" readonly>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if data.swap_style == 'xmr' and coin_to != '6' %}
|
{% if data.swap_style == 'xmr' %}
|
||||||
|
{% if data.coin_to | int in (6, 9) %} {# Not XMR or WOW #}
|
||||||
|
{% else %}
|
||||||
<div class="w-full md:w-1/2 p-3">
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee To Confirm Target</p>
|
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee To Confirm Target</p>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@@ -273,6 +278,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -287,7 +293,7 @@
|
|||||||
<div class="w-full md:flex-1 p-3">
|
<div class="w-full md:flex-1 p-3">
|
||||||
<div class="flex flex-wrap -m-3">
|
<div class="flex flex-wrap -m-3">
|
||||||
<div class="w-full md:w-1/2 p-3">
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Minimum Bid Amount</p>
|
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Minimum Purchase</p>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||||
{{ select_bid_amount_svg | safe }}
|
{{ select_bid_amount_svg | safe }}
|
||||||
@@ -467,4 +473,4 @@
|
|||||||
</div>
|
</div>
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
<select class="pl-10 hover:border-blue-500 pl-10 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-white text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" name="addr_to">
|
<select class="pl-10 hover:border-blue-500 pl-10 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-white text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" name="addr_to">
|
||||||
<option{% if data.addr_to=="-1" %} selected{% endif %} value="-1">Public Network</option>
|
<option{% if data.addr_to=="-1" %} selected{% endif %} value="-1">Public Network</option>
|
||||||
{% for a in addrs_to %}
|
{% for a in addrs_to %}
|
||||||
<option{% if data.addr_to==a[0] %} selected{% endif %} value="{{ a[0] }}">{{ a[0] }} {{ a[1] }}</option>
|
<option{% if data.addr_to==a[0] %} selected{% endif %} value="{{ a[0] }}">{{ a[0] }} ({{ a[1] }})</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -274,7 +274,7 @@ if (document.readyState === 'loading') {
|
|||||||
<div class="w-full md:flex-1 p-3">
|
<div class="w-full md:flex-1 p-3">
|
||||||
<div class="flex flex-wrap -m-3">
|
<div class="flex flex-wrap -m-3">
|
||||||
<div class="w-full md:w-1/2 p-3">
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<p class="mb-1.5 font-medium text-base dark:text-white text-coolGray-800">Minimum Bid Amount</p>
|
<p class="mb-1.5 font-medium text-base dark:text-white text-coolGray-800">Minimum Purchase</p>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||||
{{ select_bid_amount_svg | safe }}
|
{{ select_bid_amount_svg | safe }}
|
||||||
@@ -283,15 +283,15 @@ if (document.readyState === 'loading') {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-1/2 p-3">
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<p class="mb-1.5 font-medium text-base dark:text-white text-coolGray-800">Rate</p>
|
<p class="mb-1.5 font-medium text-base dark:text-white text-coolGray-800">Rate</p>
|
||||||
<div class="relative">
|
<div class="flex items-center gap-2">
|
||||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
<div class="relative flex-1">
|
||||||
{{ select_rate_svg | safe }}
|
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||||
</div>
|
{{ select_rate_svg | safe }}
|
||||||
<input class="pl-10 hover:border-blue-500 pl-10 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-white text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" id="rate" name="rate" value="{{ data.rate }}" onchange="set_rate('rate');">
|
</div>
|
||||||
</div>
|
<input class="pl-10 hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-white text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" id="rate" name="rate" value="{{ data.rate }}" onchange="set_rate('rate');">
|
||||||
<div class="text-sm mt-2">
|
</div>
|
||||||
<a href="" id="get_rate_inferred_button" class="mt-2 dark:text-white bold text-coolGray-800">Get Rate Inferred:</a><span class="dark:text-white" id="rate_inferred_display"></span>
|
<button type="button" id="get_rate_inferred_button" class="px-4 py-2.5 text-sm font-medium text-white bg-blue-500 hover:bg-blue-600 rounded-md shadow-sm focus:outline-none">Get Rate Inferred</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex form-check form-check-inline mt-5">
|
<div class="flex form-check form-check-inline mt-5">
|
||||||
<div class="flex items-center h-5"> <input class="form-check-input hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" id="rate_lock" name="rate_lock" value="rl" checked=checked> </div>
|
<div class="flex items-center h-5"> <input class="form-check-input hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" id="rate_lock" name="rate_lock" value="rl" checked=checked> </div>
|
||||||
@@ -306,7 +306,6 @@ if (document.readyState === 'loading') {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if debug_mode == true %}
|
{% if debug_mode == true %}
|
||||||
<div class="py-0 border-b items-center justify-between -mx-4 mb-6 pb-3 border-gray-400 border-opacity-20">
|
<div class="py-0 border-b items-center justify-between -mx-4 mb-6 pb-3 border-gray-400 border-opacity-20">
|
||||||
<div class="w-full md:w-10/12">
|
<div class="w-full md:w-10/12">
|
||||||
@@ -414,181 +413,225 @@ if (document.readyState === 'loading') {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<script>
|
<script>
|
||||||
const xhr_rates = new XMLHttpRequest();
|
const xhr_rates = new XMLHttpRequest();
|
||||||
xhr_rates.onload = () => {
|
xhr_rates.onload = () => {
|
||||||
if (xhr_rates.status == 200) {
|
if (xhr_rates.status == 200) {
|
||||||
const obj = JSON.parse(xhr_rates.response);
|
const obj = JSON.parse(xhr_rates.response);
|
||||||
inner_html = '<pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
inner_html = '<pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
||||||
document.getElementById('rates_display').innerHTML = inner_html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const xhr_rate = new XMLHttpRequest();
|
|
||||||
xhr_rate.onload = () => {
|
|
||||||
if (xhr_rate.status == 200) {
|
|
||||||
const obj = JSON.parse(xhr_rate.response);
|
|
||||||
if (obj.hasOwnProperty('rate')) {
|
|
||||||
document.getElementById('rate').value = obj['rate'];
|
|
||||||
} else
|
|
||||||
if (obj.hasOwnProperty('amount_to')) {
|
|
||||||
document.getElementById('amt_to').value = obj['amount_to'];
|
|
||||||
} else
|
|
||||||
if (obj.hasOwnProperty('amount_from')) {
|
|
||||||
document.getElementById('amt_from').value = obj['amount_from'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function lookup_rates() {
|
|
||||||
const coin_from = document.getElementById('coin_from').value;
|
|
||||||
const coin_to = document.getElementById('coin_to').value;
|
|
||||||
|
|
||||||
if (coin_from === '-1' || coin_to === '-1') {
|
|
||||||
alert('Coins from and to must be set first.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selectedCoin = (coin_from === '15') ? '3' : coin_from;
|
|
||||||
|
|
||||||
inner_html = '<p>Updating...</p>';
|
|
||||||
document.getElementById('rates_display').innerHTML = inner_html;
|
document.getElementById('rates_display').innerHTML = inner_html;
|
||||||
document.querySelector(".pricejsonhidden").classList.remove("hidden");
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const xhr_rates = new XMLHttpRequest();
|
const xhr_rate = new XMLHttpRequest();
|
||||||
xhr_rates.onreadystatechange = function() {
|
xhr_rate.onload = () => {
|
||||||
if (xhr_rates.readyState === XMLHttpRequest.DONE) {
|
if (xhr_rate.status == 200) {
|
||||||
if (xhr_rates.status === 200) {
|
const obj = JSON.parse(xhr_rate.response);
|
||||||
document.getElementById('rates_display').innerHTML = xhr_rates.responseText;
|
if (obj.hasOwnProperty('rate')) {
|
||||||
} else {
|
document.getElementById('rate').value = obj['rate'];
|
||||||
console.error('Error fetching data:', xhr_rates.statusText);
|
} else if (obj.hasOwnProperty('amount_to')) {
|
||||||
}
|
document.getElementById('amt_to').value = obj['amount_to'];
|
||||||
}
|
} else if (obj.hasOwnProperty('amount_from')) {
|
||||||
};
|
document.getElementById('amt_from').value = obj['amount_from'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
xhr_rates.open('POST', '/json/rates');
|
function lookup_rates() {
|
||||||
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
const coin_from = document.getElementById('coin_from').value;
|
||||||
xhr_rates.send('coin_from=' + selectedCoin + '&coin_to=' + coin_to);
|
const coin_to = document.getElementById('coin_to').value;
|
||||||
|
|
||||||
|
if (coin_from === '-1' || coin_to === '-1') {
|
||||||
|
alert('Coins from and to must be set first.');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRateInferred(event) {
|
const selectedCoin = (coin_from === '15') ? '3' : coin_from;
|
||||||
event.preventDefault(); // Prevent default form submission behavior
|
|
||||||
|
|
||||||
const coin_from = document.getElementById('coin_from').value;
|
inner_html = '<p>Updating...</p>';
|
||||||
const coin_to = document.getElementById('coin_to').value;
|
document.getElementById('rates_display').innerHTML = inner_html;
|
||||||
const params = 'coin_from=' + encodeURIComponent(coin_from) + '&coin_to=' + encodeURIComponent(coin_to);
|
document.querySelector(".pricejsonhidden").classList.remove("hidden");
|
||||||
|
|
||||||
const xhr_rates = new XMLHttpRequest();
|
const xhr_rates = new XMLHttpRequest();
|
||||||
xhr_rates.onreadystatechange = function() {
|
xhr_rates.onreadystatechange = function() {
|
||||||
if (xhr_rates.readyState === XMLHttpRequest.DONE) {
|
if (xhr_rates.readyState === XMLHttpRequest.DONE) {
|
||||||
//console.log(xhr_rates.responseText);
|
if (xhr_rates.status === 200) {
|
||||||
if (xhr_rates.status === 200) {
|
document.getElementById('rates_display').innerHTML = xhr_rates.responseText;
|
||||||
try {
|
} else {
|
||||||
const responseData = JSON.parse(xhr_rates.responseText);
|
console.error('Error fetching data:', xhr_rates.statusText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr_rates.open('POST', '/json/rates');
|
||||||
|
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
|
xhr_rates.send('coin_from=' + selectedCoin + '&coin_to=' + coin_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRateInferred(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const coin_from = document.getElementById('coin_from').value;
|
||||||
|
const coin_to = document.getElementById('coin_to').value;
|
||||||
|
const params = 'coin_from=' + encodeURIComponent(coin_from) + '&coin_to=' + encodeURIComponent(coin_to);
|
||||||
|
|
||||||
|
const xhr_rates = new XMLHttpRequest();
|
||||||
|
xhr_rates.onreadystatechange = function() {
|
||||||
|
if (xhr_rates.readyState === XMLHttpRequest.DONE) {
|
||||||
|
if (xhr_rates.status === 200) {
|
||||||
|
try {
|
||||||
|
const responseData = JSON.parse(xhr_rates.responseText);
|
||||||
|
if (responseData.coingecko && responseData.coingecko.rate_inferred) {
|
||||||
const rateInferred = responseData.coingecko.rate_inferred;
|
const rateInferred = responseData.coingecko.rate_inferred;
|
||||||
//document.getElementById('rate_inferred_display').innerText = " (" + coin_from + " to " + coin_to + "): " + rateInferred;
|
document.getElementById('rate').value = rateInferred;
|
||||||
document.getElementById('rate_inferred_display').innerText = " " + rateInferred;
|
set_rate('rate');
|
||||||
} catch (error) {
|
} else {
|
||||||
console.error('Error parsing response:', error);
|
document.getElementById('rate').value = 'Error: Rate limit';
|
||||||
|
console.error('Rate limit reached or invalid response format');
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
console.error('Error fetching data:', xhr_rates.statusText);
|
document.getElementById('rate').value = 'Error: Rate limit';
|
||||||
|
console.error('Error parsing response:', error);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('rate').value = 'Error: Rate limit';
|
||||||
|
console.error('Error fetching data:', xhr_rates.statusText);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
xhr_rates.open('POST', '/json/rates');
|
xhr_rates.open('POST', '/json/rates');
|
||||||
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
xhr_rates.send(params);
|
xhr_rates.send(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('get_rate_inferred_button').addEventListener('click', getRateInferred);
|
||||||
|
|
||||||
|
function set_swap_type_enabled(coin_from, coin_to, swap_type) {
|
||||||
|
const adaptor_sig_only_coins = [
|
||||||
|
'6', /* XMR */
|
||||||
|
'9', /* WOW */
|
||||||
|
'8', /* PART_ANON */
|
||||||
|
'7', /* PART_BLIND */
|
||||||
|
'13', /* FIRO */
|
||||||
|
'18', /* DOGE */
|
||||||
|
'17' /* BCH */
|
||||||
|
];
|
||||||
|
const secret_hash_only_coins = [
|
||||||
|
'11', /* PIVX */
|
||||||
|
'12' /* DASH */
|
||||||
|
];
|
||||||
|
|
||||||
|
let make_hidden = false;
|
||||||
|
|
||||||
|
coin_from = String(coin_from);
|
||||||
|
coin_to = String(coin_to);
|
||||||
|
|
||||||
|
if (adaptor_sig_only_coins.indexOf(coin_from) !== -1 || adaptor_sig_only_coins.indexOf(coin_to) !== -1) {
|
||||||
|
swap_type.disabled = true;
|
||||||
|
swap_type.value = 'xmr_swap';
|
||||||
|
make_hidden = true;
|
||||||
|
swap_type.classList.add('select-disabled');
|
||||||
|
} else if (secret_hash_only_coins.indexOf(coin_from) !== -1 || secret_hash_only_coins.indexOf(coin_to) !== -1) {
|
||||||
|
swap_type.disabled = true;
|
||||||
|
swap_type.value = 'seller_first';
|
||||||
|
make_hidden = true;
|
||||||
|
swap_type.classList.add('select-disabled');
|
||||||
|
} else {
|
||||||
|
swap_type.disabled = false;
|
||||||
|
swap_type.classList.remove('select-disabled');
|
||||||
|
swap_type.value = 'xmr_swap';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('get_rate_inferred_button').addEventListener('click', getRateInferred);
|
let swap_type_hidden = document.getElementById('swap_type_hidden');
|
||||||
|
if (make_hidden) {
|
||||||
|
if (!swap_type_hidden) {
|
||||||
|
swap_type_hidden = document.createElement('input');
|
||||||
|
swap_type_hidden.setAttribute('id', 'swap_type_hidden');
|
||||||
|
swap_type_hidden.setAttribute('type', 'hidden');
|
||||||
|
swap_type_hidden.setAttribute('name', 'swap_type');
|
||||||
|
document.getElementById('form').appendChild(swap_type_hidden);
|
||||||
|
}
|
||||||
|
swap_type_hidden.setAttribute('value', swap_type.value);
|
||||||
|
} else if (swap_type_hidden) {
|
||||||
|
swap_type_hidden.parentNode.removeChild(swap_type_hidden);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function set_swap_type_enabled(coin_from, coin_to, swap_type) {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
const adaptor_sig_only_coins = ['6' /* XMR */,'9' /* WOW */, '8' /* PART_ANON */, '7' /* PART_BLIND */, '13' /* FIRO */, '17' /* BCH */];
|
const coin_from = document.getElementById('coin_from');
|
||||||
const secret_hash_only_coins = ['11' /* PIVX */, '12' /* DASH */];
|
const coin_to = document.getElementById('coin_to');
|
||||||
let make_hidden = false;
|
|
||||||
if (adaptor_sig_only_coins.includes(coin_from) || adaptor_sig_only_coins.includes(coin_to)) {
|
if (coin_from && coin_to) {
|
||||||
swap_type.disabled = true;
|
coin_from.addEventListener('change', function() {
|
||||||
swap_type.value = 'xmr_swap';
|
const swap_type = document.getElementById('swap_type');
|
||||||
make_hidden = true;
|
set_swap_type_enabled(this.value, coin_to.value, swap_type);
|
||||||
swap_type.classList.add('select-disabled');
|
});
|
||||||
} else
|
|
||||||
if (secret_hash_only_coins.includes(coin_from) && secret_hash_only_coins.includes(coin_to)) {
|
coin_to.addEventListener('change', function() {
|
||||||
swap_type.disabled = true;
|
const swap_type = document.getElementById('swap_type');
|
||||||
swap_type.value = 'seller_first';
|
set_swap_type_enabled(coin_from.value, this.value, swap_type);
|
||||||
make_hidden = true;
|
});
|
||||||
swap_type.classList.add('select-disabled');
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function set_rate(value_changed) {
|
||||||
|
const coin_from = document.getElementById('coin_from').value;
|
||||||
|
const coin_to = document.getElementById('coin_to').value;
|
||||||
|
const amt_from = document.getElementById('amt_from').value;
|
||||||
|
const amt_to = document.getElementById('amt_to').value;
|
||||||
|
const rate = document.getElementById('rate').value;
|
||||||
|
const lock_rate = rate == '' ? false : document.getElementById('rate_lock').checked;
|
||||||
|
|
||||||
|
if (value_changed === 'coin_from' || value_changed === 'coin_to') {
|
||||||
|
document.getElementById('rate').value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const swap_type = document.getElementById('swap_type');
|
||||||
|
set_swap_type_enabled(coin_from, coin_to, swap_type);
|
||||||
|
|
||||||
|
if (coin_from == '-1' || coin_to == '-1') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let params = 'coin_from=' + coin_from + '&coin_to=' + coin_to;
|
||||||
|
|
||||||
|
if (value_changed == 'rate' || (lock_rate && value_changed == 'amt_from') || (amt_to == '' && value_changed == 'amt_from')) {
|
||||||
|
if (rate == '' || (amt_from == '' && amt_to == '')) {
|
||||||
|
return;
|
||||||
|
} else if (amt_from == '' && amt_to != '') {
|
||||||
|
if (value_changed == 'amt_from') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params += '&rate=' + rate + '&amt_to=' + amt_to;
|
||||||
} else {
|
} else {
|
||||||
swap_type.disabled = false;
|
params += '&rate=' + rate + '&amt_from=' + amt_from;
|
||||||
swap_type.classList.remove('select-disabled');
|
|
||||||
}
|
}
|
||||||
let swap_type_hidden = document.getElementById('swap_type_hidden');
|
} else if (lock_rate && value_changed == 'amt_to') {
|
||||||
if (make_hidden) {
|
if (amt_to == '' || rate == '') {
|
||||||
if (!swap_type_hidden) {
|
|
||||||
swap_type_hidden = document.createElement('input');
|
|
||||||
swap_type_hidden.setAttribute('id', 'swap_type_hidden');
|
|
||||||
swap_type_hidden.setAttribute('type', 'hidden');
|
|
||||||
swap_type_hidden.setAttribute('name', 'swap_type');
|
|
||||||
document.getElementById('form').appendChild(swap_type_hidden)
|
|
||||||
}
|
|
||||||
swap_type_hidden.setAttribute('value', swap_type.value);
|
|
||||||
} else
|
|
||||||
if (swap_type_hidden) {
|
|
||||||
swap_type_hidden.parentNode.removeChild(swap_type_hidden);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_rate(value_changed) {
|
|
||||||
const coin_from = document.getElementById('coin_from').value;
|
|
||||||
const coin_to = document.getElementById('coin_to').value;
|
|
||||||
const amt_from = document.getElementById('amt_from').value;
|
|
||||||
const amt_to = document.getElementById('amt_to').value;
|
|
||||||
const rate = document.getElementById('rate').value;
|
|
||||||
const lock_rate = rate == '' ? false : document.getElementById('rate_lock').checked;
|
|
||||||
|
|
||||||
const swap_type = document.getElementById('swap_type');
|
|
||||||
set_swap_type_enabled(coin_from, coin_to, swap_type);
|
|
||||||
|
|
||||||
if (coin_from == '-1' || coin_to == '-1') {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
params = 'coin_from=' + coin_from + '&coin_to=' + coin_to;
|
params += '&amt_to=' + amt_to + '&rate=' + rate;
|
||||||
if (value_changed == 'rate' || (lock_rate && value_changed == 'amt_from') || (amt_to == '' && value_changed == 'amt_from')) {
|
} else {
|
||||||
if (rate == '' || (amt_from == '' && amt_to == '')) {
|
if (amt_from == '' || amt_to == '') {
|
||||||
return;
|
return;
|
||||||
} else
|
|
||||||
if (amt_from == '' && amt_to != '') {
|
|
||||||
if (value_changed == 'amt_from') { // Don't try and set a value just cleared
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
params += '&rate=' + rate + '&amt_to=' + amt_to;
|
|
||||||
} else {
|
|
||||||
params += '&rate=' + rate + '&amt_from=' + amt_from;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
if (lock_rate && value_changed == 'amt_to') {
|
|
||||||
if (amt_to == '' || rate == '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
params += '&amt_to=' + amt_to + '&rate=' + rate;
|
|
||||||
} else {
|
|
||||||
if (amt_from == '' || amt_to == '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
params += '&amt_from=' + amt_from + '&amt_to=' + amt_to;
|
|
||||||
}
|
}
|
||||||
xhr_rate.open('POST', '/json/rate');
|
params += '&amt_from=' + amt_from + '&amt_to=' + amt_to;
|
||||||
xhr_rate.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
|
||||||
xhr_rate.send(params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
xhr_rate.open('POST', '/json/rate');
|
||||||
const coin_from = document.getElementById('coin_from').value;
|
xhr_rate.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||||
const coin_to = document.getElementById('coin_to').value;
|
xhr_rate.send(params);
|
||||||
const swap_type = document.getElementById('swap_type');
|
}
|
||||||
set_swap_type_enabled(coin_from, coin_to, swap_type);
|
|
||||||
});
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
</script>
|
const coin_from = document.getElementById('coin_from').value;
|
||||||
|
const coin_to = document.getElementById('coin_to').value;
|
||||||
|
const swap_type = document.getElementById('swap_type');
|
||||||
|
set_swap_type_enabled(coin_from, coin_to, swap_type);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
<script src="static/js/new_offer.js"></script>
|
<script src="static/js/new_offer.js"></script>
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
|
|||||||
@@ -175,6 +175,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if data.swap_style == 'xmr' %}
|
{% if data.swap_style == 'xmr' %}
|
||||||
|
{% if data.coin_from | int in (6, 9) %} {# Not XMR or WOW #}
|
||||||
|
{% else %}
|
||||||
<div class="w-full md:w-1/2 p-3">
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee From Confirm Target</p>
|
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee From Confirm Target</p>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@@ -199,6 +201,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -233,7 +236,9 @@
|
|||||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"> </div> <input class="cursor-not-allowed disabled-input hover:border-blue-500 pr-10 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-white text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-5 focus:ring-0" type="text" id="amt_to" name="amt_to" value="{{ data.amt_to }}" readonly>
|
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none"> </div> <input class="cursor-not-allowed disabled-input hover:border-blue-500 pr-10 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-white text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-5 focus:ring-0" type="text" id="amt_to" name="amt_to" value="{{ data.amt_to }}" readonly>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if data.swap_style == 'xmr' and coin_to != '6' %}
|
{% if data.swap_style == 'xmr' %}
|
||||||
|
{% if data.coin_to | int in (6, 9) %} {# Not XMR or WOW #}
|
||||||
|
{% else %}
|
||||||
<div class="w-full md:w-1/2 p-3">
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee To Confirm Target</p>
|
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee To Confirm Target</p>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
@@ -259,6 +264,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -273,7 +279,7 @@
|
|||||||
<div class="w-full md:flex-1 p-3">
|
<div class="w-full md:flex-1 p-3">
|
||||||
<div class="flex flex-wrap -m-3">
|
<div class="flex flex-wrap -m-3">
|
||||||
<div class="w-full md:w-1/2 p-3">
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Minimum Bid Amount</p>
|
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Minimum Purchase</p>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||||
{{ select_bid_amount_svg | safe }}
|
{{ select_bid_amount_svg | safe }}
|
||||||
@@ -449,4 +455,4 @@
|
|||||||
</div>
|
</div>
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -11,108 +11,88 @@
|
|||||||
<script>
|
<script>
|
||||||
function getAPIKeys() {
|
function getAPIKeys() {
|
||||||
return {
|
return {
|
||||||
cryptoCompare: '{{chart_api_key}}',
|
cryptoCompare: "{{ chart_api_key|safe }}",
|
||||||
coinGecko: '{{coingecko_api_key}}'
|
coinGecko: "{{ coingecko_api_key|safe }}"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWebSocketConfig() {
|
||||||
|
return {
|
||||||
|
port: "{{ ws_port|safe }}",
|
||||||
|
fallbackPort: "11700"
|
||||||
|
};
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% if sent_offers %}
|
<section class="py-3 px-4 mt-6">
|
||||||
<div class="container mx-auto">
|
<div class="lg:container mx-auto">
|
||||||
<section class="p-5 mt-5">
|
<div class="relative py-8 px-8 bg-coolGray-900 dark:bg-gray-500 rounded-md overflow-hidden">
|
||||||
<div class="flex flex-wrap items-center -m-2">
|
<img class="absolute z-10 left-4 top-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||||
<div class="w-full md:w-1/2 p-2">
|
<img class="absolute z-10 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="">
|
||||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover"
|
||||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">Home</a></li>
|
src="/static/images/elements/wave.svg" alt="">
|
||||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||||
<li>
|
<div class="w-full md:w-1/2 p-3">
|
||||||
<a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="{% if page_type == 'offers' %}/offers{% elif page_type == 'sentoffers' %}/sentoffers{% endif %}">
|
<h2 class="mb-3 text-2xl font-bold text-white tracking-tighter">{{ page_type }}</h2>
|
||||||
{{ page_type }}
|
<p class="font-normal text-coolGray-200 dark:text-white">{{ page_type_description }}</p>
|
||||||
</a>
|
</div>
|
||||||
</li>
|
<div class="w-full md:w-1/2 p-3 flex justify-end items-center hidden">
|
||||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
<a id="refresh" href="/newoffer"
|
||||||
</ul>
|
class="rounded-full flex items-center justify-center px-4 py-2 bg-blue-500 hover:bg-green-600 hover:border-green-600 font-medium text-sm text-white border border-blue-500 rounded-md focus:ring-0 focus:outline-none">
|
||||||
</div>
|
{{ place_new_offer_svg | safe }}
|
||||||
</div>
|
<span>Place new Offer</span>
|
||||||
</section>
|
</a>
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if sent_offers %}
|
|
||||||
<section class="py-5">
|
|
||||||
{% else %}
|
|
||||||
<section class="py-5 mt-5">
|
|
||||||
{% endif %}
|
|
||||||
<div class="container px-4 mx-auto">
|
|
||||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-gray-500 rounded-md overflow-hidden">
|
|
||||||
<img class="absolute z-10 left-4 top-4 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="dots-red">
|
|
||||||
<img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="wave">
|
|
||||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
|
||||||
<div class="w-full md:w-1/2 p-3">
|
|
||||||
<h2 class="mb-6 text-4xl font-bold text-white tracking-tighter">{{ page_type }}</h2>
|
|
||||||
<p class="font-normal text-coolGray-200 dark:text-white">{{ page_type_description }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="rounded-full{{ page_button }} w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto">
|
|
||||||
<a id="refresh" href="/newoffer" class="rounded-full flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-green-600 hover:border-green-600 font-medium text-sm text-white border border-blue-500 rounded-md focus:ring-0 focus:outline-none">{{ place_new_offer_svg | safe }}<span>Place new Offer</span></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
{% include 'inc_messages.html' %}
|
{% include 'inc_messages.html' %}
|
||||||
|
|
||||||
{% if show_chart %}
|
{% if show_chart %}
|
||||||
<section class="relative hidden md:block">
|
<section class="relative hidden md:block">
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
<div class="px-6 py-0 mt-5 h-full overflow-hidden">
|
||||||
<div class="pb-6 border-coolGray-100">
|
<div class="pb-6 border-coolGray-100">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between">
|
||||||
<div class="w-full pt-2">
|
<div class="w-full pt-2">
|
||||||
<div class="container px-4 mx-auto">
|
<div class="lg:container mx-auto">
|
||||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl container-to-blur">
|
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl container-to-blur">
|
||||||
<div class="flex justify-between items-center mb-4 mr-10 ml-10">
|
<div class="flex justify-between items-center mb-4 mr-10 ml-10">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h2 class="text-xl font-bold dark:text-white" id="chart-title">Price Chart</h2>
|
<h2 class="text-xl font-bold dark:text-white" id="chart-title">Price Chart</h2>
|
||||||
<div class="flex items-center space-x-4">
|
<div class="flex items-center space-x-4">
|
||||||
<button id="resolution-year" class="ml-5 resolution-button">1Y</button>
|
<button id="resolution-year" class="ml-5 resolution-button">1Y</button>
|
||||||
<button id="resolution-sixMonths" class="resolution-button">6M</button>
|
<button id="resolution-sixMonths" class="resolution-button">6M</button>
|
||||||
<button id="resolution-day" class="resolution-button">24H</button>
|
<button id="resolution-day" class="resolution-button">24H</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span id="load-time hidden" class="mr-2 text-sm text-gray-600 dark:text-gray-300"></span>
|
<span id="load-time hidden" class="mr-2 text-sm text-gray-600 dark:text-gray-300"></span>
|
||||||
<span id="last-refreshed-time hidden" class="mr-2 text-sm text-gray-600 dark:text-gray-300"></span>
|
<span id="last-refreshed-time hidden" class="mr-2 text-sm text-gray-600 dark:text-gray-300"></span>
|
||||||
<span id="cache-status hidden" class="mr-4 text-sm text-gray-600 dark:text-gray-300"></span>
|
<span id="cache-status hidden" class="mr-4 text-sm text-gray-600 dark:text-gray-300"></span>
|
||||||
<span id="tor-status" class="mr-4 text-sm hidden {% if tor_established %}text-green-500{% else %}text-red-500{% endif %}"> Tor {% if tor_established %}ON{% else %}OFF{% endif %}
|
<span id="tor-status" class="mr-4 text-sm hidden {% if tor_established %}text-green-500{% else %}text-red-500{% endif %}">
|
||||||
|
Tor {% if tor_established %}ON{% else %}OFF{% endif %}
|
||||||
<a href="https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_tor.html" target="_blank" rel="noopener noreferrer" class="underline">(?)</a>
|
<a href="https://academy.particl.io/en/latest/basicswap-guides/basicswapguides_tor.html" target="_blank" rel="noopener noreferrer" class="underline">(?)</a>
|
||||||
</span>
|
</span>
|
||||||
<span id="next-refresh-time" class="mr-4 text-sm text-gray-600 dark:text-gray-300">
|
<span id="next-refresh-time" class="mr-4 text-sm text-gray-600 dark:text-gray-300">
|
||||||
<span id="next-refresh-label"></span>
|
<span id="next-refresh-label"></span>
|
||||||
<span id="next-refresh-value"></span>
|
<span id="next-refresh-value"></span>
|
||||||
</span>
|
</span>
|
||||||
<div id="tooltip-volume" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
<div id="tooltip-volume" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">Toggle Coin Volume</div>
|
||||||
Toggle Coin Volume
|
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
||||||
</div>
|
|
||||||
<button id="toggle-volume" data-tooltip-target="tooltip-volume" class="text-white font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Toggle Volume">
|
<button id="toggle-volume" data-tooltip-target="tooltip-volume" class="text-white font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Toggle Volume">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z" />
|
<path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zM8 7a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zM14 4a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div id="tooltip-refresh" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
<div id="tooltip-refresh" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">Refresh Charts/Prices & Clear Cache</div>
|
||||||
Refresh Charts/Prices & Clear Cache
|
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
||||||
</div>
|
|
||||||
<button id="refresh-all" data-tooltip-target="tooltip-refresh" class="text-gray-600 dark:text-gray-400 font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Refresh Data & Clear Cache">
|
<button id="refresh-all" data-tooltip-target="tooltip-refresh" class="text-gray-600 dark:text-gray-400 font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Refresh Data & Clear Cache">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
<circle cx="12" cy="12" r="3" />
|
<circle cx="12" cy="12" r="3" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div id="tooltip-auto" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
<div id="tooltip-auto" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">Auto Refresh Enable/Disable</div>
|
||||||
Auto Refresh Enable/Disable
|
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
||||||
</div>
|
|
||||||
<button id="toggle-auto-refresh" data-enabled="false" data-tooltip-target="tooltip-auto" class="text-white font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Enable Auto-Refresh">
|
<button id="toggle-auto-refresh" data-enabled="false" data-tooltip-target="tooltip-auto" class="text-white font-bold py-2 px-4 rounded mr-2 focus:outline-none focus:ring-0 transition-colors duration-200" title="Enable Auto-Refresh">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
<path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd" />
|
<path fill-rule="evenodd" d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z" clip-rule="evenodd" />
|
||||||
@@ -130,18 +110,25 @@ function getAPIKeys() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="error-overlay" class="notice-overlay hidden absolute inset-0 bg-opacity-30 backdrop-blur-sm flex items-center justify-center">
|
||||||
<div id="error-overlay" class="error-overlay hidden absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center">
|
<div id="error-content" class="notice-content bg-white dark:bg-gray-800 rounded-xl p-6 w-full max-w-2xl mx-4 relative shadow-lg">
|
||||||
<div id="error-content" class="error-content bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-2xl mx-4 relative">
|
<div class="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-blue-400 via-blue-500 to-blue-600"></div>
|
||||||
<button id="close-error" class="absolute top-3 right-3 bg-red-500 text-white rounded-full p-2 hover:bg-red-600 focus:outline-none">
|
<button id="close-error" class="absolute top-3 right-3 text-gray-400 hover:text-gray-500 dark:text-gray-500 dark:hover:text-white rounded-full p-2 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors focus:outline-none">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<p class="text-red-600 font-semibold text-xl mb-4">Error</p>
|
<div class="flex items-center gap-3 mb-4">
|
||||||
<p id="error-message" class="text-gray-700 dark:text-gray-300 text-lg mb-6"></p>
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<p class="text-sm text-gray-600 dark:text-gray-400">To review or update your Chart API Key(s), navigate to<a href="/settings" class="text-blue-500 hover:underline">Settings & Tools > Settings > General (TAB)</a>.
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" />
|
||||||
</p>
|
</svg>
|
||||||
|
<p class="text-lg font-medium text-gray-900 dark:text-gray-100">Notice</p>
|
||||||
|
</div>
|
||||||
|
<p id="error-message" class="text-gray-600 dark:text-gray-300 text-base mb-6"></p>
|
||||||
|
<div class="text-sm text-gray-500 dark:text-gray-400">
|
||||||
|
Need to update your (API) settings?
|
||||||
|
<a href="/settings" class="text-blue-500 hover:text-blue-600 dark:hover:text-blue-400 font-medium ml-1 hover:underline">Go to Settings -> General</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,9 +137,9 @@ function getAPIKeys() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="py-4 flex flex-wrap justify-center overflow-hidden container-to-blur">
|
<section class="py-4 px-3 flex-wrap overflow-hidden container-to-blur">
|
||||||
<div class="container px-4 mx-auto">
|
<div class="lg:container mx-auto">
|
||||||
<div class="flex flex-wrap justify-center -m-3" id="coin-container">
|
<div class="flex flex-wrap justify-center lg:justify-start xl:justify-center" id="coin-container">
|
||||||
{% set coin_data = {
|
{% set coin_data = {
|
||||||
'BTC': {'name': 'Bitcoin', 'symbol': 'BTC', 'image': 'Bitcoin.png', 'show': true},
|
'BTC': {'name': 'Bitcoin', 'symbol': 'BTC', 'image': 'Bitcoin.png', 'show': true},
|
||||||
'XMR': {'name': 'Monero', 'symbol': 'XMR', 'image': 'Monero.png', 'show': true},
|
'XMR': {'name': 'Monero', 'symbol': 'XMR', 'image': 'Monero.png', 'show': true},
|
||||||
@@ -163,7 +150,7 @@ function getAPIKeys() {
|
|||||||
'PIVX': {'name': 'PIVX', 'symbol': 'PIVX', 'image': 'PIVX.png', 'show': true},
|
'PIVX': {'name': 'PIVX', 'symbol': 'PIVX', 'image': 'PIVX.png', 'show': true},
|
||||||
'DASH': {'name': 'Dash', 'symbol': 'DASH', 'image': 'Dash.png', 'show': true},
|
'DASH': {'name': 'Dash', 'symbol': 'DASH', 'image': 'Dash.png', 'show': true},
|
||||||
'ETH': {'name': 'Ethereum', 'symbol': 'ETH', 'image': 'Ethereum.png', 'show': false},
|
'ETH': {'name': 'Ethereum', 'symbol': 'ETH', 'image': 'Ethereum.png', 'show': false},
|
||||||
'DOGE': {'name': 'Dogecoin', 'symbol': 'DOGE', 'image': 'Doge.png', 'show': false},
|
'DOGE': {'name': 'Dogecoin', 'symbol': 'DOGE', 'image': 'Dogecoin.png', 'show': true},
|
||||||
'DCR': {'name': 'Decred', 'symbol': 'DCR', 'image': 'Decred.png', 'show': true},
|
'DCR': {'name': 'Decred', 'symbol': 'DCR', 'image': 'Decred.png', 'show': true},
|
||||||
'ZANO': {'name': 'Zano', 'symbol': 'ZANO', 'image': 'Zano.png', 'show': false},
|
'ZANO': {'name': 'Zano', 'symbol': 'ZANO', 'image': 'Zano.png', 'show': false},
|
||||||
'WOW': {'name': 'Wownero', 'symbol': 'WOW', 'image': 'Wownero.png', 'show': true}
|
'WOW': {'name': 'Wownero', 'symbol': 'WOW', 'image': 'Wownero.png', 'show': true}
|
||||||
@@ -185,9 +172,9 @@ function getAPIKeys() {
|
|||||||
|
|
||||||
{% for coin_symbol in custom_order %}
|
{% for coin_symbol in custom_order %}
|
||||||
{% if coin_symbol in display_coins and coin_data[coin_symbol]['show'] %}
|
{% if coin_symbol in display_coins and coin_data[coin_symbol]['show'] %}
|
||||||
<div class="w-full sm:w-1/2 lg:w-1/5 p-3" id="{{ coin_symbol.lower() }}-container">
|
<div class="w-full sm:w-1/2 md:w-1/4 lg:w-1/5 xl:w-1/6 p-3" id="{{ coin_symbol.lower() }}-container">
|
||||||
<div class="px-5 py-3 h-full bg-coolGray-100 dark:bg-gray-500 rounded-2xl dark:text-white {% if coin_symbol == 'BTC' %}active-container{% endif %}" style="min-height: 180px;">
|
<div class="px-5 py-3 h-full bg-coolGray-100 dark:bg-gray-500 rounded-2xl dark:text-white {% if coin_symbol == 'BTC' %}active-container{% endif %}" style="min-height: 180px;">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center h-10">
|
||||||
<img src="/static/images/coins/{{ coin_data[coin_symbol]['image'] }}" class="rounded-xl" style="width: 28px; height: 28px; object-fit: contain;" alt="{{ coin_data[coin_symbol]['name'] }}">
|
<img src="/static/images/coins/{{ coin_data[coin_symbol]['image'] }}" class="rounded-xl" style="width: 28px; height: 28px; object-fit: contain;" alt="{{ coin_data[coin_symbol]['name'] }}">
|
||||||
<p class="ml-1 text-black text-sm dark:text-white">
|
<p class="ml-1 text-black text-sm dark:text-white">
|
||||||
{{ coin_data[coin_symbol]['name'] }} {% if coin_data[coin_symbol]['symbol'] != coin_data[coin_symbol]['name'] %}({{ coin_data[coin_symbol]['symbol'] }}){% endif %}
|
{{ coin_data[coin_symbol]['name'] }} {% if coin_data[coin_symbol]['symbol'] != coin_data[coin_symbol]['name'] %}({{ coin_data[coin_symbol]['symbol'] }}){% endif %}
|
||||||
@@ -206,12 +193,13 @@ function getAPIKeys() {
|
|||||||
</div>
|
</div>
|
||||||
{% if coin_symbol != 'BTC' %}
|
{% if coin_symbol != 'BTC' %}
|
||||||
<div id="{{ coin_symbol.lower() }}-btc-price-div" class="flex items-center text-xs text-gray-600 dark:text-gray-300 mt-2 {% if coin_symbol == 'WOW' %}hidden{% endif %}">
|
<div id="{{ coin_symbol.lower() }}-btc-price-div" class="flex items-center text-xs text-gray-600 dark:text-gray-300 mt-2 {% if coin_symbol == 'WOW' %}hidden{% endif %}">
|
||||||
<span class="bold mr-2">BTC:</span> <span id="{{ coin_symbol.lower() }}-price-btc"></span>
|
<span class="bold mr-2 ml-1">BTC:</span>
|
||||||
|
<span id="{{ coin_symbol.lower() }}-price-btc"></span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div id="{{ coin_symbol.lower() }}-volume-div" class="flex items-center text-xs text-gray-600 dark:text-gray-300 mt-2">
|
<div id="{{ coin_symbol.lower() }}-volume-div" class="flex items-center text-xs text-gray-600 dark:text-gray-300 mt-2">
|
||||||
<span class="bold mr-2">VOL:</span>
|
<span class="bold mr-2 ml-1">VOL:</span>
|
||||||
<div id="{{ coin_symbol.lower() }}-volume-24h"></div>
|
<span id="{{ coin_symbol.lower() }}-volume-24h"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -226,17 +214,17 @@ function getAPIKeys() {
|
|||||||
<script src="/static/js/pricechart.js"></script>
|
<script src="/static/js/pricechart.js"></script>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
<div class="px-6 py-0 mt-5 h-full overflow-hidden">
|
||||||
<div class="border-coolGray-100">
|
<div class="border-coolGray-100">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between">
|
||||||
<div class="w-full mx-auto pt-2">
|
<div class="w-full mx-auto pt-2">
|
||||||
<form method="post" id="filterForm">
|
<form method="post" id="filterForm">
|
||||||
<div class="flex items-center justify-center pb-4 dark:text-white">
|
<div class="flex items-center justify-center pb-4 dark:text-white">
|
||||||
<div class="rounded-b-md">
|
<div class="rounded-b-md">
|
||||||
<div class="w-full md:w-0/12">
|
<div class="w-full md:w-0/12">
|
||||||
<div class="container flex flex-wrap">
|
<div class="lg:container flex flex-wrap justify-center">
|
||||||
<div class="md:w-auto hover-container justify-center">
|
<div class="md:w-auto hover-container">
|
||||||
<div class="flex flex-wrap justify-center">
|
<div class="flex flex-wrap">
|
||||||
<div class="pt-3 px-3 md:w-auto hover-container">
|
<div class="pt-3 px-3 md:w-auto hover-container">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<button id="coin_to_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border-l border-t border-b border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-l-lg flex items-center" disabled></button>
|
<button id="coin_to_button" class="bg-gray-50 text-gray-900 appearance-none w-10 dark:bg-gray-500 dark:text-white border-l border-t border-b border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-l-lg flex items-center" disabled></button>
|
||||||
@@ -283,14 +271,26 @@ function getAPIKeys() {
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-auto pt-3 px-3">
|
<div class="pt-3 px-3 md:w-auto hover-container">
|
||||||
|
<div class="flex">
|
||||||
|
<div class="relative">
|
||||||
|
{{ input_arrow_down_svg | safe }}
|
||||||
|
<select name="sent_from" id="sent_from" class="bg-gray-50 text-gray-900 appearance-none pr-10 pl-5 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none block w-full p-2.5 focus:ring-0">
|
||||||
|
<option value="any" {% if not filters.sent_from %} selected{% endif %}>All Offers</option>
|
||||||
|
<option value="public" {% if filters.sent_from == 'public' %} selected{% endif %}>Public</option>
|
||||||
|
<option value="private" {% if filters.sent_from == 'private' %} selected{% endif %}>Private</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="w-full lg:w-auto pt-3 px-3">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<button type="button" id="clearFilters" class="transition-opacity duration-200 flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm hover:text-white dark:text-white dark:bg-gray-500 bg-coolGray-200 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-coolGray-200 dark:border-gray-400 rounded-md shadow-button focus:ring-0 focus:outline-none" disabled>
|
<button type="button" id="clearFilters" class="transition-opacity duration-200 flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm hover:text-white dark:text-white dark:bg-gray-500 bg-coolGray-200 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-coolGray-200 dark:border-gray-400 rounded-md shadow-button focus:ring-0 focus:outline-none" disabled>
|
||||||
<span>Clear Filters</span>
|
<span>Clear Filters</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-auto pt-3 px-3">
|
<div class="w-full lg:w-auto pt-3 px-3">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<button type="button" id="refreshOffers" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
<button type="button" id="refreshOffers" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
||||||
<svg id="refreshIcon" class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
<svg id="refreshIcon" class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -300,13 +300,6 @@ function getAPIKeys() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-auto pt-3 px-3 hidden">
|
|
||||||
<div class="relative">
|
|
||||||
<button id="toggleView" class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white bg-blue-600 hover:bg-green-600 hover:border-green-600 rounded-lg transition duration-200 border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none">
|
|
||||||
<span>Toggle JSON View</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -319,18 +312,20 @@ function getAPIKeys() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div id="jsonView" class="hidden mb-4">
|
<div class="mt-5 lg:container mx-auto lg:px-0 px-6">
|
||||||
<pre id="jsonContent" class="bg-gray-100 p-4 rounded overflow-auto" style="max-height: 300px;"></pre>
|
|
||||||
</div>
|
|
||||||
<div class="container mt-5 mx-auto">
|
|
||||||
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
<div class="pt-0 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
<div class="px-0">
|
<div class="px-0">
|
||||||
<div class="w-auto mt-6 pb-6 overflow-x-auto">
|
<div class="w-auto mt-6 overflow-auto lg:overflow-hidden">
|
||||||
<table class="w-full min-w-max">
|
<table class="w-full min-w-max">
|
||||||
<thead class="uppercase">
|
<thead class="uppercase">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="p-0" data-sortable="true" data-column-index="0">
|
<th class="p-0" data-sortable="true" data-column-index="0">
|
||||||
<div class="py-3 pl-4 justify-center rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
<div class="py-3 pl-4 justify-center rounded-tl-xl bg-coolGray-200 dark:bg-gray-600">
|
||||||
|
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold"></span>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
|
<th class="p-0" data-sortable="true" data-column-index="0">
|
||||||
|
<div class="py-3 pl-4 justify-center bg-coolGray-200 dark:bg-gray-600">
|
||||||
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold">Time</span>
|
<span class="text-sm mr-1 text-gray-600 dark:text-gray-300 font-semibold">Time</span>
|
||||||
<span class="sort-icon ml-1 text-gray-600 dark:text-gray-400" id="sort-icon-0">↓</span>
|
<span class="sort-icon ml-1 text-gray-600 dark:text-gray-400" id="sort-icon-0">↓</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -340,37 +335,29 @@ function getAPIKeys() {
|
|||||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
{% if sent_offers %}
|
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-left">
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-left">
|
||||||
|
{% if sent_offers %}
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Max Recv</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Max Recv</span>
|
||||||
</div>
|
{% else %}
|
||||||
</th>
|
|
||||||
{% else %}
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-left">
|
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Max Send</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Max Send</span>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
{% endif %}
|
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-center">
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Swap</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Swap</span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
{% if sent_offers %}
|
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||||||
|
{% if sent_offers %}
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold pr-2">Your Liq.</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold pr-2">Your Liq.</span>
|
||||||
</div>
|
{% else %}
|
||||||
</th>
|
|
||||||
{% else %}
|
|
||||||
<th class="p-0">
|
|
||||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold pr-2">Max Recv</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold pr-2">Max Recv</span>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
{% endif %}
|
|
||||||
<th class="p-0" data-sortable="true" data-column-index="5">
|
<th class="p-0" data-sortable="true" data-column-index="5">
|
||||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right flex items-center justify-end">
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right flex items-center justify-end">
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Rate</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Rate</span>
|
||||||
@@ -383,10 +370,9 @@ function getAPIKeys() {
|
|||||||
<span class="sort-icon ml-1 text-gray-600 dark:text-gray-400" id="sort-icon-6">↓</span>
|
<span class="sort-icon ml-1 text-gray-600 dark:text-gray-400" id="sort-icon-6">↓</span>
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
<th class="p-0" data-sortable="true" data-column-index="7">
|
<th class="p-0">
|
||||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 rounded-tr-xl">
|
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 rounded-tr-xl">
|
||||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Trade</span>
|
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Trade</span>
|
||||||
<span class="sort-icon ml-1 text-gray-600 dark:text-gray-400" id="sort-icon-7">↓</span>
|
|
||||||
</div>
|
</div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -400,11 +386,18 @@ function getAPIKeys() {
|
|||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div class="flex flex-wrap justify-between items-center pl-6 pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
<div class="flex flex-wrap justify-between items-center pl-6 pt-6 pr-6 border-t border-gray-100 dark:border-gray-400">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<p class="text-sm font-heading dark:text-gray-400 mr-4">Last refreshed: <span id="lastRefreshTime">Never</span></p>
|
<div class="flex items-center mr-4">
|
||||||
<p class="text-sm font-heading dark:text-gray-400 mr-4"><span class="ml-4" data-listing-label>Network Listings: </span><span id="newEntriesCount"></span></p>
|
<span id="status-dot" class="w-2.5 h-2.5 rounded-full bg-gray-500 mr-2"></span>
|
||||||
<p class="text-sm font-heading dark:text-gray-400 mr-4"><span id="nextRefreshContainer" class="ml-4">Next refresh: <span id="nextRefreshTime"></span>
|
<span id="status-text" class="text-sm text-gray-500">Connecting...</span>
|
||||||
</span></p>
|
</div>
|
||||||
</div>
|
<p class="text-sm font-heading dark:text-gray-400 mr-4">
|
||||||
|
Last refreshed: <span id="lastRefreshTime">Never</span>
|
||||||
|
</p>
|
||||||
|
<p class="text-sm font-heading dark:text-gray-400 mr-4">
|
||||||
|
<span data-listing-label>Network Listings: </span>
|
||||||
|
<span id="newEntriesCount"></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<button type="button" id="prevPage" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
<button type="button" id="prevPage" class="inline-flex items-center h-9 py-1 px-4 text-xs text-blue-50 font-semibold bg-blue-500 hover:bg-green-600 rounded-lg transition duration-200 focus:ring-0 focus:outline-none">
|
||||||
{{ page_back_svg | safe }}
|
{{ page_back_svg | safe }}
|
||||||
@@ -423,6 +416,5 @@ function getAPIKeys() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||||
|
<script src="/static/js/offers.js"></script>
|
||||||
<script src="/static/js/offerstable.js"></script>
|
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
|
|||||||
@@ -281,6 +281,13 @@
|
|||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2" class="py-3 px-6">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="text-red-500 dark:text-red-500 text-sm font-medium">WARNING: Advanced features - Only enable if you know what you're doing!</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-3 px-6 bold w-96 bold">Debug Mode</td>
|
<td class="py-3 px-6 bold w-96 bold">Debug Mode</td>
|
||||||
<td class="py-3 px-6">
|
<td class="py-3 px-6">
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
<link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet">
|
<link type="text/css" media="all" href="/static/css/libs/tailwind.min.css" rel="stylesheet">
|
||||||
<link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet">
|
<link type="text/css" media="all" href="/static/css/style.css" rel="stylesheet">
|
||||||
<script src="/static/js/main.js"></script>
|
<script src="/static/js/main.js"></script>
|
||||||
<script src="/static/js/libs/flowbite.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
const isDarkMode =
|
const isDarkMode =
|
||||||
localStorage.getItem('color-theme') === 'dark' ||
|
localStorage.getItem('color-theme') === 'dark' ||
|
||||||
|
|||||||
@@ -1,36 +1,37 @@
|
|||||||
{% include 'header.html' %}
|
{% include 'header.html' %}
|
||||||
{% from 'style.html' import select_box_arrow_svg, select_box_class, circular_arrows_svg, circular_error_svg, circular_info_svg, cross_close_svg, breadcrumb_line_svg, withdraw_svg, utxo_groups_svg, create_utxo_svg, red_cross_close_svg, blue_cross_close_svg, circular_update_messages_svg, circular_error_messages_svg %}
|
{% from 'style.html' import select_box_arrow_svg, select_box_class, circular_arrows_svg, circular_error_svg, circular_info_svg, cross_close_svg, breadcrumb_line_svg, withdraw_svg, utxo_groups_svg, create_utxo_svg, red_cross_close_svg, blue_cross_close_svg, circular_update_messages_svg, circular_error_messages_svg %}
|
||||||
<script src="/static/js/libs//qrcode.js"></script>
|
<script src="/static/js/libs//qrcode.js"></script>
|
||||||
<div class="container mx-auto">
|
|
||||||
<section class="p-5 mt-5">
|
<section class="p-5 mt-5">
|
||||||
|
<div class="container mx-auto">
|
||||||
<div class="flex flex-wrap items-center -m-2">
|
<div class="flex flex-wrap items-center -m-2">
|
||||||
<div class="w-full md:w-1/2 p-2">
|
<div class="w-full md:w-1/2 p-2">
|
||||||
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
<ul class="flex flex-wrap items-center gap-x-3 mb-2">
|
||||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">Home</a></li>
|
<li><a class="flex font-medium text-md lg:text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/">Home</a></li>
|
||||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallets">Wallets</a></li>
|
<li><a class="flex font-medium text-md lg:text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallets">Wallets</a></li>
|
||||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||||
<li><a class="flex font-medium text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallet/{{ w.ticker }}">{{ w.ticker }}</a></li>
|
<li><a class="flex font-medium text-md lg:text-xs text-coolGray-500 dark:text-gray-300 hover:text-coolGray-700" href="/wallet/{{ w.ticker }}">{{ w.ticker }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="py-4">
|
<section class="py-4 px-6">
|
||||||
<div class="container px-4 mx-auto">
|
<div class="lg:container mx-auto">
|
||||||
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden"> <img class="absolute z-10 left-4 top-4 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="dots-red"> <img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="wave">
|
<div class="relative py-11 px-16 bg-coolGray-900 dark:bg-blue-500 rounded-md overflow-hidden"> <img class="absolute z-10 left-4 top-4 right-4 bottom-4" src="/static/images/elements/dots-red.svg" alt="dots-red"> <img class="absolute h-64 left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 object-cover" src="/static/images/elements/wave.svg" alt="wave">
|
||||||
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
<div class="relative z-20 flex flex-wrap items-center -m-3">
|
||||||
<div class="w-full md:w-1/2">
|
<div class="w-full md:w-1/2">
|
||||||
<h2 class="text-3xl font-bold text-white"> <span class="inline-block align-middle"><img class="mr-2 h-16" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"></span>({{ w.ticker }}) {{ w.name }} Wallet </h2>
|
<h2 class="text-3xl font-bold text-white"> <span class="inline-block align-middle"><img class="mr-2 h-16" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"></span>({{ w.ticker }}) {{ w.name }} Wallet </h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto"> <a class="rounded-full mr-5 flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="refresh" href="/wallet/{{ w.ticker }}"> {{ circular_arrows_svg | safe }}<span>Refresh</span> </a> </div>
|
<div class="w-full md:w-1/2 p-3 p-6 container flex flex-wrap items-center justify-end items-center mx-auto"> <a class="rounded-full mr-5 flex flex-wrap justify-center px-5 py-3 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border dark:bg-gray-500 dark:hover:bg-gray-700 border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="refresh" href="/wallet/{{ w.ticker }}"> {{ circular_arrows_svg | safe }}<span>Refresh</span> </a> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% include 'inc_messages.html' %}
|
{% include 'inc_messages.html' %}
|
||||||
{% if w.updating %}
|
{% if w.updating %}
|
||||||
<section class="py-4" id="messages_updating" role="alert">
|
<section class="py-4 px-6" id="messages_updating" role="alert">
|
||||||
<div class="container px-4 mx-auto">
|
<div class="lg:container mx-auto">
|
||||||
<div class="p-6 text-green-800 rounded-lg bg-blue-50 border border-blue-500 dark:bg-gray-500 dark:text-blue-400 rounded-md">
|
<div class="p-6 text-green-800 rounded-lg bg-blue-50 border border-blue-500 dark:bg-gray-500 dark:text-blue-400 rounded-md">
|
||||||
<div class="flex flex-wrap justify-between items-center -m-2">
|
<div class="flex flex-wrap justify-between items-center -m-2">
|
||||||
<div class="flex-1 p-2">
|
<div class="flex-1 p-2">
|
||||||
@@ -39,8 +40,8 @@
|
|||||||
{{ circular_update_messages_svg | safe }}
|
{{ circular_update_messages_svg | safe }}
|
||||||
</div>
|
</div>
|
||||||
<ul class="ml-4 mt-1">
|
<ul class="ml-4 mt-1">
|
||||||
<li class="font-semibold text-sm text-blue-500 error_msg"><span class="bold">UPDATING:</span></li>
|
<li class="font-semibold text-lg lg:text-sm text-blue-500 error_msg"><span class="bold">UPDATING:</span></li>
|
||||||
<li class="font-medium text-sm text-blue-500">Please wait...</li>
|
<li class="font-medium text-lg lg:text-sm text-blue-500">Please wait...</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -56,8 +57,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if w.havedata %}
|
{% if w.havedata %}
|
||||||
{% if w.error %}
|
{% if w.error %}
|
||||||
<section class="py-4" id="messages_error" role="alert">
|
<section class="py-4 px-6" id="messages_error" role="alert">
|
||||||
<div class="container px-4 mx-auto">
|
<div class="lg:container mx-auto">
|
||||||
<div class="p-6 text-green-800 rounded-lg bg-red-50 border border-red-400 dark:bg-gray-500 dark:text-red-400 rounded-md">
|
<div class="p-6 text-green-800 rounded-lg bg-red-50 border border-red-400 dark:bg-gray-500 dark:text-red-400 rounded-md">
|
||||||
<div class="flex flex-wrap justify-between items-center -m-2">
|
<div class="flex flex-wrap justify-between items-center -m-2">
|
||||||
<div class="flex-1 p-2">
|
<div class="flex-1 p-2">
|
||||||
@@ -66,8 +67,8 @@
|
|||||||
{{ circular_error_messages_svg | safe }}
|
{{ circular_error_messages_svg | safe }}
|
||||||
</div>
|
</div>
|
||||||
<ul class="ml-4 mt-1">
|
<ul class="ml-4 mt-1">
|
||||||
<li class="font-semibold text-sm text-red-500 error_msg"><span class="bold">ERROR:</span></li>
|
<li class="font-semibold text-lg lg:text-sm text-red-500 error_msg"><span class="bold">ERROR:</span></li>
|
||||||
<li class="font-medium text-sm text-red-500 error_msg">{{ w.error }}</li>
|
<li class="font-medium text-lg lg:text-sm text-red-500 error_msg">{{ w.error }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -82,24 +83,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% else %}
|
{% else %}
|
||||||
<form method="post" autocomplete="off">
|
{% if w.cid == '18' %} {# DOGE #}
|
||||||
<section>
|
<section class="py-4 px-6" id="messages_notice">
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
<div class="lg:container mx-auto">
|
||||||
|
<div class="p-6 rounded-lg bg-coolGray-100 dark:bg-gray-500 shadow-sm">
|
||||||
|
<div class="flex items-start">
|
||||||
|
<svg class="w-6 h-6 text-blue-500 mt-1 mr-3 flex-shrink-0" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"></path></svg>
|
||||||
|
<div class="flex flex-wrap -m-1">
|
||||||
|
<ul class="ml-4">
|
||||||
|
<li class="font-semibold text-lg dark:text-white mb-2">NOTICE:</li>
|
||||||
|
<li class="font-medium text-gray-600 dark:text-white leading-relaxed">
|
||||||
|
This version of DOGE Core is experimental and has been custom-built for compatibility with BasicSwap. As a result, it may not always be fully aligned with upstream changes, features unrelated to BasicSwap might not work as expected, and its code may differ from the official release.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
<section>
|
||||||
|
<form method="post" autocomplete="off">
|
||||||
|
<div class="px-6 py-0 mt-5 h-full overflow-hidden">
|
||||||
<div class="pb-6 border-coolGray-100">
|
<div class="pb-6 border-coolGray-100">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||||
<div class="w-full pt-2">
|
<div class="w-full pt-2">
|
||||||
<div class="container mt-5 mx-auto">
|
<div class="lg:container mt-5 mx-auto">
|
||||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
<div class="px-6">
|
<div class="px-6">
|
||||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
<div class="w-full pb-6 overflow-x-auto">
|
||||||
<table class="w-full min-w-max text-sm">
|
<table class="w-full text-lg lg:text-sm">
|
||||||
<thead class="uppercase">
|
<thead class="uppercase">
|
||||||
<tr class="text-left">
|
<tr class="text-left">
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Wallet</span> </div>
|
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-md lg:text-xs text-gray-600 dark:text-gray-300 font-semibold">Wallet</span> </div>
|
||||||
</th>
|
</th>
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span> </div>
|
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-md lg:text-xs text-gray-600 dark:text-gray-300 font-semibold">Details</span> </div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -110,7 +130,8 @@
|
|||||||
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.pending }} {{ w.ticker }} </span>
|
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.pending }} {{ w.ticker }} </span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr> {% if w.cid == '1' %} {# PART #}
|
</tr>
|
||||||
|
{% if w.cid == '1' %} {# PART #}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} Blind"> </span>Blind Balance: </td>
|
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} Blind"> </span>Blind Balance: </td>
|
||||||
<td class="py-3 px-6 bold coinname-value" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }} (<span class="usd-value"></span>)
|
<td class="py-3 px-6 bold coinname-value" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }} (<span class="usd-value"></span>)
|
||||||
@@ -128,11 +149,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td class="usd-value"></td>
|
<td class="usd-value"></td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
|
||||||
{# / PART #}
|
{# / PART #}
|
||||||
{% if w.cid == '3' %} {# LTC #}
|
{% elif w.cid == '3' %} {# LTC #}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold"> <span class="pr-3 inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} MWEB"> </span>MWEB Balance: </td>
|
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} MWEB"> </span>MWEB Balance: </td>
|
||||||
<td class="py-3 px-6 bold coinname-value" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }} (<span class="usd-value"></span>)
|
<td class="py-3 px-6 bold coinname-value" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }} (<span class="usd-value"></span>)
|
||||||
{% if w.mweb_pending %}
|
{% if w.mweb_pending %}
|
||||||
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.mweb_pending }} {{ w.ticker }} </span>
|
<span class="inline-block py-1 px-2 rounded-full bg-green-100 text-green-500 dark:bg-gray-500 dark:text-green-500">Pending: +{{ w.mweb_pending }} {{ w.ticker }} </span>
|
||||||
@@ -144,7 +164,7 @@
|
|||||||
{% if w.locked_utxos %}
|
{% if w.locked_utxos %}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Locked Outputs:</td>
|
<td class="py-3 px-6 bold">Locked Outputs:</td>
|
||||||
<td id='locked_utxos' class="py-3 px-6">{{ w.locked_utxos }}</td>
|
<td id="locked_utxos" class="py-3 px-6">{{ w.locked_utxos }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# / locked_utxos #}
|
{# / locked_utxos #}
|
||||||
@@ -157,7 +177,7 @@
|
|||||||
<td class="py-3 px-6">{{ w.version }}</td>
|
<td class="py-3 px-6">{{ w.version }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Blocks:</td>
|
<td class="py-3 px-6 bold">Blockheight:</td>
|
||||||
<td class="py-3 px-6">{{ w.blocks }}
|
<td class="py-3 px-6">{{ w.blocks }}
|
||||||
{% if w.known_block_count %} / {{ w.known_block_count }}
|
{% if w.known_block_count %} / {{ w.known_block_count }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -187,10 +207,12 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# / encrypted #}
|
{# / encrypted #}
|
||||||
|
{% if w.expected_seed != true %}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold">Expected Seed:</td>
|
<td class="py-3 px-6 bold">Expected Seed:</td>
|
||||||
<td class="py-3 px-6">{{ w.expected_seed }}</td> {% if block_unknown_seeds and w.expected_seed != true %} {# Only show addresses if wallet seed is correct #}
|
<td class="py-3 px-6">{{ w.expected_seed }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -201,16 +223,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
{% if block_unknown_seeds and w.expected_seed != true %} {# Only show addresses if wallet seed is correct #}
|
||||||
|
<section class="px-6 py-0 h-full overflow-hidden">
|
||||||
<div class="pb-6 border-coolGray-100">
|
<div class="pb-6 border-coolGray-100">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||||
<div class="w-full pt-2">
|
<div class="w-full pt-2">
|
||||||
<div class="container mt-5 mx-auto">
|
<div class="lg:container mt-5 mx-auto">
|
||||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
<div class="py-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
<div class="px-6">
|
<div class="px-6">
|
||||||
{% if w.cid != '4' %} {# DCR #}
|
{% if w.cid != '4' %} {# DCR #}
|
||||||
<div class="flex flex-wrap justify-end">
|
<div class="flex flex-wrap justify-end">
|
||||||
<div class="w-full md:w-auto p-1.5"> <input class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-sm text-white hover:text-red border border-red-500 hover:border-red-500 hover:bg-red-600 bg-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none cursor-pointer" type="submit" name="reseed_{{ w.cid }}" value="Reseed wallet" onclick="return confirmReseed();"> </div>
|
<div class="w-full md:w-auto p-1.5"> <input class="flex flex-wrap justify-center w-full px-4 py-2.5 font-medium text-lg lg:text-sm text-white hover:text-red border border-red-500 hover:border-red-500 hover:bg-red-600 bg-red-500 rounded-md shadow-button focus:ring-0 focus:outline-none cursor-pointer" type="submit" name="reseed_{{ w.cid }}" value="Reseed wallet" onclick="return confirmReseed();"> </div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@@ -220,127 +243,57 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% else %}
|
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||||
</table>
|
</form>
|
||||||
</div>
|
{% else %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<section class="p-6">
|
<section class="p-6">
|
||||||
|
<div class="lg:container mx-auto">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h4 class="font-semibold text-2xl text-black dark:text-white">Deposit</h4>
|
<h4 class="font-semibold text-2xl text-black dark:text-white">Deposit</h4>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<form method="post" autocomplete="off">
|
<form method="post" autocomplete="off">
|
||||||
<section>
|
<section>
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 overflow-hidden">
|
<div class="px-6 py-0 overflow-hidden">
|
||||||
<div class="pb-6 border-coolGray-100">
|
<div class="pb-6 border-coolGray-100">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||||
<div class="w-full pt-2">
|
<div class="w-full pt-2">
|
||||||
<div class="container mt-5 mx-auto">
|
<div class="lg:container mt-5 mx-auto">
|
||||||
<div class="pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
<div class="pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
<div class="px-6">
|
<div class="px-6">
|
||||||
<div class="container mx-auto">
|
<div class="flex flex-wrap max-w-7xl mx-auto justify-center -m-3">
|
||||||
<div class="flex flex-wrap max-w-7xl mx-auto -m-3">
|
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||||
{% if w.cid == '1' %} {# PART #}
|
<div class="h-full">
|
||||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
<div class="flex flex-wrap -m-3">
|
||||||
<div class="h-full">
|
<div class="w-full p-3">
|
||||||
<div class="flex flex-wrap -m-3">
|
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
||||||
<div class="w-full p-3">
|
<div class="qrcode-border flex">
|
||||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
{% if w.cid in '6, 9' %}
|
||||||
<div class="qrcode-border flex">
|
{# XMR | WOW #}
|
||||||
<div id="qrcode-stealth" class="qrcode"> </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} Stealth Address: </div>
|
|
||||||
<div class="relative flex justify-center items-center">
|
|
||||||
<div data-tooltip-target="tooltip-copy-particl-stealth" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-10 focus:ring-0" id="stealth_address"> {{ w.stealth_address }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="tooltip-copy-particl-stealth" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
|
||||||
<p>Copy to clipboard</p>
|
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{# / PART #}
|
|
||||||
{% if w.cid in '6, 9' %}
|
|
||||||
{# XMR | WOW #}
|
|
||||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
|
||||||
<div class="h-full">
|
|
||||||
<div class="flex flex-wrap -m-3">
|
|
||||||
<div class="w-full p-3">
|
|
||||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
|
||||||
<div class="qrcode-border flex">
|
|
||||||
<div id="qrcode-monero-main" class="qrcode"></div>
|
<div id="qrcode-monero-main" class="qrcode"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} Main Address: </div>
|
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">Main Address: </div>
|
||||||
<div class="relative flex justify-center items-center">
|
<div class="relative flex justify-center items-center">
|
||||||
<div data-tooltip-target="tooltip-copy-monero-main" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="monero_main_address">{{ w.main_address }}</div>
|
<div data-tooltip-target="tooltip-copy-monero-main" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="monero_main_address">{{ w.main_address }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="tooltip-copy-monero-main" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
<div id="tooltip-copy-monero-main" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||||
<p>Copy to clipboard</p>
|
{% else %}
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
|
||||||
<div class="h-full">
|
|
||||||
<div class="flex flex-wrap -m-3">
|
|
||||||
<div class="w-full p-3">
|
|
||||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
|
||||||
<div class="qrcode-border flex">
|
|
||||||
<div id="qrcode-monero-sub" class="qrcode"> </div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} Sub Address: </div>
|
|
||||||
<div class="relative flex justify-center items-center">
|
|
||||||
<div data-tooltip-target="tooltip-copy-monero-sub" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="monero_sub_address">{{ w.deposit_address }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
|
||||||
<div class="py-3 px-6 bold mt-5">
|
|
||||||
<button type="submit" class="flex justify-center py-2 px-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="newaddr_{{ w.cid }}" value="New Subaddress"> {{ circular_arrows_svg }} New {{ w.name }} Deposit Address</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="tooltip-copy-monero-sub" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
|
||||||
<p>Copy to clipboard</p>
|
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
|
||||||
<div class="h-full">
|
|
||||||
<div class="flex flex-wrap -m-3">
|
|
||||||
<div class="w-full p-3">
|
|
||||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
|
||||||
<div class="qrcode-border flex">
|
|
||||||
<div id="qrcode-deposit" class="qrcode"> </div>
|
<div id="qrcode-deposit" class="qrcode"> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} Deposit Address: </div>
|
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">Deposit Address: </div>
|
||||||
<div class="relative flex justify-center items-center">
|
<div class="relative flex justify-center items-center">
|
||||||
<div data-tooltip-target="tooltip-copy-default" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="main_deposit_address">{{ w.deposit_address }}</div>
|
<div data-tooltip-target="tooltip-copy-default" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="main_deposit_address">{{ w.deposit_address }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
||||||
<div class="py-3 px-6 bold mt-5">
|
<div class="py-3 px-6 bold mt-5">
|
||||||
<button type="submit" class="flex justify-center py-2 px-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="newaddr_{{ w.cid }}" value="New Deposit Address"> {{ circular_arrows_svg }} New {{ w.name }} Deposit Address </button>
|
<button type="submit" class="flex justify-center py-2 px-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="newaddr_{{ w.cid }}" value="New Deposit Address"> {{ circular_arrows_svg }} New {{ w.name }} Deposit Address </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="tooltip-copy-default" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
<div id="tooltip-copy-default" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||||
|
{% endif %}
|
||||||
<p>Copy to clipboard</p>
|
<p>Copy to clipboard</p>
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
</div>
|
</div>
|
||||||
@@ -348,38 +301,67 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% if w.cid in '1, 3, 6, 9' %}
|
||||||
{% if w.cid == '3' %}
|
{# PART | LTC | XMR | WOW | #}
|
||||||
{# LTC #}
|
|
||||||
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
<div class="w-full md:w-1/2 p-3 flex justify-center items-center">
|
||||||
<div class="h-full">
|
<div class="h-full">
|
||||||
<div class="flex flex-wrap -m-3">
|
<div class="flex flex-wrap -m-3">
|
||||||
<div class="w-full p-3">
|
<div class="w-full p-3">
|
||||||
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
<div class="mb-2 qrcode-container flex h-60 justify-center items-center">
|
||||||
<div class="qrcode-border flex">
|
<div class="qrcode-border flex">
|
||||||
|
{% if w.cid in '6, 9' %}
|
||||||
|
{# XMR | WOW #}
|
||||||
|
<div id="qrcode-monero-sub" class="qrcode"> </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">Subaddress: </div>
|
||||||
|
<div class="relative flex justify-center items-center">
|
||||||
|
<div data-tooltip-target="tooltip-copy-monero-sub" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full focus:ring-0" id="monero_sub_address">{{ w.deposit_address }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
||||||
|
<div class="py-3 px-6 bold mt-5">
|
||||||
|
<button type="submit" class="flex justify-center py-2 px-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="newaddr_{{ w.cid }}" value="New Subaddress"> {{ circular_arrows_svg }} New {{ w.name }} Deposit Address</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="tooltip-copy-monero-sub" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||||
|
{% elif w.cid == '1' %}
|
||||||
|
{# PART #}
|
||||||
|
<div id="qrcode-stealth" class="qrcode"> </div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">Stealth Address: </div>
|
||||||
|
<div class="relative flex justify-center items-center">
|
||||||
|
<div data-tooltip-target="tooltip-copy-particl-stealth" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-10 focus:ring-0" id="stealth_address"> {{ w.stealth_address }}
|
||||||
|
</div>
|
||||||
|
<div id="tooltip-copy-particl-stealth" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||||
|
{# / PART #}
|
||||||
|
{% elif w.cid == '3' %}
|
||||||
|
{# LTC #}
|
||||||
<div id="qrcode-mweb" class="qrcode"> </div>
|
<div id="qrcode-mweb" class="qrcode"> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">{{ w.name }} MWEB Address: </div>
|
<div class="font-normal bold text-gray-500 text-center dark:text-white mb-5">MWEB Address: </div>
|
||||||
<div class="text-center relative">
|
<div class="text-center relative">
|
||||||
<div data-tooltip-target="tooltip-copy-litecoin-mweb" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="stealth_address">{{ w.mweb_address }}</div>
|
<div data-tooltip-target="tooltip-copy-litecoin-mweb" class="input-like-container hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="stealth_address">{{ w.mweb_address }}</div>
|
||||||
<span class="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer" id="copyIcon"></span>
|
<span class="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer" id="copyIcon"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
<div class="opacity-100 text-gray-500 dark:text-gray-100 flex justify-center items-center">
|
||||||
<div class="py-3 px-6 bold mt-5">
|
<div class="py-3 px-6 bold mt-5">
|
||||||
<button type="submit" class="flex justify-center py-2 px-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="newmwebaddr_{{ w.cid }}" value="New MWEB Address"> {{ circular_arrows_svg }} New MWEB Address </button>
|
<button type="submit" class="flex justify-center py-2 px-4 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="newmwebaddr_{{ w.cid }}" value="New MWEB Address"> {{ circular_arrows_svg }} New MWEB Address </button>
|
||||||
</div>
|
</div>
|
||||||
<div id="tooltip-copy-litecoin-mweb" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
</div>
|
||||||
|
<div id="tooltip-copy-litecoin-mweb" role="tooltip" class="inline-block absolute invisible z-10 py-2 px-3 text-lg lg:text-sm font-medium text-white bg-blue-500 rounded-lg shadow-sm opacity-0 transition-opacity duration-300 tooltip">
|
||||||
|
{# / LTC #}
|
||||||
|
{% endif %}
|
||||||
<p>Copy to clipboard</p>
|
<p>Copy to clipboard</p>
|
||||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
</div>
|
||||||
{# / LTC #}
|
{% endif %}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -401,8 +383,7 @@
|
|||||||
correctLevel: QRCode.CorrectLevel.L
|
correctLevel: QRCode.CorrectLevel.L
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% elif w.cid == '3' %}
|
||||||
{% if w.cid == '3' %}
|
|
||||||
{# LTC #}
|
{# LTC #}
|
||||||
<script>
|
<script>
|
||||||
// Litecoin MWEB
|
// Litecoin MWEB
|
||||||
@@ -520,61 +501,67 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<section class="p-6">
|
<section class="p-6">
|
||||||
|
<div class="lg:container mx-auto">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h4 class="font-semibold text-2xl text-black dark:text-white">Withdraw</h4>
|
<h4 class="font-semibold text-2xl text-black dark:text-white">Withdraw</h4>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</div>
|
||||||
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
<div class="px-6 py-0 h-full overflow-hidden">
|
||||||
<div class="border-coolGray-100">
|
<div class="border-coolGray-100">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||||
<div class="w-full pt-2">
|
<div class="w-full pt-2">
|
||||||
<div class="container mt-5 mx-auto">
|
<div class="lg:container mt-5 mx-auto">
|
||||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
<div class="py-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
<div class="px-6">
|
<div class="px-6">
|
||||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
<div class="w-full pb-6 overflow-x-auto">
|
||||||
<table class="w-full min-w-max text-sm">
|
<table class="w-full text-lg lg:text-sm">
|
||||||
<thead class="uppercase">
|
<thead class="uppercase">
|
||||||
<tr class="text-left">
|
<tr class="text-left">
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span> </div>
|
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-md lg:text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span> </div>
|
||||||
</th>
|
</th>
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Input</span> </div>
|
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-md lg:text-xs text-gray-600 dark:text-gray-300 font-semibold">Input</span> </div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span> {{ w.name }} Balance: </td>
|
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Balance: </td>
|
||||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.balance }} {{ w.ticker }} </td>
|
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.balance }} {{ w.ticker }} </td>
|
||||||
|
</tr>
|
||||||
{% if w.cid == '3' %}
|
{% if w.cid == '3' %}
|
||||||
{# LTC #}
|
{# LTC #}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-4 pl-6 bold w-1/4"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span> {{ w.name }} MWEB Balance: </td>
|
<td class="py-4 pl-6 bold w-1/4"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>MWEB Balance: </td>
|
||||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }} </td>
|
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.mweb_balance }} {{ w.ticker }} </td>
|
||||||
{% endif %}
|
</tr>
|
||||||
{% if w.cid == '1' %}
|
{% elif w.cid == '1' %}
|
||||||
{# PART #}
|
{# PART #}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span> {{ w.name }} Blind Balance: </td>
|
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Blind Balance: </td>
|
||||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }} </td>
|
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.blind_balance }} {{ w.ticker }} </td>
|
||||||
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span> {{ w.name }} Anon Balance: </td>
|
<td class="py-4 pl-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }}"> </span>Anon Balance: </td>
|
||||||
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }} </td> {% endif %}
|
<td class="py-3 px-6" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }} </td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-4 pl-6 bold"> {{ w.name }} Address: </td>
|
<td class="py-4 pl-6 bold"> {{ w.name }} Address: </td>
|
||||||
<td class="py-3 px-6"> <input placeholder="{{ w.ticker }} Address" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="to_{{ w.cid }}" value="{{ w.wd_address }}"> </td>
|
<td class="py-3 px-6"> <input placeholder="{{ w.ticker }} Address" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="to_{{ w.cid }}" value="{{ w.wd_address }}"> </td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-4 pl-6 bold"> {{ w.name }} Amount:
|
<td class="py-4 pl-6 bold"> {{ w.name }} Amount:
|
||||||
<td class="py-3 px-6">
|
<td class="py-3 px-6">
|
||||||
<div class="flex"> <input placeholder="{{ w.ticker }} Amount" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" id="amount" name="amt_{{ w.cid }}" value="{{ w.wd_value }}">
|
<div class="flex"> <input placeholder="{{ w.ticker }} Amount" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" id="amount" name="amt_{{ w.cid }}" value="{{ w.wd_value }}">
|
||||||
<div class="ml-2 flex">
|
<div class="ml-2 flex">
|
||||||
{% if w.cid == '1' %}
|
{% if w.cid == '1' %}
|
||||||
{# PART #}
|
{# PART #}
|
||||||
<button type="button" class="py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">25%</button>
|
<button type="button" class="hidden md:block py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">25%</button>
|
||||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">50%</button>
|
<button type="button" class="hidden md:block ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">50%</button>
|
||||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">100%</button>
|
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.blind_balance }}', '{{ w.anon_balance }}')">100%</button>
|
||||||
<script>
|
<script>
|
||||||
function setAmount(percent, balance, cid, blindBalance, anonBalance) {
|
function setAmount(percent, balance, cid, blindBalance, anonBalance) {
|
||||||
var amountInput = document.getElementById('amount');
|
var amountInput = document.getElementById('amount');
|
||||||
@@ -609,9 +596,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
{# / PART #}
|
{# / PART #}
|
||||||
{% elif w.cid == '3' %}
|
{% elif w.cid == '3' %}
|
||||||
{# LTC #}
|
{# LTC #}
|
||||||
<button type="button" class="py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">25%</button>
|
<button type="button" class="hidden md:block py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">25%</button>
|
||||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">50%</button>
|
<button type="button" class="hidden md:block ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">50%</button>
|
||||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">100%</button>
|
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">100%</button>
|
||||||
<script>
|
<script>
|
||||||
function setAmount(percent, balance, cid, mwebBalance) {
|
function setAmount(percent, balance, cid, mwebBalance) {
|
||||||
var amountInput = document.getElementById('amount');
|
var amountInput = document.getElementById('amount');
|
||||||
@@ -642,9 +629,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</script>
|
</script>
|
||||||
{# / LTC #}
|
{# / LTC #}
|
||||||
{% else %}
|
{% else %}
|
||||||
<button type="button" class="py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }})">25%</button>
|
<button type="button" class="hidden md:block py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.25, '{{ w.balance }}', {{ w.cid }})">25%</button>
|
||||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }})">50%</button>
|
<button type="button" class="hidden md:block ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(0.5, '{{ w.balance }}', {{ w.cid }})">50%</button>
|
||||||
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }})">100%</button>
|
<button type="button" class="ml-2 py-1 px-2 bg-blue-500 text-white text-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }})">100%</button>
|
||||||
<script>
|
<script>
|
||||||
function setAmount(percent, balance, cid) {
|
function setAmount(percent, balance, cid) {
|
||||||
var amountInput = document.getElementById('amount');
|
var amountInput = document.getElementById('amount');
|
||||||
@@ -691,19 +678,21 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
{% if w.cid in '6, 9' %} {# XMR | WOW #}
|
{% if w.cid in '6, 9' %} {# XMR | WOW #}
|
||||||
<td class="py-3 px-6 bold">Sweep All:</td>
|
<td class="hidden py-3 px-6 bold">Sweep All:</td>
|
||||||
<td class="py-3 px-6">
|
<td class="hidden py-3 px-6">
|
||||||
<input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" id="sweepall" name="sweepall_{{ w.cid }}" {% if w.wd_sweepall==true %} checked=checked{% endif %}>
|
<input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" id="sweepall" name="sweepall_{{ w.cid }}" {% if w.wd_sweepall==true %} checked="checked"{% endif %}>
|
||||||
</td>
|
</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="py-3 px-6 bold">Subtract Fee:</td>
|
<td class="py-3 px-6 bold">Subtract Fee:</td>
|
||||||
<td class="py-3 px-6">
|
<td class="py-3 px-6">
|
||||||
<input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" name="subfee_{{ w.cid }}" {% if w.wd_subfee==true %} checked=checked{% endif %}>
|
<input class="hover:border-blue-500 w-5 h-5 form-check-input text-blue-600 bg-gray-50 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-1 dark:bg-gray-500 dark:border-gray-400" type="checkbox" name="subfee_{{ w.cid }}" {% if w.wd_subfee==true %} checked="checked"{% endif %}>
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td>
|
<td>
|
||||||
@@ -737,9 +726,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
|
||||||
{# / PART #}
|
{# / PART #}
|
||||||
{% if w.cid == '3' %} {# LTC #}
|
{% elif w.cid == '3' %} {# LTC #}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-3 px-6 bold">Type From:</td>
|
<td class="py-3 px-6 bold">Type From:</td>
|
||||||
<td class="py-3 px-6">
|
<td class="py-3 px-6">
|
||||||
@@ -753,14 +741,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# / LTC #}
|
{# / LTC #}
|
||||||
|
{% if w.cid not in '6,9' %} {# Not XMR WOW #}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-3 px-6 bold">Fee Rate:</td>
|
<td class="py-3 px-6 bold">Fee Rate:</td>
|
||||||
<td class="py-3 px-6">{{ w.fee_rate }}</td>
|
<td class="py-3 px-6">{{ w.fee_rate }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-3 px-6 bold">Estimate Fee:</td>
|
<td class="py-3 px-6 bold">Fee Estimate:</td>
|
||||||
<td class="py-3 px-6"> {{ w.est_fee }} </td>
|
<td class="py-3 px-6"> {{ w.est_fee }} </td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -772,25 +762,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
<div class="px-6 py-0 h-full overflow-hidden">
|
||||||
<div class="pb-6 ">
|
<div class="pb-6 ">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||||
<div class="w-full pt-2">
|
<div class="w-full pt-2">
|
||||||
<div class="container mx-auto">
|
<div class="lg:container mx-auto">
|
||||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
<div class="py-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||||
<div class="px-6">
|
<div class="px-6">
|
||||||
<div class="flex flex-wrap justify-end">
|
<div class="flex flex-wrap justify-end">
|
||||||
{% if w.cid not in '6, 9' %}
|
|
||||||
{# !XMR | WOW #}
|
|
||||||
{% if w.show_utxo_groups %}
|
|
||||||
{% else %}
|
|
||||||
<div class="w-full md:w-auto p-1.5"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="showutxogroups" name="showutxogroups" value="Show UTXO Groups"> {{ utxo_groups_svg | safe }} Show UTXO Groups </button> </div>
|
|
||||||
{% endif %} {% endif %}
|
|
||||||
{% if w.cid in '6, 9' %}
|
{% if w.cid in '6, 9' %}
|
||||||
{# XMR | WOW #}
|
{# XMR | WOW #}
|
||||||
<div class="w-full md:w-auto p-1.5 ml-2"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="estfee_{{ w.cid }}" value="Estimate Fee">Estimate {{ w.ticker }} Fee </button> </div>
|
<div class="w-full md:w-auto p-1.5 mx-1"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="estfee_{{ w.cid }}" value="Estimate Fee">Estimate {{ w.ticker }} Fee </button> </div>
|
||||||
|
{# / XMR | WOW #}
|
||||||
|
{% elif w.show_utxo_groups %}
|
||||||
|
{% else %}
|
||||||
|
<div class="w-full md:w-auto p-1.5 mx-1"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="showutxogroups" name="showutxogroups" value="Show UTXO Groups"> {{ utxo_groups_svg | safe }} Show UTXO Groups </button> </div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{# / XMR | WOW #} <div class="w-full md:w-auto p-1.5 ml-2"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="withdraw_{{ w.cid }}" value="Withdraw" onclick="return confirmWithdrawal();">{{ withdraw_svg | safe }} Withdraw {{ w.ticker }} </div>
|
<div class="w-full md:w-auto p-1.5 mx-1"> <button type="submit" class="flex flex-wrap justify-center w-full px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" name="withdraw_{{ w.cid }}" value="Withdraw" onclick="return confirmWithdrawal();">{{ withdraw_svg | safe }} Withdraw {{ w.ticker }} </button></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -798,42 +786,43 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{% if w.cid not in '6, 9' %}
|
|
||||||
{# !XMR | WOW #}
|
|
||||||
{% if w.show_utxo_groups %}
|
{% if w.show_utxo_groups %}
|
||||||
<section class="p-6">
|
<section class="p-6">
|
||||||
|
<div class="lg:container mx-auto">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<h4 class="font-semibold text-2xl text-black dark:text-white">UTXO Groups</h4>
|
<h4 class="font-semibold text-2xl text-black dark:text-white">UTXO Groups</h4>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden">
|
<div class="px-6 py-0 h-full overflow-hidden">
|
||||||
<div class="border-coolGray-100">
|
<div class="border-coolGray-100">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||||
<div class="w-full pt-2">
|
<div class="w-full pt-2">
|
||||||
<div class="container mt-5 mx-auto">
|
<div class="lg:container mt-5 mx-auto">
|
||||||
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
<div class="pt-6 pb-8 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||||
<div class="px-6">
|
<div class="px-6">
|
||||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
<div class="w-full pb-6 overflow-x-auto">
|
||||||
<table class="w-full min-w-max text-sm">
|
<table class="w-full text-lg lg:text-sm">
|
||||||
<thead class="uppercase">
|
<thead class="uppercase">
|
||||||
<tr class="text-left">
|
<tr class="text-left">
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span> </div>
|
<div class="py-3 px-6 rounded-tl-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-md lg:text-xs text-gray-600 dark:text-gray-300 font-semibold">Options</span> </div>
|
||||||
</th>
|
</th>
|
||||||
<th class="p-0">
|
<th class="p-0">
|
||||||
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-xs text-gray-600 dark:text-gray-300 font-semibold p-10"></span> </div>
|
<div class="py-3 px-6 rounded-tr-xl bg-coolGray-200 dark:bg-gray-600"> <span class="text-md lg:text-xs text-gray-600 dark:text-gray-300 font-semibold p-10"></span> </div>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-3 px-6 w-1/4 bold">UTXO Groups:</td>
|
<td class="py-3 px-6 w-1/4 bold">UTXO Groups:</td>
|
||||||
<td class="py-3 px-6"> <textarea class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="tx_view" rows="10" readonly>{{ w.utxo_groups }} </textarea> </td>
|
<td class="py-3 px-6"> <textarea class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-50 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" id="tx_view" rows="10" readonly>{{ w.utxo_groups }} </textarea> </td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||||
<td class="py-3 px-6"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="create_utxo" name="create_utxo" value="Create UTXO" onclick="return confirmUTXOResize();"> {{ create_utxo_svg | safe }}Create UTXO </button> </td>
|
<td class="py-3 px-6"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="create_utxo" name="create_utxo" value="Create UTXO" onclick="return confirmUTXOResize();"> {{ create_utxo_svg | safe }}Create UTXO </button> </td>
|
||||||
<td class="py-3 px-6"> <input placeholder="Amount" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="utxo_value" value="{{ w.utxo_value }}"> </td>
|
<td class="py-3 px-6"> <input placeholder="Amount" class="hover:border-blue-500 bg-gray-50 text-gray-900 appearance-none pr-10 dark:bg-gray-500 dark:text-white border border-gray-300 dark:border-gray-400 dark:text-gray-50 dark:placeholder-gray-400 text-lg lg:text-sm rounded-lg outline-none focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 focus:ring-0" type="text" name="utxo_value" value="{{ w.utxo_value }}"> </td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -846,14 +835,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<div class="pl-6 pr-6 pt-0 pb-0 h-full overflow-hidden ">
|
<div class="px-6 py-0 h-full overflow-hidden ">
|
||||||
<div class="pb-6 ">
|
<div class="pb-6 ">
|
||||||
<div class="flex flex-wrap items-center justify-between -m-2">
|
<div class="flex flex-wrap items-center justify-between -m-2">
|
||||||
<div class="w-full pt-2">
|
<div class="w-full pt-2">
|
||||||
<div class="container mx-auto">
|
<div class="lg:container mx-auto">
|
||||||
<div class="pt-6 pb-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
<div class="py-6 bg-coolGray-100 border-t border-gray-100 dark:border-gray-400 dark:bg-gray-500 rounded-bl-xl rounded-br-xl">
|
||||||
<div class="px-6">
|
<div class="px-6">
|
||||||
<div class="flex flex-wrap justify-end"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="closeutxogroups" name="closeutxogroups" value="Close UTXO Groups"> {{ utxo_groups_svg | safe }} Close UTXO Groups </button> </div>
|
<div class="flex flex-wrap justify-end"> <button type="submit" class="flex flex-wrap justify-center px-4 py-2.5 bg-blue-500 hover:bg-blue-600 font-medium text-lg lg:text-sm text-white border border-blue-500 rounded-md shadow-button focus:ring-0 focus:outline-none" id="closeutxogroups" name="closeutxogroups" value="Close UTXO Groups"> {{ utxo_groups_svg | safe }} Close UTXO Groups </button> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -867,7 +856,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
@@ -879,6 +867,7 @@ const coinNameToSymbol = {
|
|||||||
'Monero': 'XMR',
|
'Monero': 'XMR',
|
||||||
'Wownero': 'WOW',
|
'Wownero': 'WOW',
|
||||||
'Litecoin': 'LTC',
|
'Litecoin': 'LTC',
|
||||||
|
'Dogecoin': 'DOGE',
|
||||||
'Firo': 'FIRO',
|
'Firo': 'FIRO',
|
||||||
'Dash': 'DASH',
|
'Dash': 'DASH',
|
||||||
'PIVX': 'PIVX',
|
'PIVX': 'PIVX',
|
||||||
|
|||||||
@@ -230,6 +230,7 @@ const COIN_SYMBOLS = {
|
|||||||
'Monero': 'monero',
|
'Monero': 'monero',
|
||||||
'Wownero': 'wownero',
|
'Wownero': 'wownero',
|
||||||
'Litecoin': 'litecoin',
|
'Litecoin': 'litecoin',
|
||||||
|
'Dogecoin': 'dogecoin',
|
||||||
'Firo': 'zcoin',
|
'Firo': 'zcoin',
|
||||||
'Dash': 'dash',
|
'Dash': 'dash',
|
||||||
'PIVX': 'pivx',
|
'PIVX': 'pivx',
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ from .util import (
|
|||||||
PAGE_LIMIT,
|
PAGE_LIMIT,
|
||||||
describeBid,
|
describeBid,
|
||||||
get_data_entry,
|
get_data_entry,
|
||||||
have_data_entry,
|
|
||||||
get_data_entry_or,
|
get_data_entry_or,
|
||||||
|
have_data_entry,
|
||||||
|
listAvailableCoins,
|
||||||
listBidActions,
|
listBidActions,
|
||||||
listBidStates,
|
listBidStates,
|
||||||
listOldBidStates,
|
listOldBidStates,
|
||||||
set_pagination_filters,
|
set_pagination_filters,
|
||||||
|
setCoinFilter,
|
||||||
)
|
)
|
||||||
from basicswap.util import (
|
from basicswap.util import (
|
||||||
ensure,
|
ensure,
|
||||||
@@ -149,13 +151,12 @@ def page_bid(self, url_split, post_string):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def page_bids(
|
def page_bids(self, url_split, post_string, sent=False, available=False, received=False):
|
||||||
self, url_split, post_string, sent=False, available=False, received=False
|
|
||||||
):
|
|
||||||
server = self.server
|
server = self.server
|
||||||
swap_client = server.swap_client
|
swap_client = server.swap_client
|
||||||
swap_client.checkSystemStatus()
|
swap_client.checkSystemStatus()
|
||||||
summary = swap_client.getSummary()
|
summary = swap_client.getSummary()
|
||||||
|
filter_key = "page_available_bids" if available else "page_bids"
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
"page_no": 1,
|
"page_no": 1,
|
||||||
@@ -164,31 +165,25 @@ def page_bids(
|
|||||||
"limit": PAGE_LIMIT,
|
"limit": PAGE_LIMIT,
|
||||||
"sort_by": "created_at",
|
"sort_by": "created_at",
|
||||||
"sort_dir": "desc",
|
"sort_dir": "desc",
|
||||||
|
"coin_from": -1,
|
||||||
|
"coin_to": -1,
|
||||||
}
|
}
|
||||||
if available:
|
if available:
|
||||||
filters["bid_state_ind"] = BidStates.BID_RECEIVED
|
filters["bid_state_ind"] = BidStates.BID_RECEIVED
|
||||||
filters["with_expired"] = False
|
filters["with_expired"] = False
|
||||||
|
|
||||||
filter_prefix = (
|
|
||||||
"page_bids_sent"
|
|
||||||
if sent
|
|
||||||
else "page_bids_available" if available else "page_bids_received"
|
|
||||||
)
|
|
||||||
messages = []
|
messages = []
|
||||||
form_data = self.checkForm(post_string, "bids", messages)
|
form_data = self.checkForm(post_string, "bids", messages)
|
||||||
if form_data:
|
if form_data:
|
||||||
if have_data_entry(form_data, "clearfilters"):
|
if have_data_entry(form_data, "clearfilters"):
|
||||||
swap_client.clearFilters(filter_prefix)
|
swap_client.clearFilters(filter_key)
|
||||||
else:
|
else:
|
||||||
|
filters["coin_from"] = setCoinFilter(form_data, "coin_from")
|
||||||
|
filters["coin_to"] = setCoinFilter(form_data, "coin_to")
|
||||||
|
|
||||||
if have_data_entry(form_data, "sort_by"):
|
if have_data_entry(form_data, "sort_by"):
|
||||||
sort_by = get_data_entry(form_data, "sort_by")
|
sort_by = get_data_entry(form_data, "sort_by")
|
||||||
ensure(
|
ensure(sort_by in ["created_at"], "Invalid sort by")
|
||||||
sort_by
|
|
||||||
in [
|
|
||||||
"created_at",
|
|
||||||
],
|
|
||||||
"Invalid sort by",
|
|
||||||
)
|
|
||||||
filters["sort_by"] = sort_by
|
filters["sort_by"] = sort_by
|
||||||
if have_data_entry(form_data, "sort_dir"):
|
if have_data_entry(form_data, "sort_dir"):
|
||||||
sort_dir = get_data_entry(form_data, "sort_dir")
|
sort_dir = get_data_entry(form_data, "sort_dir")
|
||||||
@@ -199,7 +194,7 @@ def page_bids(
|
|||||||
if state_ind != -1:
|
if state_ind != -1:
|
||||||
try:
|
try:
|
||||||
_ = BidStates(state_ind)
|
_ = BidStates(state_ind)
|
||||||
except Exception as e: # noqa: F841
|
except Exception:
|
||||||
raise ValueError("Invalid state")
|
raise ValueError("Invalid state")
|
||||||
filters["bid_state_ind"] = state_ind
|
filters["bid_state_ind"] = state_ind
|
||||||
if have_data_entry(form_data, "with_expired"):
|
if have_data_entry(form_data, "with_expired"):
|
||||||
@@ -208,38 +203,64 @@ def page_bids(
|
|||||||
|
|
||||||
set_pagination_filters(form_data, filters)
|
set_pagination_filters(form_data, filters)
|
||||||
if have_data_entry(form_data, "applyfilters"):
|
if have_data_entry(form_data, "applyfilters"):
|
||||||
swap_client.setFilters(filter_prefix, filters)
|
swap_client.setFilters(filter_key, filters)
|
||||||
else:
|
else:
|
||||||
saved_filters = swap_client.getFilters(filter_prefix)
|
saved_filters = swap_client.getFilters(filter_key)
|
||||||
if saved_filters:
|
if saved_filters:
|
||||||
filters.update(saved_filters)
|
filters.update(saved_filters)
|
||||||
|
|
||||||
bids = swap_client.listBids(sent=sent, filters=filters)
|
|
||||||
|
|
||||||
page_data = {
|
page_data = {
|
||||||
"bid_states": listBidStates(),
|
"bid_states": listBidStates(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
coins_from, coins_to = listAvailableCoins(swap_client, split_from=True)
|
||||||
|
|
||||||
|
if available:
|
||||||
|
bids = swap_client.listBids(sent=False, filters=filters)
|
||||||
|
template = server.env.get_template("bids_available.html")
|
||||||
|
return self.render_template(
|
||||||
|
template,
|
||||||
|
{
|
||||||
|
"page_type_available": "Bids Available",
|
||||||
|
"page_type_available_description": "Bids available for you to accept.",
|
||||||
|
"messages": messages,
|
||||||
|
"filters": filters,
|
||||||
|
"data": page_data,
|
||||||
|
"summary": summary,
|
||||||
|
"filter_key": filter_key,
|
||||||
|
"coins_from": coins_from,
|
||||||
|
"coins": coins_to,
|
||||||
|
"bids": [
|
||||||
|
(
|
||||||
|
format_timestamp(b[0]),
|
||||||
|
b[2].hex(),
|
||||||
|
b[3].hex(),
|
||||||
|
strBidState(b[5]),
|
||||||
|
strTxState(b[7]),
|
||||||
|
strTxState(b[8]),
|
||||||
|
b[11],
|
||||||
|
)
|
||||||
|
for b in bids
|
||||||
|
],
|
||||||
|
"bids_count": len(bids),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
sent_bids = swap_client.listBids(sent=True, filters=filters)
|
||||||
|
received_bids = swap_client.listBids(sent=False, filters=filters)
|
||||||
|
|
||||||
template = server.env.get_template("bids.html")
|
template = server.env.get_template("bids.html")
|
||||||
return self.render_template(
|
return self.render_template(
|
||||||
template,
|
template,
|
||||||
{
|
{
|
||||||
"page_type_sent": "Bids Sent" if sent else "",
|
|
||||||
"page_type_available": "Bids Available" if available else "",
|
|
||||||
"page_type_received": "Received Bids" if received else "",
|
|
||||||
"page_type_sent_description": (
|
|
||||||
"All the bids you have placed on offers." if sent else ""
|
|
||||||
),
|
|
||||||
"page_type_available_description": (
|
|
||||||
"Bids available for you to accept." if available else ""
|
|
||||||
),
|
|
||||||
"page_type_received_description": (
|
|
||||||
"All the bids placed on your offers." if received else ""
|
|
||||||
),
|
|
||||||
"messages": messages,
|
"messages": messages,
|
||||||
"filters": filters,
|
"filters": filters,
|
||||||
"data": page_data,
|
"data": page_data,
|
||||||
"summary": summary,
|
"summary": summary,
|
||||||
"bids": [
|
"filter_key": filter_key,
|
||||||
|
"coins_from": coins_from,
|
||||||
|
"coins": coins_to,
|
||||||
|
"sent_bids": [
|
||||||
(
|
(
|
||||||
format_timestamp(b[0]),
|
format_timestamp(b[0]),
|
||||||
b[2].hex(),
|
b[2].hex(),
|
||||||
@@ -249,8 +270,22 @@ def page_bids(
|
|||||||
strTxState(b[8]),
|
strTxState(b[8]),
|
||||||
b[11],
|
b[11],
|
||||||
)
|
)
|
||||||
for b in bids
|
for b in sent_bids
|
||||||
],
|
],
|
||||||
"bids_count": len(bids),
|
"received_bids": [
|
||||||
|
(
|
||||||
|
format_timestamp(b[0]),
|
||||||
|
b[2].hex(),
|
||||||
|
b[3].hex(),
|
||||||
|
strBidState(b[5]),
|
||||||
|
strTxState(b[7]),
|
||||||
|
strTxState(b[8]),
|
||||||
|
b[11],
|
||||||
|
)
|
||||||
|
for b in received_bids
|
||||||
|
],
|
||||||
|
"sent_bids_count": len(sent_bids),
|
||||||
|
"received_bids_count": len(received_bids),
|
||||||
|
"bids_count": len(sent_bids) + len(received_bids),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -131,9 +131,9 @@ def parseOfferFormData(swap_client, form_data, page_data, options={}):
|
|||||||
parsed_data["amt_bid_min"] < 0
|
parsed_data["amt_bid_min"] < 0
|
||||||
or parsed_data["amt_bid_min"] > parsed_data["amt_from"]
|
or parsed_data["amt_bid_min"] > parsed_data["amt_from"]
|
||||||
):
|
):
|
||||||
errors.append("Minimum Bid Amount out of range")
|
errors.append("Minimum Purchase Quantity out of range")
|
||||||
except Exception:
|
except Exception:
|
||||||
errors.append("Minimum Bid Amount")
|
errors.append("Minimum Purchase Quantity")
|
||||||
|
|
||||||
if have_data_entry(form_data, "rate") and not have_data_entry(form_data, "amt_to"):
|
if have_data_entry(form_data, "rate") and not have_data_entry(form_data, "amt_to"):
|
||||||
parsed_data["rate"] = ci_to.make_int(form_data["rate"], r=1)
|
parsed_data["rate"] = ci_to.make_int(form_data["rate"], r=1)
|
||||||
@@ -528,6 +528,15 @@ def page_newoffer(self, url_split, post_string):
|
|||||||
|
|
||||||
coins_from, coins_to = listAvailableCoins(swap_client, split_from=True)
|
coins_from, coins_to = listAvailableCoins(swap_client, split_from=True)
|
||||||
|
|
||||||
|
addrs_from_raw = swap_client.listSMSGAddresses("offer_send_from")
|
||||||
|
addrs_to_raw = swap_client.listSMSGAddresses("offer_send_to")
|
||||||
|
|
||||||
|
all_addresses = swap_client.listAllSMSGAddresses({})
|
||||||
|
addr_notes = {addr["addr"]: addr["note"] for addr in all_addresses}
|
||||||
|
|
||||||
|
addrs_from = [(addr[0], addr_notes.get(addr[0], "")) for addr in addrs_from_raw]
|
||||||
|
addrs_to = [(addr[0], addr_notes.get(addr[0], "")) for addr in addrs_to_raw]
|
||||||
|
|
||||||
automation_filters = {"type_ind": Concepts.OFFER, "sort_by": "label"}
|
automation_filters = {"type_ind": Concepts.OFFER, "sort_by": "label"}
|
||||||
automation_strategies = swap_client.listAutomationStrategies(automation_filters)
|
automation_strategies = swap_client.listAutomationStrategies(automation_filters)
|
||||||
|
|
||||||
@@ -556,8 +565,8 @@ def page_newoffer(self, url_split, post_string):
|
|||||||
"err_messages": err_messages,
|
"err_messages": err_messages,
|
||||||
"coins_from": coins_from,
|
"coins_from": coins_from,
|
||||||
"coins": coins_to,
|
"coins": coins_to,
|
||||||
"addrs": swap_client.listSMSGAddresses("offer_send_from"),
|
"addrs": addrs_from,
|
||||||
"addrs_to": swap_client.listSMSGAddresses("offer_send_to"),
|
"addrs_to": addrs_to,
|
||||||
"data": page_data,
|
"data": page_data,
|
||||||
"automation_strategies": automation_strategies,
|
"automation_strategies": automation_strategies,
|
||||||
"summary": summary,
|
"summary": summary,
|
||||||
@@ -581,7 +590,7 @@ def page_offer(self, url_split, post_string):
|
|||||||
offer, xmr_offer = swap_client.getXmrOffer(offer_id)
|
offer, xmr_offer = swap_client.getXmrOffer(offer_id)
|
||||||
ensure(offer, "Unknown offer ID")
|
ensure(offer, "Unknown offer ID")
|
||||||
|
|
||||||
extend_data = { # Defaults
|
extend_data = {
|
||||||
"nb_validmins": 10,
|
"nb_validmins": 10,
|
||||||
}
|
}
|
||||||
messages = []
|
messages = []
|
||||||
@@ -598,7 +607,6 @@ def page_offer(self, url_split, post_string):
|
|||||||
|
|
||||||
reverse_bid: bool = True if offer.bid_reversed else False
|
reverse_bid: bool = True if offer.bid_reversed else False
|
||||||
|
|
||||||
# Set defaults
|
|
||||||
debugind = -1
|
debugind = -1
|
||||||
bid_amount = ci_from.format_amount(offer.amount_from)
|
bid_amount = ci_from.format_amount(offer.amount_from)
|
||||||
bid_rate = ci_to.format_amount(offer.rate)
|
bid_rate = ci_to.format_amount(offer.rate)
|
||||||
@@ -617,7 +625,6 @@ def page_offer(self, url_split, post_string):
|
|||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
err_messages.append("Revoke offer failed: " + str(ex))
|
err_messages.append("Revoke offer failed: " + str(ex))
|
||||||
elif b"repeat_offer" in form_data:
|
elif b"repeat_offer" in form_data:
|
||||||
# Can't set the post data here as browsers will always resend the original post data when responding to redirects
|
|
||||||
self.send_response(302)
|
self.send_response(302)
|
||||||
self.send_header("Location", "/newoffer?offer_from=" + offer_id.hex())
|
self.send_header("Location", "/newoffer?offer_from=" + offer_id.hex())
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|||||||
@@ -122,8 +122,30 @@ def set_pagination_filters(form_data, filters):
|
|||||||
filters["page_no"] = 1
|
filters["page_no"] = 1
|
||||||
elif form_data and have_data_entry(form_data, "pageforwards"):
|
elif form_data and have_data_entry(form_data, "pageforwards"):
|
||||||
filters["page_no"] = int(form_data[b"pageno"][0]) + 1
|
filters["page_no"] = int(form_data[b"pageno"][0]) + 1
|
||||||
if filters["page_no"] > 1:
|
|
||||||
filters["offset"] = (filters["page_no"] - 1) * PAGE_LIMIT
|
no_limit = False
|
||||||
|
if form_data:
|
||||||
|
if "is_json" in form_data:
|
||||||
|
no_limit = form_data.get("no_limit", False)
|
||||||
|
else:
|
||||||
|
no_limit = b"no_limit" in form_data
|
||||||
|
|
||||||
|
if no_limit:
|
||||||
|
filters["offset"] = 0
|
||||||
|
filters["limit"] = None
|
||||||
|
else:
|
||||||
|
if filters["page_no"] > 1:
|
||||||
|
filters["offset"] = (filters["page_no"] - 1) * PAGE_LIMIT
|
||||||
|
filters["limit"] = PAGE_LIMIT
|
||||||
|
|
||||||
|
|
||||||
|
def get_data_with_pagination(data, filters):
|
||||||
|
if filters.get("limit") is None:
|
||||||
|
return data
|
||||||
|
|
||||||
|
offset = filters.get("offset", 0)
|
||||||
|
limit = filters.get("limit", PAGE_LIMIT)
|
||||||
|
return data[offset:offset + limit]
|
||||||
|
|
||||||
|
|
||||||
def getTxIdHex(bid, tx_type, suffix):
|
def getTxIdHex(bid, tx_type, suffix):
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2022-2024 tecnovert
|
# Copyright (c) 2022-2025 tecnovert
|
||||||
# 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.
|
||||||
|
|
||||||
|
from hashlib import sha256 as hashlib_sha256 # hashlib is faster than pycryptodome
|
||||||
from basicswap.contrib.blake256.blake256 import blake_hash
|
from basicswap.contrib.blake256.blake256 import blake_hash
|
||||||
|
from Crypto.Hash import HMAC, RIPEMD160, SHA512 # pycryptodome
|
||||||
from Crypto.Hash import HMAC, RIPEMD160, SHA256, SHA512 # pycryptodome
|
|
||||||
|
|
||||||
|
|
||||||
def sha256(data: bytes) -> bytes:
|
def sha256(data: bytes) -> bytes:
|
||||||
h = SHA256.new()
|
h = hashlib_sha256()
|
||||||
h.update(data)
|
h.update(data)
|
||||||
return h.digest()
|
return h.digest()
|
||||||
|
|
||||||
|
|||||||
42
basicswap/util/logging.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Copyright (c) 2025 The Basicswap developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from basicswap.util.crypto import (
|
||||||
|
sha256,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BSXLogger(logging.Logger):
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__(name)
|
||||||
|
self.safe_logs = False
|
||||||
|
self.safe_logs_prefix = b""
|
||||||
|
|
||||||
|
def addr(self, addr: str) -> str:
|
||||||
|
if self.safe_logs:
|
||||||
|
return (
|
||||||
|
"A_"
|
||||||
|
+ sha256(self.safe_logs_prefix + addr.encode(encoding="utf-8"))[
|
||||||
|
:8
|
||||||
|
].hex()
|
||||||
|
)
|
||||||
|
return addr
|
||||||
|
|
||||||
|
def id(self, concept_id: bytes, prefix: str = "") -> str:
|
||||||
|
if concept_id is None:
|
||||||
|
return prefix + "None"
|
||||||
|
if isinstance(concept_id, str):
|
||||||
|
concept_id = bytes.fromhex(concept_id)
|
||||||
|
if self.safe_logs:
|
||||||
|
return (prefix if len(prefix) > 0 else "_") + sha256(
|
||||||
|
self.safe_logs_prefix + concept_id
|
||||||
|
)[:8].hex()
|
||||||
|
return prefix + concept_id.hex()
|
||||||
|
|
||||||
|
def info_s(self, msg, *args, **kwargs):
|
||||||
|
if self.safe_logs is False:
|
||||||
|
self.info(msg, *args, **kwargs)
|
||||||
@@ -204,12 +204,12 @@ It may take a few minutes to start as the coin daemons are started before the ht
|
|||||||
|
|
||||||
Add a coin (Stop basicswap first):
|
Add a coin (Stop basicswap first):
|
||||||
|
|
||||||
export SWAP_DATADIR=/Users/$USER/coinswaps
|
export SWAP_DATADIR=$HOME/coinswaps
|
||||||
basicswap-prepare --usebtcfastsync --datadir=/$SWAP_DATADIR --addcoin=bitcoin
|
basicswap-prepare --usebtcfastsync --datadir=/$SWAP_DATADIR --addcoin=bitcoin
|
||||||
|
|
||||||
|
|
||||||
Start after installed:
|
Start after installed:
|
||||||
|
|
||||||
export SWAP_DATADIR=/Users/$USER/coinswaps
|
export SWAP_DATADIR=$HOME/coinswaps
|
||||||
. $SWAP_DATADIR/venv/bin/activate && python -V
|
. $SWAP_DATADIR/venv/bin/activate && python -V
|
||||||
basicswap-run --datadir=$SWAP_DATADIR
|
basicswap-run --datadir=$SWAP_DATADIR
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3.4'
|
|
||||||
services:
|
services:
|
||||||
swapclient:
|
swapclient:
|
||||||
image: i_swapclient
|
image: i_swapclient
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3.4'
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
swapclient:
|
swapclient:
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
version: '3.3'
|
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
|
|||||||
16
docker/production/compose-fragments/1_dogecoin.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
dogecoin_core:
|
||||||
|
image: i_dogecoin
|
||||||
|
build:
|
||||||
|
context: dogecoin
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
container_name: dogecoin_core
|
||||||
|
volumes:
|
||||||
|
- ${DATA_PATH}/dogecoin:/data
|
||||||
|
expose:
|
||||||
|
- ${DOGE_RPC_PORT}
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "3"
|
||||||
|
restart: unless-stopped
|
||||||
25
docker/production/dogecoin/Dockerfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
FROM i_swapclient as install_stage
|
||||||
|
|
||||||
|
RUN basicswap-prepare --preparebinonly --bindir=/coin_bin --withcoin=dogecoin --withoutcoin=particl && \
|
||||||
|
find /coin_bin -name *.tar.gz -delete
|
||||||
|
|
||||||
|
FROM debian:bullseye-slim
|
||||||
|
COPY --from=install_stage /coin_bin .
|
||||||
|
|
||||||
|
ENV DOGECOIN_DATA /data
|
||||||
|
|
||||||
|
RUN groupadd -r dogecoin && useradd -r -m -g dogecoin dogecoin \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -qq --no-install-recommends gosu \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& mkdir "$DOGECOIN_DATA" \
|
||||||
|
&& chown -R dogecoin:dogecoin "$DOGECOIN_DATA" \
|
||||||
|
&& ln -sfn "$DOGECOIN_DATA" /home/dogecoin/.dogecoin \
|
||||||
|
&& chown -h dogecoin:dogecoin /home/dogecoin/.dogecoin
|
||||||
|
VOLUME /data
|
||||||
|
|
||||||
|
COPY entrypoint.sh /entrypoint.sh
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
|
||||||
|
EXPOSE 8332 8333 18332 18333 18443 18444
|
||||||
|
CMD ["/dogecoin/dogecoind", "--datadir=/data"]
|
||||||
11
docker/production/dogecoin/entrypoint.sh
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [[ "$1" == "dogecoin-cli" || "$1" == "dogecoin-tx" || "$1" == "dogecoind" || "$1" == "test_dogecoin" ]]; then
|
||||||
|
mkdir -p "$DOGECOIN_DATA"
|
||||||
|
|
||||||
|
chown -h dogecoin:dogecoin /home/dogecoin/.dogecoin
|
||||||
|
exec gosu dogecoin "$@"
|
||||||
|
else
|
||||||
|
exec "$@"
|
||||||
|
fi
|
||||||
@@ -6,7 +6,7 @@ ENV LANG=C.UTF-8 \
|
|||||||
|
|
||||||
RUN apt-get update; \
|
RUN apt-get update; \
|
||||||
apt-get install -y --no-install-recommends \
|
apt-get install -y --no-install-recommends \
|
||||||
python3-pip libpython3-dev gnupg pkg-config gcc libc-dev gosu tzdata wget unzip;
|
python3-pip libpython3-dev gnupg pkg-config gcc libc-dev gosu tzdata wget unzip cmake ninja-build;
|
||||||
|
|
||||||
ARG BASICSWAP_URL=https://github.com/basicswap/basicswap/archive/master.zip
|
ARG BASICSWAP_URL=https://github.com/basicswap/basicswap/archive/master.zip
|
||||||
ARG BASICSWAP_DIR=basicswap-master
|
ARG BASICSWAP_DIR=basicswap-master
|
||||||
|
|||||||
6
guix.scm
@@ -114,15 +114,15 @@
|
|||||||
(define-public basicswap
|
(define-public basicswap
|
||||||
(package
|
(package
|
||||||
(name "basicswap")
|
(name "basicswap")
|
||||||
(version "0.14.1")
|
(version "0.14.3")
|
||||||
(source (origin
|
(source (origin
|
||||||
(method git-fetch)
|
(method git-fetch)
|
||||||
(uri (git-reference
|
(uri (git-reference
|
||||||
(url "https://github.com/basicswap/basicswap")
|
(url "https://github.com/basicswap/basicswap")
|
||||||
(commit "062cc6dbdc3c1f489d2bf78ce7cd99fbc885f14e")))
|
(commit "3b60472c04a58f26e33665f0eb0e88a558050c74")))
|
||||||
(sha256
|
(sha256
|
||||||
(base32
|
(base32
|
||||||
"16m61d45rn4lzvximsnkvrdg4hfsdk4460lhyarixjcdzknh1z1z"))
|
"0xrli8mzigm0ryn28y28xvy4gc0358ck2036ncx5f1sj5s8dwfkh"))
|
||||||
(file-name (git-file-name name version))))
|
(file-name (git-file-name name version))))
|
||||||
(build-system pyproject-build-system)
|
(build-system pyproject-build-system)
|
||||||
|
|
||||||
|
|||||||