mirror of
https://github.com/basicswap/basicswap.git
synced 2025-04-03 21:09:05 +00:00
Compare commits
192 commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
122 changed files with 21366 additions and 7994 deletions
.github
DockerfileREADME.mdbasicswap
__init__.pybase.pybasicswap.py
bin
chainparams.pyconfig.pycontrib/test_framework
http_server.pyinterface
js_server.pypgp
keys
bitcoin_laanwj.pgpdash_pasta.pgpdecred_release.pgpdogecoin_patricklodder.pgpdogecoin_xanimo.pgpfiro_reuben.pgplitecoin_davidburkett38.pgplitecoin_thrasher.pgpmonero_binaryfate.pgpnamecoin_JeremyRand.pgpnavcoin_builder.pgpnicolasdorier.ascparticl_tecnovert.pgppivx_fuzzbawls.pgp
sigs
protocols
static
css
images/coins
Dogecoin-20.pngDogecoin.pngLitecoin%20MWEB.pngParticlAnon-20.pngParticlAnon.pngParticlBlind-20.pngParticlBlind.pngWownero.png
js
templates
active.htmlbids.htmlbids_available.htmlfooter.htmlheader.htmlindex.htmloffer.htmloffer_confirm.htmloffer_new_1.htmloffer_new_2.htmloffers.htmlsettings.htmlunlock.htmlwallet.htmlwallets.html
ui
util
doc
docker
guix.scmpgp/keys
11
.github/dependabot.yml
vendored
Normal file
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
17
.github/workflows/ci.yml
vendored
|
@ -30,6 +30,9 @@ jobs:
|
|||
- name: Install
|
||||
run: |
|
||||
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
|
||||
run: |
|
||||
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
|
||||
env:
|
||||
cache-name: cache-cores
|
||||
CACHE_KEY: $(printf $(python bin/basicswap-prepare.py --version --withcoins=bitcoin) | sha256sum | head -c 64)
|
||||
with:
|
||||
path: $BIN_DIR
|
||||
key: $CACHE_KEY
|
||||
path: /tmp/cached_bin
|
||||
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
|
||||
run: |
|
||||
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
|
||||
run: |
|
||||
export PYTHONPATH=$(pwd)
|
||||
|
|
|
@ -6,7 +6,7 @@ ENV LANG=C.UTF-8 \
|
|||
|
||||
RUN apt-get update; \
|
||||
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
|
||||
COPY ./requirements.txt requirements.txt
|
||||
|
|
12
README.md
12
README.md
|
@ -64,6 +64,12 @@ BasicSwap is compatible with the following digital assets.
|
|||
<td>XMR
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Bitcoin Cash
|
||||
</td>
|
||||
<td>BCH
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dash
|
||||
</td>
|
||||
|
@ -106,6 +112,12 @@ BasicSwap is compatible with the following digital assets.
|
|||
<td>PART
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dogecoin
|
||||
</td>
|
||||
<td>DOGE
|
||||
</td>
|
||||
</tr>
|
||||
</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).
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
name = "basicswap"
|
||||
|
||||
__version__ = "0.14.2"
|
||||
__version__ = "0.14.3"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -28,6 +28,9 @@ from .rpc import (
|
|||
from .util import (
|
||||
TemporaryError,
|
||||
)
|
||||
from .util.logging import (
|
||||
BSXLogger,
|
||||
)
|
||||
from .chainparams import (
|
||||
Coins,
|
||||
chainparams,
|
||||
|
@ -75,6 +78,7 @@ class BaseApp(DBMethods):
|
|||
self.delay_event.set()
|
||||
|
||||
def prepareLogging(self):
|
||||
logging.setLoggerClass(BSXLogger)
|
||||
self.log = logging.getLogger(self.log_name)
|
||||
self.log.propagate = False
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
567
basicswap/bin/prepare.py
Normal file → Executable file
567
basicswap/bin/prepare.py
Normal file → Executable file
File diff suppressed because it is too large
Load diff
|
@ -2,32 +2,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import signal
|
||||
import logging
|
||||
import traceback
|
||||
import subprocess
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import basicswap.config as cfg
|
||||
from basicswap import __version__
|
||||
from basicswap.ui.util import getCoinName
|
||||
from basicswap.basicswap import BasicSwap
|
||||
from basicswap.chainparams import chainparams
|
||||
from basicswap.chainparams import chainparams, Coins
|
||||
from basicswap.http_server import HttpThread
|
||||
from basicswap.contrib.websocket_server import WebsocketServer
|
||||
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.level = logging.DEBUG
|
||||
if not len(logger.handlers):
|
||||
logger.addHandler(logging.StreamHandler(sys.stdout))
|
||||
initial_logger = logging.getLogger()
|
||||
initial_logger.level = logging.DEBUG
|
||||
if not len(initial_logger.handlers):
|
||||
initial_logger.addHandler(initial_logger.StreamHandler(sys.stdout))
|
||||
logger = initial_logger
|
||||
|
||||
swap_client = None
|
||||
|
||||
|
@ -48,9 +49,10 @@ def is_known_coin(coin_name: str) -> bool:
|
|||
|
||||
|
||||
def signal_handler(sig, frame):
|
||||
global swap_client
|
||||
logger.info("Signal %d detected, ending program." % (sig))
|
||||
if swap_client is not None:
|
||||
os.write(
|
||||
sys.stdout.fileno(), f"Signal {sig} detected, ending program.\n".encode("utf-8")
|
||||
)
|
||||
if swap_client is not None and not swap_client.chainstate_delay_event.is_set():
|
||||
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))
|
||||
datadir_path = os.path.expanduser(node_dir)
|
||||
|
||||
# Rewrite litecoin.conf for 0.21.3
|
||||
# Rewrite litecoin.conf
|
||||
# TODO: Remove
|
||||
needs_rewrite: bool = False
|
||||
ltc_conf_path = os.path.join(datadir_path, "litecoin.conf")
|
||||
if os.path.exists(ltc_conf_path):
|
||||
config_to_add = ["blockfilterindex=0", "peerblockfilters=0"]
|
||||
with open(ltc_conf_path) as fp:
|
||||
for line in fp:
|
||||
line = line.strip()
|
||||
if line in config_to_add:
|
||||
config_to_add.remove(line)
|
||||
|
||||
if len(config_to_add) > 0:
|
||||
if line.endswith("=onion"):
|
||||
needs_rewrite = True
|
||||
break
|
||||
if needs_rewrite:
|
||||
logger.info("Rewriting litecoin.conf")
|
||||
shutil.copyfile(ltc_conf_path, ltc_conf_path + ".last")
|
||||
with open(ltc_conf_path, "a") as fp:
|
||||
for line in config_to_add:
|
||||
fp.write(line + "\n")
|
||||
with (
|
||||
open(ltc_conf_path + ".last") as fp_from,
|
||||
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 = [
|
||||
daemon_bin,
|
||||
|
@ -241,12 +249,25 @@ def getWalletBinName(coin_id: int, coin_settings, default_name: str) -> str:
|
|||
) + (".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 = []
|
||||
if "config_filename" in coin_settings:
|
||||
extra_args.append("--conf=" + coin_settings["config_filename"])
|
||||
if "port" in coin_settings:
|
||||
extra_args.append("--port=" + str(int(coin_settings["port"])))
|
||||
if "port" in coin_settings and coin_id != Coins.BTC:
|
||||
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
|
||||
|
||||
|
||||
|
@ -421,7 +442,9 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
swap_client.log.info(f"Starting {display_name} daemon")
|
||||
|
||||
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(
|
||||
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
|
||||
)
|
||||
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:
|
||||
try:
|
||||
d.handle.wait(timeout=120)
|
||||
|
@ -520,8 +543,8 @@ def runClient(fp, data_dir, chain, start_only_coins):
|
|||
if fp:
|
||||
fp.close()
|
||||
closed_pids.append(d.handle.pid)
|
||||
except Exception as ex:
|
||||
swap_client.log.error("Error: {}".format(ex))
|
||||
except Exception as e:
|
||||
swap_client.log.error(f"Error: {e}")
|
||||
|
||||
if os.path.exists(pids_path):
|
||||
with open(pids_path) as fd:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2019-2024 tecnovert
|
||||
# Copyright (c) 2024-2025 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -31,6 +32,7 @@ class Coins(IntEnum):
|
|||
LTC_MWEB = 15
|
||||
# ZANO = 16
|
||||
BCH = 17
|
||||
DOGE = 18
|
||||
|
||||
|
||||
chainparams = {
|
||||
|
@ -89,6 +91,8 @@ chainparams = {
|
|||
"bip44": 0,
|
||||
"min_amount": 100000,
|
||||
"max_amount": 10000000 * COIN,
|
||||
"ext_public_key_prefix": 0x0488B21E,
|
||||
"ext_secret_key_prefix": 0x0488ADE4,
|
||||
},
|
||||
"testnet": {
|
||||
"rpcport": 18332,
|
||||
|
@ -100,6 +104,8 @@ chainparams = {
|
|||
"min_amount": 100000,
|
||||
"max_amount": 10000000 * COIN,
|
||||
"name": "testnet3",
|
||||
"ext_public_key_prefix": 0x043587CF,
|
||||
"ext_secret_key_prefix": 0x04358394,
|
||||
},
|
||||
"regtest": {
|
||||
"rpcport": 18443,
|
||||
|
@ -110,6 +116,8 @@ chainparams = {
|
|||
"bip44": 1,
|
||||
"min_amount": 100000,
|
||||
"max_amount": 10000000 * COIN,
|
||||
"ext_public_key_prefix": 0x043587CF,
|
||||
"ext_secret_key_prefix": 0x04358394,
|
||||
},
|
||||
},
|
||||
Coins.LTC: {
|
||||
|
@ -153,12 +161,51 @@ chainparams = {
|
|||
"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: {
|
||||
"name": "decred",
|
||||
"ticker": "DCR",
|
||||
"message_magic": "Decred Signed Message:\n",
|
||||
"blocks_target": 60 * 5,
|
||||
"decimal_places": 8,
|
||||
"has_multiwallet": False,
|
||||
"mainnet": {
|
||||
"rpcport": 9109,
|
||||
"pubkey_address": 0x073F,
|
||||
|
@ -364,6 +411,7 @@ chainparams = {
|
|||
"has_cltv": False,
|
||||
"has_csv": False,
|
||||
"has_segwit": False,
|
||||
"has_multiwallet": False,
|
||||
"mainnet": {
|
||||
"rpcport": 8888,
|
||||
"pubkey_address": 82,
|
||||
|
@ -403,6 +451,7 @@ chainparams = {
|
|||
"decimal_places": 8,
|
||||
"has_csv": True,
|
||||
"has_segwit": True,
|
||||
"has_multiwallet": False,
|
||||
"mainnet": {
|
||||
"rpcport": 44444,
|
||||
"pubkey_address": 53,
|
||||
|
@ -479,10 +528,13 @@ chainparams = {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
name_map = {}
|
||||
ticker_map = {}
|
||||
|
||||
|
||||
for c, params in chainparams.items():
|
||||
name_map[params["name"].lower()] = c
|
||||
ticker_map[params["ticker"].lower()] = c
|
||||
|
||||
|
||||
|
@ -490,4 +542,11 @@ def getCoinIdFromTicker(ticker: str) -> str:
|
|||
try:
|
||||
return ticker_map[ticker.lower()]
|
||||
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_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(
|
||||
os.getenv("NAMECOIN_BINDIR", os.path.join(DEFAULT_TEST_BINDIR, "namecoin"))
|
||||
)
|
||||
|
|
64
basicswap/contrib/test_framework/descriptors.py
Normal file
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)
|
||||
if page == "bid":
|
||||
return page_bid(self, url_split, post_string)
|
||||
if page == "receivedbids":
|
||||
return page_bids(self, url_split, post_string, received=True)
|
||||
if page == "sentbids":
|
||||
return page_bids(self, url_split, post_string, sent=True)
|
||||
if page == "bids":
|
||||
return page_bids(self, url_split, post_string)
|
||||
if page == "availablebids":
|
||||
return page_bids(self, url_split, post_string, available=True)
|
||||
if page == "watched":
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2024 tecnovert
|
||||
# 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.
|
||||
|
||||
|
@ -53,6 +54,10 @@ class CoinInterface:
|
|||
self._mx_wallet = threading.Lock()
|
||||
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):
|
||||
self._unknown_wallet_seed = True
|
||||
self._restore_height = None
|
||||
|
@ -188,7 +193,7 @@ class Secp256k1Interface(CoinInterface, AdaptorSigInterface):
|
|||
def curve_type():
|
||||
return Curves.secp256k1
|
||||
|
||||
def getNewSecretKey(self) -> bytes:
|
||||
def getNewRandomKey(self) -> bytes:
|
||||
return i2b(getSecretInt())
|
||||
|
||||
def getPubkey(self, privkey: bytes) -> bytes:
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- 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
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from typing import Union
|
||||
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 .btc import BTCInterface, ensure_op, findOutput
|
||||
from basicswap.rpc import make_rpc_func
|
||||
|
@ -454,11 +454,14 @@ class BCHInterface(BTCInterface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockSpendTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
size,
|
||||
pay_fee,
|
||||
"createSCLockSpendTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize_without_witness()
|
||||
|
@ -506,11 +509,14 @@ class BCHInterface(BTCInterface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockRefundTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
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
|
||||
|
@ -582,11 +588,14 @@ class BCHInterface(BTCInterface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockRefundSpendToFTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize_without_witness()
|
||||
|
@ -780,7 +789,7 @@ class BCHInterface(BTCInterface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.nLockTime == 0, "Bad nLockTime") # TODO match txns created by cores
|
||||
|
@ -835,7 +844,7 @@ class BCHInterface(BTCInterface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.nLockTime == 0, "nLockTime not 0")
|
||||
|
@ -881,7 +890,7 @@ class BCHInterface(BTCInterface):
|
|||
size = self.getTxSize(tx)
|
||||
vsize = size
|
||||
|
||||
self._log.info(
|
||||
self._log.info_s(
|
||||
"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
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.nLockTime == 0, "nLockTime not 0")
|
||||
|
@ -947,9 +956,7 @@ class BCHInterface(BTCInterface):
|
|||
size = self.getTxSize(tx)
|
||||
vsize = size
|
||||
|
||||
self._log.info(
|
||||
"tx amount, vsize, fee: %ld, %ld, %ld", tx_value, vsize, fee_paid
|
||||
)
|
||||
self._log.info_s(f"tx amount, vsize, fee: {tx_value}, {vsize}, {fee_paid}")
|
||||
|
||||
return True
|
||||
|
||||
|
@ -962,7 +969,7 @@ class BCHInterface(BTCInterface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.nLockTime == 0, "nLockTime not 0")
|
||||
|
@ -995,7 +1002,7 @@ class BCHInterface(BTCInterface):
|
|||
size = self.getTxSize(tx)
|
||||
vsize = size
|
||||
|
||||
self._log.info(
|
||||
self._log.info_s(
|
||||
"tx amount, vsize, fee: %ld, %ld, %ld", tx.vout[0].nValue, vsize, fee_paid
|
||||
)
|
||||
|
||||
|
@ -1115,11 +1122,14 @@ class BCHInterface(BTCInterface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createMercyTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
1,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createMercyTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {1}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
txHex = tx.serialize_without_witness()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -18,15 +18,9 @@ from basicswap.basicswap_util import (
|
|||
getVoutByAddress,
|
||||
getVoutByScriptPubKey,
|
||||
)
|
||||
from basicswap.contrib.test_framework import (
|
||||
segwit_addr,
|
||||
)
|
||||
from basicswap.interface.base import (
|
||||
Secp256k1Interface,
|
||||
)
|
||||
from basicswap.interface.base import Secp256k1Interface
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
b2h,
|
||||
i2b,
|
||||
b2i,
|
||||
i2h,
|
||||
|
@ -35,6 +29,7 @@ from basicswap.util.ecc import (
|
|||
pointToCPK,
|
||||
CPKToPoint,
|
||||
)
|
||||
from basicswap.util.extkey import ExtKeyPair
|
||||
from basicswap.util.script import (
|
||||
decodeScriptNum,
|
||||
getCompactSizeLen,
|
||||
|
@ -44,6 +39,7 @@ from basicswap.util.script import (
|
|||
from basicswap.util.address import (
|
||||
toWIF,
|
||||
b58encode,
|
||||
b58decode,
|
||||
decodeWif,
|
||||
decodeAddress,
|
||||
pubkeyToAddress,
|
||||
|
@ -63,6 +59,8 @@ from coincurve.ecdsaotves import (
|
|||
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 (
|
||||
COIN,
|
||||
COutPoint,
|
||||
|
@ -217,6 +215,10 @@ class BTCInterface(Secp256k1Interface):
|
|||
rv += output.nValue
|
||||
return rv
|
||||
|
||||
@staticmethod
|
||||
def est_lock_tx_vsize() -> int:
|
||||
return 110
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
return 147
|
||||
|
@ -262,10 +264,22 @@ class BTCInterface(Secp256k1Interface):
|
|||
self._rpcport = coin_settings["rpcport"]
|
||||
self._rpcauth = coin_settings["rpcauth"]
|
||||
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._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.setConfTarget(coin_settings["conf_target"])
|
||||
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._expect_seedid_hex = None
|
||||
self._altruistic = coin_settings.get("altruistic", True)
|
||||
self._use_descriptors = coin_settings.get("use_descriptors", False)
|
||||
|
||||
def open_rpc(self, wallet=None):
|
||||
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
|
||||
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:
|
||||
# Skip over other expected wallets
|
||||
if wallet_name in ("mweb",):
|
||||
continue
|
||||
|
||||
change_watchonly_wallet: bool = (
|
||||
self._rpc_wallet_watch == self._rpc_wallet
|
||||
)
|
||||
|
||||
self._rpc_wallet = wallet_name
|
||||
self._log.info(
|
||||
"Switched {} wallet name to {}.".format(
|
||||
self.ticker(), self._rpc_wallet
|
||||
)
|
||||
f"Switched {self.ticker()} wallet name to {self._rpc_wallet}."
|
||||
)
|
||||
self.rpc_wallet = make_rpc_func(
|
||||
self._rpcport,
|
||||
|
@ -314,6 +332,8 @@ class BTCInterface(Secp256k1Interface):
|
|||
host=self._rpc_host,
|
||||
wallet=self._rpc_wallet,
|
||||
)
|
||||
if change_watchonly_wallet:
|
||||
self.rpc_wallet_watch = self.rpc_wallet
|
||||
break
|
||||
|
||||
return len(wallets)
|
||||
|
@ -358,9 +378,40 @@ class BTCInterface(Secp256k1Interface):
|
|||
raise ValueError(f"Block header not found at time: {time}")
|
||||
|
||||
def initialiseWallet(self, key_bytes: bytes) -> None:
|
||||
key_wif = self.encodeKey(key_bytes)
|
||||
self.rpc_wallet("sethdseed", [True, key_wif])
|
||||
assert len(key_bytes) == 32
|
||||
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):
|
||||
rv = self.rpc_wallet("getwalletinfo")
|
||||
|
@ -370,16 +421,23 @@ class BTCInterface(Secp256k1Interface):
|
|||
return rv
|
||||
|
||||
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()
|
||||
best_block = blockchaininfo["bestblockhash"]
|
||||
|
||||
chain_synced = round(blockchaininfo["verificationprogress"], 3)
|
||||
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()
|
||||
try:
|
||||
|
@ -390,16 +448,43 @@ class BTCInterface(Secp256k1Interface):
|
|||
)
|
||||
if block_header["time"] < start_time:
|
||||
return block_header["height"]
|
||||
if "previousblockhash" not in block_header: # Genesis block
|
||||
return block_header["height"]
|
||||
block_hash = block_header["previousblockhash"]
|
||||
finally:
|
||||
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:
|
||||
wi = self.rpc_wallet("getwalletinfo")
|
||||
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:
|
||||
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()
|
||||
self._expect_seedid_hex = expect_seedid
|
||||
self._have_checked_seed = True
|
||||
|
@ -424,6 +509,10 @@ class BTCInterface(Secp256k1Interface):
|
|||
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
||||
if not or_watch_only:
|
||||
return addr_info["ismine"]
|
||||
|
||||
if self._use_descriptors:
|
||||
addr_info = self.rpc_wallet_watch("getaddressinfo", [address])
|
||||
|
||||
return addr_info["ismine"] or addr_info["iswatchonly"]
|
||||
|
||||
def checkAddressMine(self, address: str) -> None:
|
||||
|
@ -491,6 +580,20 @@ class BTCInterface(Secp256k1Interface):
|
|||
pkh = hash160(pk)
|
||||
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:
|
||||
# pkh is ripemd160(sha256(pk))
|
||||
assert len(pkh) == 20
|
||||
|
@ -526,7 +629,12 @@ class BTCInterface(Secp256k1Interface):
|
|||
pk = self.getPubkey(key)
|
||||
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]
|
||||
|
||||
def encodeKey(self, key_bytes: bytes) -> str:
|
||||
|
@ -626,11 +734,14 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockRefundTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
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
|
||||
|
@ -681,11 +792,14 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockRefundSpendTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
@ -748,11 +862,14 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockRefundSpendToFTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
@ -795,11 +912,14 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockSpendTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
@ -824,7 +944,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.nLockTime == 0, "Bad nLockTime") # TODO match txns created by cores
|
||||
|
@ -912,7 +1032,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.nLockTime == 0, "nLockTime not 0")
|
||||
|
@ -951,7 +1071,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
fee_rate_paid = fee_paid * 1000 // vsize
|
||||
|
||||
self._log.info(
|
||||
self._log.info_s(
|
||||
"tx amount, vsize, feerate: %ld, %ld, %ld",
|
||||
locked_coin,
|
||||
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
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.nLockTime == 0, "nLockTime not 0")
|
||||
|
@ -1017,7 +1137,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
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
|
||||
)
|
||||
|
||||
|
@ -1035,7 +1155,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.nLockTime == 0, "nLockTime not 0")
|
||||
|
@ -1073,7 +1193,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
fee_rate_paid = fee_paid * 1000 // vsize
|
||||
|
||||
self._log.info(
|
||||
self._log.info_s(
|
||||
"tx amount, vsize, feerate: %ld, %ld, %ld",
|
||||
tx.vout[0].nValue,
|
||||
vsize,
|
||||
|
@ -1296,7 +1416,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
def getWalletTransaction(self, txid: bytes):
|
||||
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
|
||||
# TODO: filter errors
|
||||
return None
|
||||
|
@ -1383,7 +1503,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
witness_bytes = 109
|
||||
vsize = self.getTxVSize(tx, add_witness_bytes=witness_bytes)
|
||||
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}."
|
||||
)
|
||||
return pay_fee
|
||||
|
@ -1397,10 +1517,13 @@ class BTCInterface(Secp256k1Interface):
|
|||
cb_swap_value: int,
|
||||
b_fee: int,
|
||||
restore_height: int,
|
||||
spend_actual_balance: bool = False,
|
||||
lock_tx_vout=None,
|
||||
) -> bytes:
|
||||
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
|
||||
|
||||
|
@ -1408,7 +1531,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
script_pk = self.getPkDest(Kbs)
|
||||
|
||||
if locked_n is None:
|
||||
wtx = self.rpc_wallet(
|
||||
wtx = self.rpc_wallet_watch(
|
||||
"gettransaction",
|
||||
[
|
||||
chain_b_lock_txid.hex(),
|
||||
|
@ -1445,10 +1568,23 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
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])
|
||||
|
||||
def isWatchOnlyAddress(self, address: str):
|
||||
def isWatchOnlyAddress(self, address: str) -> bool:
|
||||
addr_info = self.rpc_wallet("getaddressinfo", [address])
|
||||
return addr_info["iswatchonly"]
|
||||
|
||||
|
@ -1466,10 +1602,11 @@ class BTCInterface(Secp256k1Interface):
|
|||
vout: int = -1,
|
||||
):
|
||||
# Add watchonly address and rescan if required
|
||||
|
||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||
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(
|
||||
"Rescanning {} chain from height: {}".format(
|
||||
self.coin_name(), rescan_from
|
||||
|
@ -1479,7 +1616,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
return_txid = True if txid is None else False
|
||||
if txid is None:
|
||||
txns = self.rpc_wallet(
|
||||
txns = self.rpc_wallet_watch(
|
||||
"listunspent",
|
||||
[
|
||||
0,
|
||||
|
@ -1500,7 +1637,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
|
||||
try:
|
||||
# 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
|
||||
if "blockhash" in tx:
|
||||
|
@ -1802,16 +1939,20 @@ class BTCInterface(Secp256k1Interface):
|
|||
def unlockWallet(self, password: str):
|
||||
if password == "":
|
||||
return
|
||||
self._log.info("unlockWallet - {}".format(self.ticker()))
|
||||
self._log.info(f"unlockWallet - {self.ticker()}")
|
||||
|
||||
if self.coin_type() == Coins.BTC:
|
||||
# Recreate wallet if none found
|
||||
# Required when encrypting an existing btc wallet, workaround is to delete the btc wallet and recreate
|
||||
wallets = self.rpc("listwallets")
|
||||
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
|
||||
self.rpc("createwallet", ["wallet.dat", False, True, "", False, False])
|
||||
self.rpc(
|
||||
"createwallet", [self._rpc_wallet, False, True, "", False, False]
|
||||
)
|
||||
self.rpc_wallet("encryptwallet", [password])
|
||||
|
||||
# Max timeout value, ~3 years
|
||||
|
@ -1819,7 +1960,7 @@ class BTCInterface(Secp256k1Interface):
|
|||
self._sc.checkWalletSeed(self.coin_type())
|
||||
|
||||
def lockWallet(self):
|
||||
self._log.info("lockWallet - {}".format(self.ticker()))
|
||||
self._log.info(f"lockWallet - {self.ticker()}")
|
||||
self.rpc_wallet("walletlock")
|
||||
|
||||
def get_p2sh_script_pubkey(self, script: bytearray) -> bytearray:
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -27,7 +27,6 @@ from basicswap.interface.btc import (
|
|||
)
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
b2h,
|
||||
b2i,
|
||||
i2b,
|
||||
i2h,
|
||||
|
@ -211,6 +210,10 @@ class DCRInterface(Secp256k1Interface):
|
|||
def txoType():
|
||||
return CTxOut
|
||||
|
||||
@staticmethod
|
||||
def est_lock_tx_vsize() -> int:
|
||||
return 224
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
return 327
|
||||
|
@ -273,6 +276,9 @@ class DCRInterface(Secp256k1Interface):
|
|||
self._connection_type = coin_settings["connection_type"]
|
||||
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):
|
||||
return openrpc(self._rpcport, self._rpcauth, host=self._rpc_host)
|
||||
|
||||
|
@ -1123,11 +1129,14 @@ class DCRInterface(Secp256k1Interface):
|
|||
fee_info["size"] = size
|
||||
|
||||
self._log.info(
|
||||
"createSCLockSpendTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
||||
tx.TxHash().hex(),
|
||||
tx_fee_rate,
|
||||
size,
|
||||
pay_fee,
|
||||
"createSCLockSpendTx {}{}.".format(
|
||||
self._log.id(tx.TxHash()),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, size, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize(TxSerializeType.NoWitness)
|
||||
|
@ -1167,11 +1176,14 @@ class DCRInterface(Secp256k1Interface):
|
|||
tx.vout[0].value = locked_coin - pay_fee
|
||||
|
||||
self._log.info(
|
||||
"createSCLockRefundTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
||||
tx.TxHash().hex(),
|
||||
tx_fee_rate,
|
||||
size,
|
||||
pay_fee,
|
||||
"createSCLockRefundTx {}{}.".format(
|
||||
self._log.id(tx.TxHash()),
|
||||
(
|
||||
""
|
||||
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
|
||||
|
@ -1215,11 +1227,14 @@ class DCRInterface(Secp256k1Interface):
|
|||
tx.vout[0].value = locked_coin - pay_fee
|
||||
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
||||
tx.TxHash().hex(),
|
||||
tx_fee_rate,
|
||||
size,
|
||||
pay_fee,
|
||||
"createSCLockRefundSpendTx {}{}.".format(
|
||||
self._log.id(tx.TxHash()),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, size, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize(TxSerializeType.NoWitness)
|
||||
|
@ -1244,7 +1259,7 @@ class DCRInterface(Secp256k1Interface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.locktime == 0, "Bad locktime")
|
||||
|
@ -1320,7 +1335,7 @@ class DCRInterface(Secp256k1Interface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.locktime == 0, "Bad locktime")
|
||||
|
@ -1390,7 +1405,7 @@ class DCRInterface(Secp256k1Interface):
|
|||
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.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
|
||||
tx = self.loadTx(tx_bytes)
|
||||
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.locktime == 0, "locktime not 0")
|
||||
|
@ -1539,11 +1554,14 @@ class DCRInterface(Secp256k1Interface):
|
|||
tx.vout[0].value = locked_amount - pay_fee
|
||||
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, size, fee: %ld, %ld, %ld.",
|
||||
tx.TxHash().hex(),
|
||||
tx_fee_rate,
|
||||
size,
|
||||
pay_fee,
|
||||
"createSCLockRefundSpendToFTx {}{}.".format(
|
||||
self._log.id(tx.TxHash()),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, size, fee: {tx_fee_rate}, {size}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize(TxSerializeType.NoWitness)
|
||||
|
@ -1712,7 +1730,7 @@ class DCRInterface(Secp256k1Interface):
|
|||
witness_bytes = 115
|
||||
size = len(tx.serialize()) + witness_bytes
|
||||
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}."
|
||||
)
|
||||
return pay_fee
|
||||
|
@ -1726,6 +1744,7 @@ class DCRInterface(Secp256k1Interface):
|
|||
cb_swap_value: int,
|
||||
b_fee: int,
|
||||
restore_height: int,
|
||||
spend_actual_balance: bool = False,
|
||||
lock_tx_vout=None,
|
||||
) -> bytes:
|
||||
self._log.info("spendBLockTx %s:\n", chain_b_lock_txid.hex())
|
||||
|
|
62
basicswap/interface/doge.py
Normal file
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
|
||||
)
|
||||
|
||||
if "wallet_name" in coin_settings:
|
||||
raise ValueError(f"Invalid setting for {self.coin_name()}: wallet_name")
|
||||
|
||||
def getExchangeName(self, exchange_name: str) -> str:
|
||||
return "zcoin"
|
||||
|
||||
|
@ -102,7 +105,9 @@ class FIROInterface(BTCInterface):
|
|||
|
||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||
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(
|
||||
"Rescanning {} chain from height: {}".format(
|
||||
self.coin_name(), rescan_from
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2023 tecnovert
|
||||
# Copyright (c) 2024-2025 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# 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):
|
||||
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._rpcport,
|
||||
self._rpcauth,
|
||||
|
@ -52,7 +53,6 @@ class LTCInterface(BTCInterface):
|
|||
|
||||
def getWalletInfo(self):
|
||||
rv = super(LTCInterface, self).getWalletInfo()
|
||||
|
||||
mweb_info = self.rpc_wallet_mweb("getwalletinfo")
|
||||
rv["mweb_balance"] = mweb_info["balance"]
|
||||
rv["mweb_unconfirmed"] = mweb_info["unconfirmed_balance"]
|
||||
|
@ -88,13 +88,13 @@ class LTCInterface(BTCInterface):
|
|||
|
||||
|
||||
class LTCInterfaceMWEB(LTCInterface):
|
||||
@staticmethod
|
||||
def coin_type():
|
||||
|
||||
def interface_type(self) -> int:
|
||||
return Coins.LTC_MWEB
|
||||
|
||||
def __init__(self, coin_settings, network, swap_client=None):
|
||||
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._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("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
|
||||
self.rpc("createwallet", ["mweb", False, True, password, False, False, True])
|
||||
|
||||
|
@ -137,7 +137,7 @@ class LTCInterfaceMWEB(LTCInterface):
|
|||
self.rpc_wallet("walletpassphrase", [password, 100000000])
|
||||
|
||||
if self.getWalletSeedID() == "Not found":
|
||||
self._sc.initialiseWallet(self.coin_type())
|
||||
self._sc.initialiseWallet(self.interface_type())
|
||||
|
||||
# Workaround to trigger mweb_spk_man->LoadMWEBKeychain()
|
||||
self.rpc("unloadwallet", ["mweb"])
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -41,7 +41,6 @@ from basicswap.util.address import (
|
|||
from basicswap.util import (
|
||||
b2i,
|
||||
i2b,
|
||||
i2h,
|
||||
ensure,
|
||||
)
|
||||
from basicswap.basicswap_util import (
|
||||
|
@ -81,6 +80,9 @@ class NAVInterface(BTCInterface):
|
|||
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:
|
||||
# p2sh-p2wsh
|
||||
return True
|
||||
|
@ -549,7 +551,9 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
if not self.isAddressMine(dest_address, or_watch_only=True):
|
||||
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(
|
||||
"Rescanning {} chain from height: {}".format(
|
||||
self.coin_name(), rescan_from
|
||||
|
@ -666,6 +670,7 @@ class NAVInterface(BTCInterface):
|
|||
cb_swap_value: int,
|
||||
b_fee: int,
|
||||
restore_height: int,
|
||||
spend_actual_balance: bool = False,
|
||||
lock_tx_vout=None,
|
||||
) -> bytes:
|
||||
self._log.info("spendBLockTx %s:\n", chain_b_lock_txid.hex())
|
||||
|
@ -812,11 +817,14 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockRefundTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockRefundTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
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
|
||||
|
@ -867,11 +875,14 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockRefundSpendTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
@ -924,11 +935,14 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockRefundSpendToFTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockRefundSpendToFTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
@ -971,11 +985,14 @@ class NAVInterface(BTCInterface):
|
|||
|
||||
tx.rehash()
|
||||
self._log.info(
|
||||
"createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
i2h(tx.sha256),
|
||||
tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockSpendTx {}{}.".format(
|
||||
self._log.id(i2b(tx.sha256)),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {tx_fee_rate}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return tx.serialize()
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -66,6 +66,10 @@ class PARTInterface(BTCInterface):
|
|||
def txVersion() -> int:
|
||||
return 0xA0
|
||||
|
||||
@staticmethod
|
||||
def est_lock_tx_vsize() -> int:
|
||||
return 138
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
return 200
|
||||
|
@ -187,10 +191,18 @@ class PARTInterface(BTCInterface):
|
|||
|
||||
|
||||
class PARTInterfaceBlind(PARTInterface):
|
||||
|
||||
def interface_type(self) -> int:
|
||||
return Coins.PART_BLIND
|
||||
|
||||
@staticmethod
|
||||
def balance_type():
|
||||
return BalanceTypes.BLIND
|
||||
|
||||
@staticmethod
|
||||
def est_lock_tx_vsize() -> int:
|
||||
return 980
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
return 1032
|
||||
|
@ -240,7 +252,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
def createSCLockTx(self, value: int, script: bytearray, vkbv: bytes) -> bytes:
|
||||
|
||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||
ephemeral_key = self.getNewSecretKey()
|
||||
ephemeral_key = self.getNewRandomKey()
|
||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||
assert len(ephemeral_pubkey) == 33
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
|
@ -257,9 +269,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
]
|
||||
params = [inputs, outputs]
|
||||
rv = self.rpc_wallet("createrawparttransaction", params)
|
||||
|
||||
tx_bytes = bytes.fromhex(rv["hex"])
|
||||
return tx_bytes
|
||||
return bytes.fromhex(rv["hex"])
|
||||
|
||||
def fundSCLockTx(self, tx_bytes: bytes, feerate: int, vkbv: bytes) -> bytes:
|
||||
feerate_str = self.format_amount(feerate)
|
||||
|
@ -288,7 +298,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
"lockUnspents": True,
|
||||
"feeRate": feerate_str,
|
||||
}
|
||||
rv = self.rpc(
|
||||
rv = self.rpc_wallet(
|
||||
"fundrawtransactionfrom", ["blind", tx_hex, {}, outputs_info, options]
|
||||
)
|
||||
return bytes.fromhex(rv["hex"])
|
||||
|
@ -307,7 +317,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
lock_tx_obj = self.rpc("decoderawtransaction", [tx_lock_bytes.hex()])
|
||||
assert self.getTxid(tx_lock_bytes).hex() == lock_tx_obj["txid"]
|
||||
# Nonce is derived from vkbv, ephemeral_key isn't used
|
||||
ephemeral_key = self.getNewSecretKey()
|
||||
ephemeral_key = self.getNewRandomKey()
|
||||
ephemeral_pubkey = self.getPubkey(ephemeral_key)
|
||||
assert len(ephemeral_pubkey) == 33
|
||||
nonce = self.getScriptLockTxNonce(vkbv)
|
||||
|
@ -348,7 +358,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# 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)
|
||||
inputs_info = {
|
||||
"0": {
|
||||
|
@ -428,7 +438,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# 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)
|
||||
inputs_info = {
|
||||
"0": {
|
||||
|
@ -467,7 +477,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
):
|
||||
lock_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
||||
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["locktime"] == 0, "Bad nLockTime")
|
||||
|
@ -531,7 +541,9 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
):
|
||||
lock_refund_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
||||
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["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_txid_hex = lock_refund_spend_tx_obj["txid"]
|
||||
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")
|
||||
|
@ -745,7 +759,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
dummy_witness_stack = self.getScriptLockTxDummyWitness(script_lock)
|
||||
|
||||
# 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)
|
||||
inputs_info = {
|
||||
"0": {
|
||||
|
@ -779,11 +793,14 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
)
|
||||
actual_tx_fee_rate = pay_fee * 1000 // vsize
|
||||
self._log.info(
|
||||
"createSCLockSpendTx %s:\n fee_rate, vsize, fee: %ld, %ld, %ld.",
|
||||
lock_spend_tx_obj["txid"],
|
||||
actual_tx_fee_rate,
|
||||
vsize,
|
||||
pay_fee,
|
||||
"createSCLockSpendTx {}{}.".format(
|
||||
self._log.id(lock_spend_tx_obj["txid"]),
|
||||
(
|
||||
""
|
||||
if self._log.safe_logs
|
||||
else f":\n fee_rate, vsize, fee: {actual_tx_fee_rate}, {vsize}, {pay_fee}"
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
fee_info["vsize"] = vsize
|
||||
|
@ -798,7 +815,9 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
):
|
||||
lock_spend_tx_obj = self.rpc("decoderawtransaction", [tx_bytes.hex()])
|
||||
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["locktime"] == 0, "Bad nLockTime")
|
||||
|
@ -949,7 +968,7 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
dummy_witness_stack = [x.hex() for x in dummy_witness_stack]
|
||||
|
||||
# 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)
|
||||
inputs_info = {
|
||||
"0": {
|
||||
|
@ -1158,10 +1177,44 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
sub_fee: bool = False,
|
||||
lock_unspents: bool = True,
|
||||
) -> str:
|
||||
txn = self.rpc_wallet(
|
||||
"createrawtransaction", [[], {addr_to: self.format_amount(amount)}]
|
||||
# Estimate lock tx size / fee
|
||||
|
||||
# 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 = {
|
||||
"lockUnspents": lock_unspents,
|
||||
"conf_target": self._conf_target,
|
||||
|
@ -1170,14 +1223,24 @@ class PARTInterfaceBlind(PARTInterface):
|
|||
options["subtractFeeFromOutputs"] = [
|
||||
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):
|
||||
|
||||
def interface_type(self) -> int:
|
||||
return Coins.PART_ANON
|
||||
|
||||
@staticmethod
|
||||
def balance_type():
|
||||
return BalanceTypes.ANON
|
||||
|
||||
@staticmethod
|
||||
def est_lock_tx_vsize() -> int:
|
||||
return 1153
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_a_lock_spend_tx_vsize() -> int:
|
||||
raise ValueError("Not possible")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/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.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# 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
|
||||
# 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:
|
||||
raise ValueError("Not possible")
|
||||
|
||||
@staticmethod
|
||||
def est_lock_tx_vsize() -> int:
|
||||
# TODO: Estimate with ringsize
|
||||
return 1604
|
||||
|
||||
@staticmethod
|
||||
def xmr_swap_b_lock_spend_tx_vsize() -> int:
|
||||
# TODO: Estimate with ringsize
|
||||
|
@ -78,7 +83,7 @@ class XMRInterface(CoinInterface):
|
|||
|
||||
def is_transient_error(self, ex) -> bool:
|
||||
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 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._wallet_password = None
|
||||
self._have_checked_seed = False
|
||||
self._wallet_filename = coin_settings.get("wallet_name", "swap_wallet")
|
||||
|
||||
daemon_login = None
|
||||
if coin_settings.get("rpcuser", "") != "":
|
||||
|
@ -170,14 +176,23 @@ class XMRInterface(CoinInterface):
|
|||
ensure(new_priority >= 0 and new_priority < 4, "Invalid fee_priority value")
|
||||
self._fee_priority = new_priority
|
||||
|
||||
def setWalletFilename(self, wallet_filename):
|
||||
self._wallet_filename = wallet_filename
|
||||
|
||||
def createWallet(self, params):
|
||||
if self._wallet_password is not None:
|
||||
params["password"] = self._wallet_password
|
||||
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):
|
||||
params = {"filename": filename}
|
||||
|
@ -326,7 +341,7 @@ class XMRInterface(CoinInterface):
|
|||
|
||||
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
|
||||
return i2b(edu.get_secret())
|
||||
|
||||
|
@ -403,7 +418,9 @@ class XMRInterface(CoinInterface):
|
|||
params["priority"] = self._fee_priority
|
||||
rv = self.rpc_wallet("transfer", params)
|
||||
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"])
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2020-2024 tecnovert
|
||||
# Copyright (c) 2024-2025 The Basicswap developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -14,6 +15,7 @@ from .util import (
|
|||
)
|
||||
from .basicswap_util import (
|
||||
strBidState,
|
||||
strTxState,
|
||||
SwapTypes,
|
||||
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_own_offer": o.was_sent,
|
||||
"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:
|
||||
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)
|
||||
rv = []
|
||||
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_id": b[2].hex(),
|
||||
"offer_id": b[3].hex(),
|
||||
"created_at": b[0],
|
||||
"expire_at": b[1],
|
||||
"coin_from": b[9],
|
||||
"amount_from": swap_client.ci(b[9]).format_amount(b[4]),
|
||||
"coin_from": ci_from.coin_name(),
|
||||
"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_state": strBidState(b[5]),
|
||||
"addr_from": b[11],
|
||||
"addr_to": offer.addr_to if offer else None
|
||||
}
|
||||
|
||||
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)
|
||||
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")
|
||||
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:
|
||||
|
@ -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.")
|
||||
|
||||
ci = swap_client.ci(coin)
|
||||
rv = {"coin": ci.ticker()}
|
||||
if coin in (Coins.XMR, Coins.WOW):
|
||||
key_view = swap_client.getWalletKey(coin, 1, for_ed25519=True)
|
||||
key_spend = swap_client.getWalletKey(coin, 2, for_ed25519=True)
|
||||
address = ci.getAddressFromKeys(key_view, key_spend)
|
||||
return bytes(
|
||||
json.dumps(
|
||||
{
|
||||
"coin": ci.ticker(),
|
||||
"key_view": ci.encodeKey(key_view),
|
||||
"key_spend": ci.encodeKey(key_spend),
|
||||
"address": address,
|
||||
}
|
||||
),
|
||||
"UTF-8",
|
||||
|
||||
expect_address = swap_client.getCachedMainWalletAddress(ci)
|
||||
rv.update(
|
||||
{
|
||||
"key_view": ci.encodeKey(key_view),
|
||||
"key_spend": ci.encodeKey(key_spend),
|
||||
"address": address,
|
||||
"expected_address": (
|
||||
"Unset" if expect_address is None else expect_address
|
||||
),
|
||||
}
|
||||
)
|
||||
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(
|
||||
json.dumps(
|
||||
{"coin": ci.ticker(), "seed": seed_key.hex(), "seed_id": seed_id.hex()}
|
||||
),
|
||||
json.dumps(rv),
|
||||
"UTF-8",
|
||||
)
|
||||
|
||||
|
@ -943,6 +980,67 @@ def js_readurl(self, url_split, post_string, is_json) -> bytes:
|
|||
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 = {
|
||||
"coins": js_coins,
|
||||
"wallets": js_wallets,
|
||||
|
@ -968,6 +1066,7 @@ pages = {
|
|||
"lock": js_lock,
|
||||
"help": js_help,
|
||||
"readurl": js_readurl,
|
||||
"active": js_active,
|
||||
}
|
||||
|
||||
|
||||
|
|
1257
basicswap/pgp/keys/bitcoin_laanwj.pgp
Normal file
1257
basicswap/pgp/keys/bitcoin_laanwj.pgp
Normal file
File diff suppressed because it is too large
Load diff
166
basicswap/pgp/keys/dash_pasta.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
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
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-----
|
||||
|
||||
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
|
||||
PSxdIAmV43ZrNIOxKzYOsA==
|
||||
=w412
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
PSxdIAmV43ZrNIOxKzYOsA==
|
||||
=w412
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
161
basicswap/pgp/keys/nicolasdorier.asc
Normal file
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
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 -*-
|
||||
|
||||
# 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
|
||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -15,9 +15,10 @@ from basicswap.chainparams import (
|
|||
Coins,
|
||||
)
|
||||
from basicswap.basicswap_util import (
|
||||
EventLogTypes,
|
||||
KeyTypes,
|
||||
SwapTypes,
|
||||
EventLogTypes,
|
||||
TxTypes,
|
||||
)
|
||||
from . import ProtocolInterface
|
||||
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):
|
||||
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
|
||||
try:
|
||||
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()))
|
||||
|
||||
# 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_to = self.ci(Coins(offer.coin_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)
|
||||
if summed_pkbs != xmr_swap.pkbs:
|
||||
err_msg: str = "Summed key does not match expected wallet spend pubkey"
|
||||
have_pk = summed_pkbs.hex()
|
||||
expect_pk = xmr_swap.pkbs.hex()
|
||||
self.log.error(f"{err_msg}. Got: {have_pk}, Expect: {expect_pk}")
|
||||
self.log.error(
|
||||
f"{err_msg}. Got: {summed_pkbs.hex()}, Expect: {xmr_swap.pkbs.hex()}"
|
||||
)
|
||||
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)
|
||||
elif coin_to in (Coins.PART_BLIND, Coins.PART_ANON):
|
||||
address_to = self.getCachedStealthAddressForCoin(base_coin_to, use_cursor)
|
||||
else:
|
||||
address_to = self.getCachedStealthAddressForCoin(
|
||||
ci_follower.coin_type(), use_cursor
|
||||
address_to = self.getReceiveAddressFromPool(
|
||||
base_coin_to, bid_id, TxTypes.XMR_SWAP_B_LOCK_SPEND, use_cursor
|
||||
)
|
||||
amount = bid.amount_to
|
||||
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,
|
||||
)
|
||||
self.log.debug(
|
||||
"Submitted lock B spend txn %s to %s chain for bid %s",
|
||||
txid.hex(),
|
||||
ci_follower.coin_name(),
|
||||
bid_id.hex(),
|
||||
f"Submitted lock B spend txn {self.log.id(txid)} to {ci_follower.coin_name()} chain for bid {self.log.id(bid_id)}."
|
||||
)
|
||||
self.logBidEvent(
|
||||
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
|
||||
|
||||
key_type = KeyTypes.KBSF if was_sent else KeyTypes.KBSL
|
||||
|
||||
return ci_follower.encodeKey(
|
||||
swap_client.getPathKey(
|
||||
ci_leader.coin_type(),
|
||||
ci_follower.coin_type(),
|
||||
ci_leader.interface_type(),
|
||||
ci_follower.interface_type(),
|
||||
bid.created_at,
|
||||
xmr_swap.contract_count,
|
||||
key_type,
|
||||
|
|
|
@ -1,153 +1,157 @@
|
|||
/* General Styles */
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.monospace {
|
||||
font-family: monospace;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.floatright {
|
||||
position: fixed;
|
||||
top: 1.25rem;
|
||||
right: 1.25rem;
|
||||
z-index: 9999;
|
||||
position: fixed;
|
||||
top: 1.25rem;
|
||||
right: 1.25rem;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.padded_row td {
|
||||
padding-top: 1.5em;
|
||||
padding-top: 1.5em;
|
||||
}
|
||||
|
||||
/* Modal Styles */
|
||||
.modal-highest {
|
||||
z-index: 9999;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
/* Animation */
|
||||
#hide {
|
||||
-moz-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
-webkit-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
-o-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
animation: cssAnimation 0s ease-in 15s forwards;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
-moz-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
-webkit-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
-o-animation: cssAnimation 0s ease-in 15s forwards;
|
||||
animation: cssAnimation 0s ease-in 15s forwards;
|
||||
-webkit-animation-fill-mode: forwards;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
@keyframes cssAnimation {
|
||||
to {
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
to {
|
||||
width: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes cssAnimation {
|
||||
to {
|
||||
width: 0;
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
to {
|
||||
width: 0;
|
||||
height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom Select Styles */
|
||||
.custom-select .select {
|
||||
appearance: none;
|
||||
background-image: url('/static/images/other/coin.png');
|
||||
background-position: 10px center;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
appearance: none;
|
||||
background-image: url('/static/images/other/coin.png');
|
||||
background-position: 10px center;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.custom-select select::-webkit-scrollbar {
|
||||
width: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.custom-select .select option {
|
||||
padding-left: 0;
|
||||
text-indent: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 50%;
|
||||
padding-left: 0;
|
||||
text-indent: 0;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 50%;
|
||||
}
|
||||
|
||||
.custom-select .select option.no-space {
|
||||
padding-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.custom-select .select option[data-image] {
|
||||
background-image: url('');
|
||||
background-image: url('');
|
||||
}
|
||||
|
||||
.custom-select .select-icon {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
transform: translateY(-50%);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.custom-select .select-image {
|
||||
display: none;
|
||||
margin-top: 10px;
|
||||
display: none;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.custom-select .select:focus + .select-dropdown .select-image {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Blur and Overlay Styles */
|
||||
.blurred {
|
||||
filter: blur(3px);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
filter: blur(3px);
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.error-overlay.non-blurred {
|
||||
filter: none;
|
||||
pointer-events: auto;
|
||||
user-select: auto;
|
||||
filter: none;
|
||||
pointer-events: auto;
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
/* Form Element Styles */
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
select:disabled,
|
||||
input:disabled,
|
||||
textarea:disabled {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
@media screen and (-webkit-min-device-pixel-ratio: 0) {
|
||||
select:disabled,
|
||||
input:disabled,
|
||||
textarea:disabled {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
border: 1px solid red !important;
|
||||
border: 1px solid red !important;
|
||||
}
|
||||
|
||||
/* Active Container Styles */
|
||||
.active-container {
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.active-container::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: 1px solid rgb(77, 132, 240);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border: 1px solid rgb(77, 132, 240);
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Center Spin Animation */
|
||||
.center-spin {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hover Container Styles */
|
||||
|
@ -155,205 +159,209 @@
|
|||
.hover-container:hover #coin_to,
|
||||
.hover-container:hover #coin_from_button,
|
||||
.hover-container:hover #coin_from {
|
||||
border-color: #3b82f6;
|
||||
border-color: #3b82f6;
|
||||
}
|
||||
|
||||
#coin_to_button, #coin_from_button {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 20px 20px;
|
||||
#coin_to_button,
|
||||
#coin_from_button {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
/* Input-like Container Styles */
|
||||
.input-like-container {
|
||||
max-width: 100%;
|
||||
background-color: #ffffff;
|
||||
width: 360px;
|
||||
padding: 1rem;
|
||||
color: #374151;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
outline: none;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-all;
|
||||
height: auto;
|
||||
min-height: 90px;
|
||||
max-height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
max-width: 100%;
|
||||
background-color: #ffffff;
|
||||
width: 360px;
|
||||
padding: 1rem;
|
||||
color: #374151;
|
||||
border-radius: 0.375rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
outline: none;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-all;
|
||||
height: auto;
|
||||
min-height: 90px;
|
||||
max-height: 150px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.input-like-container.dark {
|
||||
background-color: #374151;
|
||||
color: #ffffff;
|
||||
background-color: #374151;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.input-like-container.copying {
|
||||
width: inherit;
|
||||
width: inherit;
|
||||
}
|
||||
|
||||
/* QR Code Styles */
|
||||
.qrcode {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.qrcode-border {
|
||||
border: 2px solid;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0px;
|
||||
border: 2px solid;
|
||||
background-color: #ffffff;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.qrcode img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 0px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
#showQR {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.qrcode-container {
|
||||
margin-top: 25px;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
/* Disabled Element Styles */
|
||||
select.select-disabled,
|
||||
.disabled-input-enabled,
|
||||
select.disabled-select-enabled {
|
||||
opacity: 0.40 !important;
|
||||
opacity: 0.40 !important;
|
||||
}
|
||||
|
||||
/* Shutdown Modal Styles */
|
||||
#shutdownModal {
|
||||
z-index: 50;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
#shutdownModal > div:first-child {
|
||||
z-index: 40;
|
||||
z-index: 40;
|
||||
}
|
||||
|
||||
#shutdownModal > div:last-child {
|
||||
z-index: 50;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
#shutdownModal > div {
|
||||
transition: opacity 0.3s ease-out;
|
||||
transition: opacity 0.3s ease-out;
|
||||
}
|
||||
|
||||
#shutdownModal.hidden > div {
|
||||
opacity: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#shutdownModal:not(.hidden) > div {
|
||||
opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.shutdown-button {
|
||||
transition: all 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.shutdown-button.shutdown-disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
color: #a0aec0;
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
color: #a0aec0;
|
||||
}
|
||||
|
||||
.shutdown-button.shutdown-disabled:hover {
|
||||
background-color: #4a5568;
|
||||
background-color: #4a5568;
|
||||
}
|
||||
|
||||
.shutdown-button.shutdown-disabled svg {
|
||||
opacity: 0.5;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
|
||||
/* Loading line animation */
|
||||
/* Loading Line Animation */
|
||||
.loading-line {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #ccc;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #ccc;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.loading-line::before {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, transparent, #007bff, transparent);
|
||||
animation: loading 1.5s infinite;
|
||||
content: '';
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to right, transparent, #007bff, transparent);
|
||||
animation: loading 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
}
|
||||
/* Hide the loading line once data is loaded */
|
||||
|
||||
.usd-value:not(.loading) .loading-line,
|
||||
.profit-loss:not(.loading) .loading-line {
|
||||
display: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.resolution-button {
|
||||
/* Resolution Button Styles */
|
||||
.resolution-button {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #4B5563; /* gray-600 */
|
||||
font-size: 0.875rem; /* text-sm */
|
||||
font-weight: 500; /* font-medium */
|
||||
color: #4B5563;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 0.25rem;
|
||||
transition: all 0.2s;
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.resolution-button:hover {
|
||||
color: #1F2937; /* gray-800 */
|
||||
}
|
||||
.resolution-button:hover {
|
||||
color: #1F2937;
|
||||
}
|
||||
|
||||
.resolution-button:focus {
|
||||
outline: 2px solid #3B82F6; /* blue-500 */
|
||||
}
|
||||
.resolution-button:focus {
|
||||
outline: 2px solid #3B82F6;
|
||||
}
|
||||
|
||||
.resolution-button.active {
|
||||
color: #3B82F6; /* blue-500 */
|
||||
outline: 2px solid #3B82F6; /* blue-500 */
|
||||
}
|
||||
.resolution-button.active {
|
||||
color: #3B82F6;
|
||||
outline: 2px solid #3B82F6;
|
||||
}
|
||||
|
||||
.dark .resolution-button {
|
||||
color: #9CA3AF; /* gray-400 */
|
||||
}
|
||||
.dark .resolution-button {
|
||||
color: #9CA3AF;
|
||||
}
|
||||
|
||||
.dark .resolution-button:hover {
|
||||
color: #F3F4F6; /* gray-100 */
|
||||
}
|
||||
.dark .resolution-button:hover {
|
||||
color: #F3F4F6;
|
||||
}
|
||||
|
||||
.dark .resolution-button.active {
|
||||
color: #60A5FA; /* blue-400 */
|
||||
outline-color: #60A5FA; /* blue-400 */
|
||||
.dark .resolution-button.active {
|
||||
color: #60A5FA;
|
||||
outline-color: #60A5FA;
|
||||
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 ![]() (image error) Size: 2.3 KiB After ![]() (image error) Size: 2.3 KiB ![]() ![]() |
Before ![]() (image error) Size: 16 KiB After ![]() (image error) Size: 16 KiB ![]() ![]() |
Before ![]() (image error) Size: 1.8 KiB After ![]() (image error) Size: 1.8 KiB ![]() ![]() |
Binary file not shown.
Before ![]() (image error) Size: 1.7 KiB |
Binary file not shown.
Before ![]() (image error) Size: 7.7 KiB |
Binary file not shown.
Before ![]() (image error) Size: 1.7 KiB |
Binary file not shown.
Before ![]() (image error) Size: 7.7 KiB |
Binary file not shown.
Before ![]() (image error) Size: 8.4 KiB After ![]() (image error) Size: 9 KiB ![]() ![]() |
872
basicswap/static/js/active.js
Normal file
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
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
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
1988
basicswap/static/js/bids_sentreceived.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -14,7 +14,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||
const image = selectedOption.getAttribute('data-image') || '';
|
||||
const name = selectedOption.textContent.trim();
|
||||
select.style.backgroundImage = image ? `url(${image}?${new Date().getTime()})` : '';
|
||||
|
||||
|
||||
const selectImage = select.nextElementSibling.querySelector('.select-image');
|
||||
if (selectImage) {
|
||||
selectImage.src = image;
|
||||
|
|
190
basicswap/static/js/dropdown.js
Normal file
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);
|
File diff suppressed because one or more lines are too long
1825
basicswap/static/js/libs/popper.js
Normal file
1825
basicswap/static/js/libs/popper.js
Normal file
File diff suppressed because it is too large
Load diff
2516
basicswap/static/js/libs/tippy.js
Normal file
2516
basicswap/static/js/libs/tippy.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -19,8 +19,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
const backdrop = document.querySelectorAll('.navbar-backdrop');
|
||||
|
||||
if (close.length) {
|
||||
for (var i = 0; i < close.length; i++) {
|
||||
close[i].addEventListener('click', function() {
|
||||
for (var k = 0; k < close.length; k++) {
|
||||
close[k].addEventListener('click', function() {
|
||||
for (var j = 0; j < menu.length; j++) {
|
||||
menu[j].classList.toggle('hidden');
|
||||
}
|
||||
|
@ -29,12 +29,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
}
|
||||
|
||||
if (backdrop.length) {
|
||||
for (var i = 0; i < backdrop.length; i++) {
|
||||
backdrop[i].addEventListener('click', function() {
|
||||
for (var l = 0; l < backdrop.length; l++) {
|
||||
backdrop[l].addEventListener('click', function() {
|
||||
for (var j = 0; j < menu.length; j++) {
|
||||
menu[j].classList.toggle('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
window.addEventListener('DOMContentLoaded', (event) => {
|
||||
let err_msgs = document.querySelectorAll('p.error_msg');
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const err_msgs = document.querySelectorAll('p.error_msg');
|
||||
for (let i = 0; i < err_msgs.length; i++) {
|
||||
err_msg = err_msgs[i].innerText;
|
||||
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
|
||||
let inputs = document.querySelectorAll('input.error');
|
||||
let selects = document.querySelectorAll('select.error');
|
||||
let elements = [...inputs, ...selects];
|
||||
const inputs = document.querySelectorAll('input.error');
|
||||
const selects = document.querySelectorAll('select.error');
|
||||
const elements = [...inputs, ...selects];
|
||||
elements.forEach((element) => {
|
||||
element.addEventListener('focus', (event) => {
|
||||
event.target.classList.remove('error');
|
||||
|
|
2921
basicswap/static/js/offers.js
Normal file
2921
basicswap/static/js/offers.js
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
115
basicswap/static/js/tabs.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
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' %}
|
||||
{% from 'style.html' import breadcrumb_line_svg, circular_arrows_svg %}
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-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="/">
|
||||
<p>Home</p>
|
||||
</a>
|
||||
</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="/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>
|
||||
{% 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">Swaps in Progress</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">Monitor your currently active swap transactions.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 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 pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-8 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 text-sm">
|
||||
<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">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 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>
|
||||
</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>
|
||||
</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>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="/static/js/active.js"></script>
|
||||
|
||||
{% include 'footer.html' %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,220 +1,368 @@
|
|||
{% 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 %}
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-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="/"><p>Home</p></a>
|
||||
</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="#">{{ page_type_available }} {{ page_type_received }} {{ page_type_sent }}</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">{{ 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>
|
||||
{% 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 %}
|
||||
|
||||
|
||||
<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">Sent Bids / Received Bids</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">View, and manage bids.</p>
|
||||
</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>
|
||||
|
||||
<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' %}
|
||||
</body>
|
||||
</html>
|
118
basicswap/templates/bids_available.html
Normal file
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="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">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>
|
||||
{{ love_svg | safe }}
|
||||
</div>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,73 +1,4 @@
|
|||
{% 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' %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
<td class="py-3 px-6 bold">{{ data.amt_to }} {{ 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">
|
||||
<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>
|
||||
</tr>
|
||||
<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 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">Settings</span>
|
||||
<span class="text-xs text-gray-600 dark:text-gray-300 font-semibold">Value</span>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</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">
|
||||
<td class="py-3 px-6 bold">Send From Address</td>
|
||||
<td class="py-3 px-6">
|
||||
|
@ -475,21 +465,135 @@ if (document.readyState === 'loading') {
|
|||
handleBidsPageAddress();
|
||||
}
|
||||
</script>
|
||||
{% 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">
|
||||
<td class="py-3 px-6 bold">Amount</td>
|
||||
<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-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');">
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if data.rate_negotiable == true %}
|
||||
<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"> <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');">
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% 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">
|
||||
<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"
|
||||
id="bid_amount_send"
|
||||
autocomplete="off"
|
||||
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">
|
||||
<td class="py-3 px-6 bold">Minutes valid</td>
|
||||
<td class="py-3 px-6">
|
||||
|
@ -519,15 +623,24 @@ if (document.readyState === 'loading') {
|
|||
</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">
|
||||
<div class="w-full md:w-auto p-1.5 ml-2">
|
||||
<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">
|
||||
<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">
|
||||
<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>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
<div class="w-full md:w-auto p-1.5">
|
||||
<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>
|
||||
<!-- 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"
|
||||
|
@ -538,15 +651,12 @@ if (document.readyState === 'loading') {
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<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="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="text-center">
|
||||
<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="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>
|
||||
|
@ -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>
|
||||
</div>
|
||||
|
||||
<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="font-medium text-gray-900 dark:text-white text-lg">
|
||||
|
@ -564,20 +673,17 @@ if (document.readyState === 'loading') {
|
|||
<span id="modal-send-currency"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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="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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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="font-medium text-gray-900 dark:text-white text-lg" id="modal-valid-mins"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center gap-4">
|
||||
<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">
|
||||
|
@ -592,115 +698,317 @@ if (document.readyState === 'loading') {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
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);
|
||||
const xhr_rates = new XMLHttpRequest();
|
||||
xhr_rates.onload = () => {
|
||||
if (xhr_rates.status == 200) {
|
||||
const obj = JSON.parse(xhr_rates.response);
|
||||
const inner_html = '<h4 class="bold>Rates</h4><pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
||||
const ratesDisplay = document.getElementById('rates_display');
|
||||
if (ratesDisplay) {
|
||||
ratesDisplay.innerHTML = inner_html;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const xhr_bid_params = new XMLHttpRequest();
|
||||
xhr_bid_params.onload = () => {
|
||||
if (xhr_bid_params.status == 200) {
|
||||
const obj = JSON.parse(xhr_bid_params.response);
|
||||
const bidAmountSendInput = document.getElementById('bid_amount_send');
|
||||
if (bidAmountSendInput) {
|
||||
bidAmountSendInput.value = obj['amount_to'];
|
||||
}
|
||||
updateModalValues();
|
||||
}
|
||||
};
|
||||
|
||||
function lookup_rates() {
|
||||
const coin_from = document.getElementById('coin_from')?.value;
|
||||
const coin_to = document.getElementById('coin_to')?.value;
|
||||
|
||||
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() {
|
||||
document.getElementById('modal-amt-receive').textContent = document.getElementById('bid_amt_from').textContent;
|
||||
document.getElementById('modal-amt-send').textContent = document.getElementById('bid_amt_to').textContent;
|
||||
document.getElementById('modal-receive-currency').textContent = '{{ data.tla_from }}';
|
||||
document.getElementById('modal-send-currency').textContent = '{{ data.tla_to }}';
|
||||
|
||||
{% if data.xmr_type == true %}
|
||||
document.getElementById('modal-fee-info').textContent = `(excluding estimated {{ data.amt_from_lock_spend_tx_fee }} {{ data.tla_from }} in tx fees)`;
|
||||
{% else %}
|
||||
document.getElementById('modal-fee-info').textContent = '(excluding a tx fee)';
|
||||
{% endif %}
|
||||
|
||||
const addrSelect = document.querySelector('select[name="addr_from"]');
|
||||
const selectedText = addrSelect.options[addrSelect.selectedIndex].text;
|
||||
const addrText = selectedText === 'New Address' ? selectedText : selectedText.split(' ')[0];
|
||||
document.getElementById('modal-addr-from').textContent = addrText;
|
||||
|
||||
document.getElementById('modal-valid-mins').textContent = document.querySelector('input[name="validmins"]').value;
|
||||
function resetForm() {
|
||||
const bidAmountSendInput = document.getElementById('bid_amount_send');
|
||||
const bidAmountInput = document.getElementById('bid_amount');
|
||||
const bidRateInput = document.getElementById('bid_rate');
|
||||
const validMinsInput = document.querySelector('input[name="validmins"]');
|
||||
const amtVar = document.getElementById('amt_var')?.value === 'True';
|
||||
if (bidAmountSendInput) {
|
||||
bidAmountSendInput.value = amtVar ? '' : bidAmountSendInput.getAttribute('max');
|
||||
}
|
||||
if (bidAmountInput) {
|
||||
bidAmountInput.value = amtVar ? '' : bidAmountInput.getAttribute('max');
|
||||
}
|
||||
if (bidRateInput && !bidRateInput.disabled) {
|
||||
const defaultRate = document.getElementById('offer_rate')?.value || '';
|
||||
bidRateInput.value = defaultRate;
|
||||
}
|
||||
if (validMinsInput) {
|
||||
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() {
|
||||
updateModalValues();
|
||||
document.getElementById('confirmModal').classList.remove('hidden');
|
||||
return false;
|
||||
updateModalValues();
|
||||
const modal = document.getElementById('confirmModal');
|
||||
if (modal) {
|
||||
modal.classList.remove('hidden');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hideConfirmModal() {
|
||||
document.getElementById('confirmModal').classList.add('hidden');
|
||||
return false;
|
||||
const modal = document.getElementById('confirmModal');
|
||||
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) {
|
||||
event.preventDefault();
|
||||
window.location.href = `/offer/${window.location.pathname.split('/')[2]}`;
|
||||
if (event) event.preventDefault();
|
||||
const pathParts = window.location.pathname.split('/');
|
||||
const offerId = pathParts[pathParts.indexOf('offer') + 1];
|
||||
window.location.href = `/offer/${offerId}`;
|
||||
}
|
||||
|
||||
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');
|
||||
if (modalCancelBtn) {
|
||||
modalCancelBtn.onclick = hideConfirmModal;
|
||||
}
|
||||
const sendBidBtn = document.querySelector('button[name="sendbid"][value="Send Bid"]');
|
||||
if (sendBidBtn) {
|
||||
sendBidBtn.onclick = showConfirmModal;
|
||||
}
|
||||
|
||||
const mainCancelBtn = document.querySelector('button[name="cancel"]');
|
||||
if (mainCancelBtn) {
|
||||
mainCancelBtn.onclick = handleCancelClick;
|
||||
}
|
||||
|
||||
// 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 modalCancelBtn = document.querySelector('#confirmModal .flex button:last-child');
|
||||
if (modalCancelBtn) {
|
||||
modalCancelBtn.onclick = hideConfirmModal;
|
||||
}
|
||||
|
||||
const bidAmountInput = document.getElementById('bid_amount');
|
||||
if (bidAmountInput) {
|
||||
bidAmountInput.addEventListener('change', () => {
|
||||
updateBidParams('amount');
|
||||
updateModalValues();
|
||||
});
|
||||
}
|
||||
const mainCancelBtn = document.querySelector('button[name="cancel"]');
|
||||
if (mainCancelBtn) {
|
||||
mainCancelBtn.onclick = handleCancelClick;
|
||||
}
|
||||
|
||||
const bidRateInput = document.getElementById('bid_rate');
|
||||
if (bidRateInput) {
|
||||
bidRateInput.addEventListener('change', () => {
|
||||
updateBidParams('rate');
|
||||
updateModalValues();
|
||||
});
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
handleBidsPageAddress();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -768,67 +1076,5 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</section>
|
||||
</div>
|
||||
{% 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>
|
||||
</html>
|
||||
|
|
|
@ -168,6 +168,8 @@
|
|||
</div>
|
||||
</div>
|
||||
{% 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">
|
||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee From Confirm Target</p>
|
||||
<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>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</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>
|
||||
</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">
|
||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee To Confirm Target</p>
|
||||
<div class="relative">
|
||||
|
@ -273,6 +278,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -287,7 +293,7 @@
|
|||
<div class="w-full md:flex-1 p-3">
|
||||
<div class="flex flex-wrap -m-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="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
{{ select_bid_amount_svg | safe }}
|
||||
|
@ -467,4 +473,4 @@
|
|||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</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">
|
||||
<option{% if data.addr_to=="-1" %} selected{% endif %} value="-1">Public Network</option>
|
||||
{% 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 %}
|
||||
</select>
|
||||
</div>
|
||||
|
@ -274,7 +274,7 @@ if (document.readyState === 'loading') {
|
|||
<div class="w-full md:flex-1 p-3">
|
||||
<div class="flex flex-wrap -m-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="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
{{ select_bid_amount_svg | safe }}
|
||||
|
@ -283,15 +283,15 @@ if (document.readyState === 'loading') {
|
|||
</div>
|
||||
</div>
|
||||
<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>
|
||||
<div class="relative">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
{{ select_rate_svg | safe }}
|
||||
</div>
|
||||
<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 class="text-sm mt-2">
|
||||
<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>
|
||||
<p class="mb-1.5 font-medium text-base dark:text-white text-coolGray-800">Rate</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="relative flex-1">
|
||||
<div class="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
{{ select_rate_svg | safe }}
|
||||
</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>
|
||||
<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 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>
|
||||
|
@ -306,7 +306,6 @@ if (document.readyState === 'loading') {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% 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="w-full md:w-10/12">
|
||||
|
@ -414,181 +413,225 @@ if (document.readyState === 'loading') {
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
const xhr_rates = new XMLHttpRequest();
|
||||
xhr_rates.onload = () => {
|
||||
if (xhr_rates.status == 200) {
|
||||
const obj = JSON.parse(xhr_rates.response);
|
||||
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>';
|
||||
<script>
|
||||
const xhr_rates = new XMLHttpRequest();
|
||||
xhr_rates.onload = () => {
|
||||
if (xhr_rates.status == 200) {
|
||||
const obj = JSON.parse(xhr_rates.response);
|
||||
inner_html = '<pre><code>' + JSON.stringify(obj, null, ' ') + '</code></pre>';
|
||||
document.getElementById('rates_display').innerHTML = inner_html;
|
||||
document.querySelector(".pricejsonhidden").classList.remove("hidden");
|
||||
}
|
||||
};
|
||||
|
||||
const xhr_rates = new XMLHttpRequest();
|
||||
xhr_rates.onreadystatechange = function() {
|
||||
if (xhr_rates.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr_rates.status === 200) {
|
||||
document.getElementById('rates_display').innerHTML = xhr_rates.responseText;
|
||||
} else {
|
||||
console.error('Error fetching data:', xhr_rates.statusText);
|
||||
}
|
||||
}
|
||||
};
|
||||
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'];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
function getRateInferred(event) {
|
||||
event.preventDefault(); // Prevent default form submission behavior
|
||||
const selectedCoin = (coin_from === '15') ? '3' : coin_from;
|
||||
|
||||
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);
|
||||
inner_html = '<p>Updating...</p>';
|
||||
document.getElementById('rates_display').innerHTML = inner_html;
|
||||
document.querySelector(".pricejsonhidden").classList.remove("hidden");
|
||||
|
||||
const xhr_rates = new XMLHttpRequest();
|
||||
xhr_rates.onreadystatechange = function() {
|
||||
if (xhr_rates.readyState === XMLHttpRequest.DONE) {
|
||||
//console.log(xhr_rates.responseText);
|
||||
if (xhr_rates.status === 200) {
|
||||
try {
|
||||
const responseData = JSON.parse(xhr_rates.responseText);
|
||||
const xhr_rates = new XMLHttpRequest();
|
||||
xhr_rates.onreadystatechange = function() {
|
||||
if (xhr_rates.readyState === XMLHttpRequest.DONE) {
|
||||
if (xhr_rates.status === 200) {
|
||||
document.getElementById('rates_display').innerHTML = xhr_rates.responseText;
|
||||
} else {
|
||||
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;
|
||||
//document.getElementById('rate_inferred_display').innerText = " (" + coin_from + " to " + coin_to + "): " + rateInferred;
|
||||
document.getElementById('rate_inferred_display').innerText = " " + rateInferred;
|
||||
} catch (error) {
|
||||
console.error('Error parsing response:', error);
|
||||
document.getElementById('rate').value = rateInferred;
|
||||
set_rate('rate');
|
||||
} else {
|
||||
document.getElementById('rate').value = 'Error: Rate limit';
|
||||
console.error('Rate limit reached or invalid response format');
|
||||
}
|
||||
} else {
|
||||
console.error('Error fetching data:', xhr_rates.statusText);
|
||||
} catch (error) {
|
||||
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.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhr_rates.send(params);
|
||||
xhr_rates.open('POST', '/json/rates');
|
||||
xhr_rates.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
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) {
|
||||
const adaptor_sig_only_coins = ['6' /* XMR */,'9' /* WOW */, '8' /* PART_ANON */, '7' /* PART_BLIND */, '13' /* FIRO */, '17' /* BCH */];
|
||||
const secret_hash_only_coins = ['11' /* PIVX */, '12' /* DASH */];
|
||||
let make_hidden = false;
|
||||
if (adaptor_sig_only_coins.includes(coin_from) || adaptor_sig_only_coins.includes(coin_to)) {
|
||||
swap_type.disabled = true;
|
||||
swap_type.value = 'xmr_swap';
|
||||
make_hidden = true;
|
||||
swap_type.classList.add('select-disabled');
|
||||
} else
|
||||
if (secret_hash_only_coins.includes(coin_from) && secret_hash_only_coins.includes(coin_to)) {
|
||||
swap_type.disabled = true;
|
||||
swap_type.value = 'seller_first';
|
||||
make_hidden = true;
|
||||
swap_type.classList.add('select-disabled');
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const coin_from = document.getElementById('coin_from');
|
||||
const coin_to = document.getElementById('coin_to');
|
||||
|
||||
if (coin_from && coin_to) {
|
||||
coin_from.addEventListener('change', function() {
|
||||
const swap_type = document.getElementById('swap_type');
|
||||
set_swap_type_enabled(this.value, coin_to.value, swap_type);
|
||||
});
|
||||
|
||||
coin_to.addEventListener('change', function() {
|
||||
const swap_type = document.getElementById('swap_type');
|
||||
set_swap_type_enabled(coin_from.value, this.value, swap_type);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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 {
|
||||
swap_type.disabled = false;
|
||||
swap_type.classList.remove('select-disabled');
|
||||
params += '&rate=' + rate + '&amt_from=' + amt_from;
|
||||
}
|
||||
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_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') {
|
||||
} else if (lock_rate && value_changed == 'amt_to') {
|
||||
if (amt_to == '' || rate == '') {
|
||||
return;
|
||||
}
|
||||
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') { // 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;
|
||||
params += '&amt_to=' + amt_to + '&rate=' + rate;
|
||||
} else {
|
||||
if (amt_from == '' || amt_to == '') {
|
||||
return;
|
||||
}
|
||||
xhr_rate.open('POST', '/json/rate');
|
||||
xhr_rate.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhr_rate.send(params);
|
||||
params += '&amt_from=' + amt_from + '&amt_to=' + amt_to;
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
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>
|
||||
xhr_rate.open('POST', '/json/rate');
|
||||
xhr_rate.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhr_rate.send(params);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
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>
|
||||
<script src="static/js/new_offer.js"></script>
|
||||
{% include 'footer.html' %}
|
||||
|
|
|
@ -175,6 +175,8 @@
|
|||
</div>
|
||||
</div>
|
||||
{% 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">
|
||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee From Confirm Target</p>
|
||||
<div class="relative">
|
||||
|
@ -199,6 +201,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</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>
|
||||
</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">
|
||||
<p class="mb-1.5 font-medium text-base text-coolGray-800 dark:text-white">Fee To Confirm Target</p>
|
||||
<div class="relative">
|
||||
|
@ -259,6 +264,7 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -273,7 +279,7 @@
|
|||
<div class="w-full md:flex-1 p-3">
|
||||
<div class="flex flex-wrap -m-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="flex absolute inset-y-0 left-0 items-center pl-3 pointer-events-none">
|
||||
{{ select_bid_amount_svg | safe }}
|
||||
|
@ -449,4 +455,4 @@
|
|||
</div>
|
||||
{% include 'footer.html' %}
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -11,108 +11,88 @@
|
|||
<script>
|
||||
function getAPIKeys() {
|
||||
return {
|
||||
cryptoCompare: '{{chart_api_key}}',
|
||||
coinGecko: '{{coingecko_api_key}}'
|
||||
cryptoCompare: "{{ chart_api_key|safe }}",
|
||||
coinGecko: "{{ coingecko_api_key|safe }}"
|
||||
};
|
||||
}
|
||||
|
||||
function getWebSocketConfig() {
|
||||
return {
|
||||
port: "{{ ws_port|safe }}",
|
||||
fallbackPort: "11700"
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
{% if sent_offers %}
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-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>{{ 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="{% if page_type == 'offers' %}/offers{% elif page_type == 'sentoffers' %}/sentoffers{% endif %}">
|
||||
{{ page_type }}
|
||||
</a>
|
||||
</li>
|
||||
<li>{{ breadcrumb_line_svg | safe }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</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>
|
||||
<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-gray-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">{{ page_type }}</h2>
|
||||
<p class="font-normal text-coolGray-200 dark:text-white">{{ page_type_description }}</p>
|
||||
</div>
|
||||
<div class="w-full md:w-1/2 p-3 flex justify-end items-center hidden">
|
||||
<a id="refresh" href="/newoffer"
|
||||
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">
|
||||
{{ place_new_offer_svg | safe }}
|
||||
<span>Place new Offer</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{% include 'inc_messages.html' %}
|
||||
|
||||
{% if show_chart %}
|
||||
<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="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="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="flex justify-between items-center mb-4 mr-10 ml-10">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-xl font-bold dark:text-white" id="chart-title">Price Chart</h2>
|
||||
<div class="flex items-center space-x-4">
|
||||
<button id="resolution-year" class="ml-5 resolution-button">1Y</button>
|
||||
<button id="resolution-sixMonths" class="resolution-button">6M</button>
|
||||
<button id="resolution-day" class="resolution-button">24H</button>
|
||||
<button id="resolution-year" class="ml-5 resolution-button">1Y</button>
|
||||
<button id="resolution-sixMonths" class="resolution-button">6M</button>
|
||||
<button id="resolution-day" class="resolution-button">24H</button>
|
||||
</div>
|
||||
</div>
|
||||
<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="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="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>
|
||||
</span>
|
||||
</span>
|
||||
<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-value"></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">
|
||||
Toggle Coin Volume
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
<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>
|
||||
<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">
|
||||
<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>
|
||||
</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">
|
||||
Refresh Charts/Prices & Clear Cache
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
<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>
|
||||
<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">
|
||||
<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" />
|
||||
</svg>
|
||||
</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">
|
||||
Auto Refresh Enable/Disable
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
<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>
|
||||
<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">
|
||||
<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 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="error-content bg-white dark:bg-gray-800 rounded-lg p-6 w-full max-w-2xl mx-4 relative">
|
||||
<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">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<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-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 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 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-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" />
|
||||
</svg>
|
||||
</button>
|
||||
<p class="text-red-600 font-semibold text-xl mb-4">Error</p>
|
||||
<p id="error-message" class="text-gray-700 dark:text-gray-300 text-lg mb-6"></p>
|
||||
<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>.
|
||||
</p>
|
||||
<div class="flex items-center gap-3 mb-4">
|
||||
<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">
|
||||
<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" />
|
||||
</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>
|
||||
|
@ -150,9 +137,9 @@ function getAPIKeys() {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section class="py-4 flex flex-wrap justify-center overflow-hidden container-to-blur">
|
||||
<div class="container px-4 mx-auto">
|
||||
<div class="flex flex-wrap justify-center -m-3" id="coin-container">
|
||||
<section class="py-4 px-3 flex-wrap overflow-hidden container-to-blur">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="flex flex-wrap justify-center lg:justify-start xl:justify-center" id="coin-container">
|
||||
{% set coin_data = {
|
||||
'BTC': {'name': 'Bitcoin', 'symbol': 'BTC', 'image': 'Bitcoin.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},
|
||||
'DASH': {'name': 'Dash', 'symbol': 'DASH', 'image': 'Dash.png', 'show': true},
|
||||
'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},
|
||||
'ZANO': {'name': 'Zano', 'symbol': 'ZANO', 'image': 'Zano.png', 'show': false},
|
||||
'WOW': {'name': 'Wownero', 'symbol': 'WOW', 'image': 'Wownero.png', 'show': true}
|
||||
|
@ -185,9 +172,9 @@ function getAPIKeys() {
|
|||
|
||||
{% for coin_symbol in custom_order %}
|
||||
{% 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="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'] }}">
|
||||
<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 %}
|
||||
|
@ -206,12 +193,13 @@ function getAPIKeys() {
|
|||
</div>
|
||||
{% 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 %}">
|
||||
<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>
|
||||
{% endif %}
|
||||
<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>
|
||||
<div id="{{ coin_symbol.lower() }}-volume-24h"></div>
|
||||
<span class="bold mr-2 ml-1">VOL:</span>
|
||||
<span id="{{ coin_symbol.lower() }}-volume-24h"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -226,17 +214,17 @@ function getAPIKeys() {
|
|||
<script src="/static/js/pricechart.js"></script>
|
||||
|
||||
<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="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">
|
||||
<form method="post" id="filterForm">
|
||||
<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="container flex flex-wrap">
|
||||
<div class="md:w-auto hover-container justify-center">
|
||||
<div class="flex flex-wrap justify-center">
|
||||
<div class="lg:container flex flex-wrap justify-center">
|
||||
<div class="md:w-auto hover-container">
|
||||
<div class="flex flex-wrap">
|
||||
<div class="pt-3 px-3 md:w-auto hover-container">
|
||||
<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>
|
||||
|
@ -283,14 +271,26 @@ function getAPIKeys() {
|
|||
{% endif %}
|
||||
</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">
|
||||
<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>
|
||||
</button>
|
||||
</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">
|
||||
<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">
|
||||
|
@ -300,13 +300,6 @@ function getAPIKeys() {
|
|||
</button>
|
||||
</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>
|
||||
|
@ -319,18 +312,20 @@ function getAPIKeys() {
|
|||
</section>
|
||||
|
||||
<section>
|
||||
<div id="jsonView" class="hidden mb-4">
|
||||
<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="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 pb-6 overflow-x-auto">
|
||||
<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" 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="sort-icon ml-1 text-gray-600 dark:text-gray-400" id="sort-icon-0">↓</span>
|
||||
</div>
|
||||
|
@ -340,37 +335,29 @@ function getAPIKeys() {
|
|||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Details</span>
|
||||
</div>
|
||||
</th>
|
||||
{% if sent_offers %}
|
||||
<th class="p-0">
|
||||
<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>
|
||||
</div>
|
||||
</th>
|
||||
{% else %}
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-left">
|
||||
{% else %}
|
||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold">Max Send</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</th>
|
||||
{% endif %}
|
||||
<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>
|
||||
{% if sent_offers %}
|
||||
<th class="p-0">
|
||||
<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>
|
||||
</div>
|
||||
</th>
|
||||
{% else %}
|
||||
<th class="p-0">
|
||||
<div class="py-3 px-4 bg-coolGray-200 dark:bg-gray-600 text-right">
|
||||
{% else %}
|
||||
<span class="text-sm text-gray-600 dark:text-gray-300 font-semibold pr-2">Max Recv</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</th>
|
||||
{% endif %}
|
||||
<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">
|
||||
<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>
|
||||
</div>
|
||||
</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">
|
||||
<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>
|
||||
</th>
|
||||
</tr>
|
||||
|
@ -400,11 +386,18 @@ function getAPIKeys() {
|
|||
<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">
|
||||
<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 class="ml-4" data-listing-label>Network Listings: </span><span id="newEntriesCount"></span></p>
|
||||
<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></p>
|
||||
</div>
|
||||
<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">
|
||||
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">
|
||||
<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 }}
|
||||
|
@ -423,6 +416,5 @@ function getAPIKeys() {
|
|||
</section>
|
||||
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
|
||||
<script src="/static/js/offerstable.js"></script>
|
||||
<script src="/static/js/offers.js"></script>
|
||||
{% include 'footer.html' %}
|
||||
|
|
|
@ -281,6 +281,13 @@
|
|||
</th>
|
||||
</tr>
|
||||
</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">
|
||||
<td class="py-3 px-6 bold w-96 bold">Debug Mode</td>
|
||||
<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/style.css" rel="stylesheet">
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/libs/flowbite.js"></script>
|
||||
<script>
|
||||
const isDarkMode =
|
||||
localStorage.getItem('color-theme') === 'dark' ||
|
||||
|
|
|
@ -1,36 +1,37 @@
|
|||
{% 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 %}
|
||||
<script src="/static/js/libs//qrcode.js"></script>
|
||||
<div class="container mx-auto">
|
||||
<section class="p-5 mt-5">
|
||||
<div class="container mx-auto">
|
||||
<div class="flex flex-wrap items-center -m-2">
|
||||
<div class="w-full md:w-1/2 p-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><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><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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-4">
|
||||
<div class="container px-4 mx-auto">
|
||||
<section class="py-4 px-6">
|
||||
<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 z-20 flex flex-wrap items-center -m-3">
|
||||
<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>
|
||||
</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>
|
||||
</section>
|
||||
{% include 'inc_messages.html' %}
|
||||
{% if w.updating %}
|
||||
<section class="py-4" id="messages_updating" role="alert">
|
||||
<div class="container px-4 mx-auto">
|
||||
<section class="py-4 px-6" id="messages_updating" role="alert">
|
||||
<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="flex flex-wrap justify-between items-center -m-2">
|
||||
<div class="flex-1 p-2">
|
||||
|
@ -39,8 +40,8 @@
|
|||
{{ circular_update_messages_svg | safe }}
|
||||
</div>
|
||||
<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-medium text-sm text-blue-500">Please wait...</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-lg lg:text-sm text-blue-500">Please wait...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -56,8 +57,8 @@
|
|||
{% endif %}
|
||||
{% if w.havedata %}
|
||||
{% if w.error %}
|
||||
<section class="py-4" id="messages_error" role="alert">
|
||||
<div class="container px-4 mx-auto">
|
||||
<section class="py-4 px-6" id="messages_error" role="alert">
|
||||
<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="flex flex-wrap justify-between items-center -m-2">
|
||||
<div class="flex-1 p-2">
|
||||
|
@ -66,8 +67,8 @@
|
|||
{{ circular_error_messages_svg | safe }}
|
||||
</div>
|
||||
<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-medium text-sm text-red-500 error_msg">{{ w.error }}</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-lg lg:text-sm text-red-500 error_msg">{{ w.error }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -82,24 +83,43 @@
|
|||
</div>
|
||||
</section>
|
||||
{% else %}
|
||||
<form method="post" autocomplete="off">
|
||||
<section>
|
||||
<div class="pl-6 pr-6 pt-0 pb-0 mt-5 h-full overflow-hidden">
|
||||
{% if w.cid == '18' %} {# DOGE #}
|
||||
<section class="py-4 px-6" id="messages_notice">
|
||||
<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="flex flex-wrap items-center justify-between -m-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="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<div class="w-full pb-6 overflow-x-auto">
|
||||
<table class="w-full text-lg lg:text-sm">
|
||||
<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">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 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>
|
||||
</tr>
|
||||
</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>
|
||||
{% endif %}
|
||||
</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">
|
||||
<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>)
|
||||
|
@ -128,11 +149,10 @@
|
|||
</td>
|
||||
<td class="usd-value"></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{# / 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">
|
||||
<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>)
|
||||
{% 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>
|
||||
|
@ -144,7 +164,7 @@
|
|||
{% if w.locked_utxos %}
|
||||
<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 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>
|
||||
{% endif %}
|
||||
{# / locked_utxos #}
|
||||
|
@ -157,7 +177,7 @@
|
|||
<td class="py-3 px-6">{{ w.version }}</td>
|
||||
</tr>
|
||||
<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 }}
|
||||
{% if w.known_block_count %} / {{ w.known_block_count }}
|
||||
{% endif %}
|
||||
|
@ -187,10 +207,12 @@
|
|||
</tr>
|
||||
{% endif %}
|
||||
{# / 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">
|
||||
<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>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -201,16 +223,17 @@
|
|||
</div>
|
||||
</div>
|
||||
</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="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="lg:container mt-5 mx-auto">
|
||||
<div class="py-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="px-6">
|
||||
{% if w.cid != '4' %} {# DCR #}
|
||||
<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>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -220,127 +243,57 @@
|
|||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% else %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<section class="p-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="flex items-center">
|
||||
<h4 class="font-semibold text-2xl text-black dark:text-white">Deposit</h4>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<form method="post" autocomplete="off">
|
||||
<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="flex flex-wrap items-center justify-between -m-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="px-6">
|
||||
<div class="container mx-auto">
|
||||
<div class="flex flex-wrap max-w-7xl mx-auto -m-3">
|
||||
{% if w.cid == '1' %} {# PART #}
|
||||
<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-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 class="flex flex-wrap max-w-7xl mx-auto justify-center -m-3">
|
||||
<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">
|
||||
{% if w.cid in '6, 9' %}
|
||||
{# XMR | WOW #}
|
||||
<div id="qrcode-monero-main" class="qrcode"></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 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 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">
|
||||
<p>Copy to clipboard</p>
|
||||
<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="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">
|
||||
{% else %}
|
||||
<div id="qrcode-deposit" class="qrcode"> </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 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 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 Deposit Address"> {{ circular_arrows_svg }} New {{ w.name }} Deposit Address </button>
|
||||
</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>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
|
@ -348,38 +301,67 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if w.cid == '3' %}
|
||||
{# LTC #}
|
||||
{% if w.cid in '1, 3, 6, 9' %}
|
||||
{# PART | LTC | 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">
|
||||
{% 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>
|
||||
</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 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>
|
||||
</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="newmwebaddr_{{ w.cid }}" value="New MWEB Address"> {{ circular_arrows_svg }} New MWEB Address </button>
|
||||
</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>
|
||||
<div class="tooltip-arrow" data-popper-arrow></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{# / LTC #}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -401,8 +383,7 @@
|
|||
correctLevel: QRCode.CorrectLevel.L
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% if w.cid == '3' %}
|
||||
{% elif w.cid == '3' %}
|
||||
{# LTC #}
|
||||
<script>
|
||||
// Litecoin MWEB
|
||||
|
@ -520,61 +501,67 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
});
|
||||
</script>
|
||||
<section class="p-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="flex items-center">
|
||||
<h4 class="font-semibold text-2xl text-black dark:text-white">Withdraw</h4>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</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="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="container mt-5 mx-auto">
|
||||
<div class="pt-6 pb-6 bg-coolGray-100 dark:bg-gray-500 rounded-xl">
|
||||
<div class="lg:container mt-5 mx-auto">
|
||||
<div class="py-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 text-sm">
|
||||
<div class="w-full pb-6 overflow-x-auto">
|
||||
<table class="w-full text-lg lg:text-sm">
|
||||
<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">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 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>
|
||||
</tr>
|
||||
</thead>
|
||||
<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>
|
||||
</tr>
|
||||
{% if w.cid == '3' %}
|
||||
{# LTC #}
|
||||
<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>
|
||||
{% endif %}
|
||||
{% if w.cid == '1' %}
|
||||
</tr>
|
||||
{% elif w.cid == '1' %}
|
||||
{# PART #}
|
||||
<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>
|
||||
</tr>
|
||||
<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-3 px-6" data-coinname="{{ w.name }}">{{ w.anon_balance }} {{ w.ticker }} </td> {% endif %}
|
||||
<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>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<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-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 class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
<td class="py-4 pl-6 bold"> {{ w.name }} Amount:
|
||||
<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">
|
||||
{% if w.cid == '1' %}
|
||||
{# 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="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="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="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="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-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>
|
||||
function setAmount(percent, balance, cid, blindBalance, anonBalance) {
|
||||
var amountInput = document.getElementById('amount');
|
||||
|
@ -609,9 +596,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
{# / PART #}
|
||||
{% elif w.cid == '3' %}
|
||||
{# 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="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="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="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="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-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }}, '{{ w.mweb_balance }}')">100%</button>
|
||||
<script>
|
||||
function setAmount(percent, balance, cid, mwebBalance) {
|
||||
var amountInput = document.getElementById('amount');
|
||||
|
@ -642,9 +629,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</script>
|
||||
{# / LTC #}
|
||||
{% 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="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="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="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="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-lg lg:text-sm rounded-md focus:outline-none" onclick="setAmount(1, '{{ w.balance }}', {{ w.cid }})">100%</button>
|
||||
<script>
|
||||
function setAmount(percent, balance, cid) {
|
||||
var amountInput = document.getElementById('amount');
|
||||
|
@ -691,19 +678,21 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</script>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100">
|
||||
{% if w.cid in '6, 9' %} {# XMR | WOW #}
|
||||
<td class="py-3 px-6 bold">Sweep All:</td>
|
||||
<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" id="sweepall" name="sweepall_{{ w.cid }}" {% if w.wd_sweepall==true %} checked=checked{% endif %}>
|
||||
<td class="hidden py-3 px-6 bold">Sweep All:</td>
|
||||
<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 %}>
|
||||
</td>
|
||||
{% else %}
|
||||
<td class="py-3 px-6 bold">Subtract Fee:</td>
|
||||
<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>
|
||||
{% endif %}
|
||||
<td>
|
||||
|
@ -737,9 +726,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{# / PART #}
|
||||
{% if w.cid == '3' %} {# LTC #}
|
||||
{% elif w.cid == '3' %} {# LTC #}
|
||||
<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">
|
||||
|
@ -753,14 +741,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</tr>
|
||||
{% endif %}
|
||||
{# / LTC #}
|
||||
{% if w.cid not in '6,9' %} {# Not XMR WOW #}
|
||||
<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">{{ w.fee_rate }}</td>
|
||||
</tr>
|
||||
<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>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -772,25 +762,23 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</div>
|
||||
</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="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="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="lg:container mx-auto">
|
||||
<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="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' %}
|
||||
{# 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 %}
|
||||
{# / 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>
|
||||
|
@ -798,42 +786,43 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{% if w.cid not in '6, 9' %}
|
||||
{# !XMR | WOW #}
|
||||
{% if w.show_utxo_groups %}
|
||||
<section class="p-6">
|
||||
<div class="lg:container mx-auto">
|
||||
<div class="flex items-center">
|
||||
<h4 class="font-semibold text-2xl text-black dark:text-white">UTXO Groups</h4>
|
||||
</div>
|
||||
</div>
|
||||
</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="flex flex-wrap items-center justify-between -m-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="px-6">
|
||||
<div class="w-full mt-6 pb-6 overflow-x-auto">
|
||||
<table class="w-full min-w-max text-sm">
|
||||
<div class="w-full pb-6 overflow-x-auto">
|
||||
<table class="w-full text-lg lg:text-sm">
|
||||
<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">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 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>
|
||||
</tr>
|
||||
</thead>
|
||||
<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"> <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 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"> <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"> <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-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>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -846,14 +835,14 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
</div>
|
||||
</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="flex flex-wrap items-center justify-between -m-2">
|
||||
<div class="w-full pt-2">
|
||||
<div class="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="lg:container mx-auto">
|
||||
<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="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>
|
||||
|
@ -867,7 +856,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<input type="hidden" name="formid" value="{{ form_id }}">
|
||||
</form>
|
||||
<script>
|
||||
|
@ -879,6 +867,7 @@ const coinNameToSymbol = {
|
|||
'Monero': 'XMR',
|
||||
'Wownero': 'WOW',
|
||||
'Litecoin': 'LTC',
|
||||
'Dogecoin': 'DOGE',
|
||||
'Firo': 'FIRO',
|
||||
'Dash': 'DASH',
|
||||
'PIVX': 'PIVX',
|
||||
|
|
|
@ -230,6 +230,7 @@ const COIN_SYMBOLS = {
|
|||
'Monero': 'monero',
|
||||
'Wownero': 'wownero',
|
||||
'Litecoin': 'litecoin',
|
||||
'Dogecoin': 'dogecoin',
|
||||
'Firo': 'zcoin',
|
||||
'Dash': 'dash',
|
||||
'PIVX': 'pivx',
|
||||
|
|
|
@ -9,12 +9,14 @@ from .util import (
|
|||
PAGE_LIMIT,
|
||||
describeBid,
|
||||
get_data_entry,
|
||||
have_data_entry,
|
||||
get_data_entry_or,
|
||||
have_data_entry,
|
||||
listAvailableCoins,
|
||||
listBidActions,
|
||||
listBidStates,
|
||||
listOldBidStates,
|
||||
set_pagination_filters,
|
||||
setCoinFilter,
|
||||
)
|
||||
from basicswap.util import (
|
||||
ensure,
|
||||
|
@ -149,13 +151,12 @@ def page_bid(self, url_split, post_string):
|
|||
)
|
||||
|
||||
|
||||
def page_bids(
|
||||
self, url_split, post_string, sent=False, available=False, received=False
|
||||
):
|
||||
def page_bids(self, url_split, post_string, sent=False, available=False, received=False):
|
||||
server = self.server
|
||||
swap_client = server.swap_client
|
||||
swap_client.checkSystemStatus()
|
||||
summary = swap_client.getSummary()
|
||||
filter_key = "page_available_bids" if available else "page_bids"
|
||||
|
||||
filters = {
|
||||
"page_no": 1,
|
||||
|
@ -164,31 +165,25 @@ def page_bids(
|
|||
"limit": PAGE_LIMIT,
|
||||
"sort_by": "created_at",
|
||||
"sort_dir": "desc",
|
||||
"coin_from": -1,
|
||||
"coin_to": -1,
|
||||
}
|
||||
if available:
|
||||
filters["bid_state_ind"] = BidStates.BID_RECEIVED
|
||||
filters["with_expired"] = False
|
||||
|
||||
filter_prefix = (
|
||||
"page_bids_sent"
|
||||
if sent
|
||||
else "page_bids_available" if available else "page_bids_received"
|
||||
)
|
||||
messages = []
|
||||
form_data = self.checkForm(post_string, "bids", messages)
|
||||
if form_data:
|
||||
if have_data_entry(form_data, "clearfilters"):
|
||||
swap_client.clearFilters(filter_prefix)
|
||||
swap_client.clearFilters(filter_key)
|
||||
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"):
|
||||
sort_by = get_data_entry(form_data, "sort_by")
|
||||
ensure(
|
||||
sort_by
|
||||
in [
|
||||
"created_at",
|
||||
],
|
||||
"Invalid sort by",
|
||||
)
|
||||
ensure(sort_by in ["created_at"], "Invalid sort by")
|
||||
filters["sort_by"] = sort_by
|
||||
if have_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:
|
||||
try:
|
||||
_ = BidStates(state_ind)
|
||||
except Exception as e: # noqa: F841
|
||||
except Exception:
|
||||
raise ValueError("Invalid state")
|
||||
filters["bid_state_ind"] = state_ind
|
||||
if have_data_entry(form_data, "with_expired"):
|
||||
|
@ -208,38 +203,64 @@ def page_bids(
|
|||
|
||||
set_pagination_filters(form_data, filters)
|
||||
if have_data_entry(form_data, "applyfilters"):
|
||||
swap_client.setFilters(filter_prefix, filters)
|
||||
swap_client.setFilters(filter_key, filters)
|
||||
else:
|
||||
saved_filters = swap_client.getFilters(filter_prefix)
|
||||
saved_filters = swap_client.getFilters(filter_key)
|
||||
if saved_filters:
|
||||
filters.update(saved_filters)
|
||||
|
||||
bids = swap_client.listBids(sent=sent, filters=filters)
|
||||
|
||||
page_data = {
|
||||
"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")
|
||||
return self.render_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,
|
||||
"filters": filters,
|
||||
"data": page_data,
|
||||
"summary": summary,
|
||||
"bids": [
|
||||
"filter_key": filter_key,
|
||||
"coins_from": coins_from,
|
||||
"coins": coins_to,
|
||||
"sent_bids": [
|
||||
(
|
||||
format_timestamp(b[0]),
|
||||
b[2].hex(),
|
||||
|
@ -249,8 +270,22 @@ def page_bids(
|
|||
strTxState(b[8]),
|
||||
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
|
||||
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:
|
||||
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"):
|
||||
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)
|
||||
|
||||
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_strategies = swap_client.listAutomationStrategies(automation_filters)
|
||||
|
||||
|
@ -556,8 +565,8 @@ def page_newoffer(self, url_split, post_string):
|
|||
"err_messages": err_messages,
|
||||
"coins_from": coins_from,
|
||||
"coins": coins_to,
|
||||
"addrs": swap_client.listSMSGAddresses("offer_send_from"),
|
||||
"addrs_to": swap_client.listSMSGAddresses("offer_send_to"),
|
||||
"addrs": addrs_from,
|
||||
"addrs_to": addrs_to,
|
||||
"data": page_data,
|
||||
"automation_strategies": automation_strategies,
|
||||
"summary": summary,
|
||||
|
@ -581,7 +590,7 @@ def page_offer(self, url_split, post_string):
|
|||
offer, xmr_offer = swap_client.getXmrOffer(offer_id)
|
||||
ensure(offer, "Unknown offer ID")
|
||||
|
||||
extend_data = { # Defaults
|
||||
extend_data = {
|
||||
"nb_validmins": 10,
|
||||
}
|
||||
messages = []
|
||||
|
@ -598,7 +607,6 @@ def page_offer(self, url_split, post_string):
|
|||
|
||||
reverse_bid: bool = True if offer.bid_reversed else False
|
||||
|
||||
# Set defaults
|
||||
debugind = -1
|
||||
bid_amount = ci_from.format_amount(offer.amount_from)
|
||||
bid_rate = ci_to.format_amount(offer.rate)
|
||||
|
@ -617,7 +625,6 @@ def page_offer(self, url_split, post_string):
|
|||
except Exception as ex:
|
||||
err_messages.append("Revoke offer failed: " + str(ex))
|
||||
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_header("Location", "/newoffer?offer_from=" + offer_id.hex())
|
||||
self.end_headers()
|
||||
|
|
|
@ -122,8 +122,30 @@ def set_pagination_filters(form_data, filters):
|
|||
filters["page_no"] = 1
|
||||
elif form_data and have_data_entry(form_data, "pageforwards"):
|
||||
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):
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2022-2024 tecnovert
|
||||
# Copyright (c) 2022-2025 tecnovert
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# 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 Crypto.Hash import HMAC, RIPEMD160, SHA256, SHA512 # pycryptodome
|
||||
from Crypto.Hash import HMAC, RIPEMD160, SHA512 # pycryptodome
|
||||
|
||||
|
||||
def sha256(data: bytes) -> bytes:
|
||||
h = SHA256.new()
|
||||
h = hashlib_sha256()
|
||||
h.update(data)
|
||||
return h.digest()
|
||||
|
||||
|
|
42
basicswap/util/logging.py
Normal file
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):
|
||||
|
||||
export SWAP_DATADIR=/Users/$USER/coinswaps
|
||||
export SWAP_DATADIR=$HOME/coinswaps
|
||||
basicswap-prepare --usebtcfastsync --datadir=/$SWAP_DATADIR --addcoin=bitcoin
|
||||
|
||||
|
||||
Start after installed:
|
||||
|
||||
export SWAP_DATADIR=/Users/$USER/coinswaps
|
||||
|
||||
export SWAP_DATADIR=$HOME/coinswaps
|
||||
. $SWAP_DATADIR/venv/bin/activate && python -V
|
||||
basicswap-run --datadir=$SWAP_DATADIR
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
version: '3.4'
|
||||
services:
|
||||
swapclient:
|
||||
image: i_swapclient
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
version: '3.4'
|
||||
services:
|
||||
|
||||
swapclient:
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
version: '3.3'
|
||||
|
||||
networks:
|
||||
default:
|
||||
|
|
16
docker/production/compose-fragments/1_dogecoin.yml
Normal file
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
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
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; \
|
||||
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_DIR=basicswap-master
|
||||
|
|
6
guix.scm
6
guix.scm
|
@ -114,15 +114,15 @@
|
|||
(define-public basicswap
|
||||
(package
|
||||
(name "basicswap")
|
||||
(version "0.14.1")
|
||||
(version "0.14.3")
|
||||
(source (origin
|
||||
(method git-fetch)
|
||||
(uri (git-reference
|
||||
(url "https://github.com/basicswap/basicswap")
|
||||
(commit "062cc6dbdc3c1f489d2bf78ce7cd99fbc885f14e")))
|
||||
(commit "3b60472c04a58f26e33665f0eb0e88a558050c74")))
|
||||
(sha256
|
||||
(base32
|
||||
"16m61d45rn4lzvximsnkvrdg4hfsdk4460lhyarixjcdzknh1z1z"))
|
||||
"0xrli8mzigm0ryn28y28xvy4gc0358ck2036ncx5f1sj5s8dwfkh"))
|
||||
(file-name (git-file-name name version))))
|
||||
(build-system pyproject-build-system)
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue