mirror of
https://github.com/basicswap/basicswap.git
synced 2026-05-07 06:52:12 +02:00
Merge pull request #466 from tecnovert/mweb_change_helper
LTC MWEB change back to LTC helper functions
This commit is contained in:
@@ -92,7 +92,9 @@ class LTCInterface(BTCInterface):
|
|||||||
if "address" not in u:
|
if "address" not in u:
|
||||||
continue
|
continue
|
||||||
utxo_address: str = u["address"]
|
utxo_address: str = u["address"]
|
||||||
if any(utxo_address.startswith(prefix) for prefix in ("mweb1", "tmweb1")):
|
if any(
|
||||||
|
utxo_address.startswith(prefix) for prefix in ("ltcmweb1", "tmweb1")
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
if "desc" in u:
|
if "desc" in u:
|
||||||
desc = u["desc"]
|
desc = u["desc"]
|
||||||
@@ -111,6 +113,76 @@ class LTCInterface(BTCInterface):
|
|||||||
) + self.make_int(u["amount"], r=1)
|
) + self.make_int(u["amount"], r=1)
|
||||||
return unspent_addr
|
return unspent_addr
|
||||||
|
|
||||||
|
def getMWEBBalance(self) -> int:
|
||||||
|
if self.useBackend():
|
||||||
|
raise ValueError("MWEB not supported in electrum mode")
|
||||||
|
|
||||||
|
value: int = 0
|
||||||
|
unspent = self.rpc_wallet(
|
||||||
|
"listunspent",
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
for u in unspent:
|
||||||
|
if "address" not in u:
|
||||||
|
continue
|
||||||
|
utxo_address: str = u["address"]
|
||||||
|
if any(
|
||||||
|
utxo_address.startswith(prefix) for prefix in ("ltcmweb1", "tmweb1")
|
||||||
|
):
|
||||||
|
value += self.make_int(u["amount"], r=1)
|
||||||
|
return value
|
||||||
|
|
||||||
|
def convertMWEBBalance(self):
|
||||||
|
if self.useBackend():
|
||||||
|
raise ValueError("MWEB not supported in electrum mode")
|
||||||
|
|
||||||
|
self._log.info(f"convertMWEBBalance - {self.ticker()}")
|
||||||
|
locked_before = self.rpc_wallet("listlockunspent")
|
||||||
|
lock_utxos = []
|
||||||
|
try:
|
||||||
|
# Hack: mark all the other utxos as unspendable, alternative is to use a mweb_transfer wallet
|
||||||
|
utxos = self.rpc_wallet("listunspent")
|
||||||
|
mweb_amount: int = 0
|
||||||
|
for utxo in utxos:
|
||||||
|
utxo_address: str = utxo.get("address", "")
|
||||||
|
if any(
|
||||||
|
utxo_address.startswith(prefix) for prefix in ("ltcmweb1", "tmweb1")
|
||||||
|
):
|
||||||
|
mweb_amount += self.make_int(utxo["amount"], r=1)
|
||||||
|
continue
|
||||||
|
utxo_op = {"txid": utxo["txid"], "vout": utxo["vout"]}
|
||||||
|
if utxo_op in locked_before:
|
||||||
|
continue
|
||||||
|
lock_utxos.append(utxo_op)
|
||||||
|
|
||||||
|
if mweb_amount == 0:
|
||||||
|
raise ValueError("No MWEB outputs to convert")
|
||||||
|
self.rpc_wallet("lockunspent", [False, lock_utxos])
|
||||||
|
subfee_to_mweb: bool = True
|
||||||
|
convert_value = self.format_amount(mweb_amount)
|
||||||
|
plain_addr: str = self.rpc_wallet("getnewaddress", ["transfer", "bech32"])
|
||||||
|
|
||||||
|
# Double check generated address is owned by this wallet
|
||||||
|
if not self.isAddressMine(plain_addr):
|
||||||
|
raise ValueError("Generated address not owned by wallet!")
|
||||||
|
params = [
|
||||||
|
plain_addr,
|
||||||
|
convert_value,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
subfee_to_mweb,
|
||||||
|
True,
|
||||||
|
self._conf_target,
|
||||||
|
]
|
||||||
|
txid = self.rpc_wallet("sendtoaddress", params)
|
||||||
|
|
||||||
|
self._log.info(f"MWEB in plain converted in txid: {self._log.id(txid)}")
|
||||||
|
return txid
|
||||||
|
finally:
|
||||||
|
self.rpc_wallet("lockunspent", [True, lock_utxos])
|
||||||
|
|
||||||
def unlockWallet(self, password: str, check_seed: bool = True) -> None:
|
def unlockWallet(self, password: str, check_seed: bool = True) -> None:
|
||||||
if password == "":
|
if password == "":
|
||||||
return
|
return
|
||||||
|
|||||||
+13
-2
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright (c) 2020-2024 tecnovert
|
# Copyright (c) 2020-2024 tecnovert
|
||||||
# Copyright (c) 2024-2025 The Basicswap developers
|
# Copyright (c) 2024-2026 The Basicswap developers
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
@@ -129,7 +129,6 @@ def js_walletbalances(self, url_split, post_string, is_json) -> bytes:
|
|||||||
swap_client = self.server.swap_client
|
swap_client = self.server.swap_client
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
swap_client.updateWalletsInfo()
|
swap_client.updateWalletsInfo()
|
||||||
wallets = swap_client.getCachedWalletsInfo()
|
wallets = swap_client.getCachedWalletsInfo()
|
||||||
coins_with_balances = []
|
coins_with_balances = []
|
||||||
@@ -332,6 +331,18 @@ def js_wallets(self, url_split, post_string, is_json):
|
|||||||
return bytes(
|
return bytes(
|
||||||
json.dumps(swap_client.ci(coin_type).getNewMwebAddress()), "UTF-8"
|
json.dumps(swap_client.ci(coin_type).getNewMwebAddress()), "UTF-8"
|
||||||
)
|
)
|
||||||
|
elif cmd == "mwebbalance":
|
||||||
|
# mweb outputs left behind when sending LTC -> MWEB
|
||||||
|
if coin_type not in (Coins.LTC,):
|
||||||
|
raise ValueError("Invalid coin for command")
|
||||||
|
ci = swap_client.ci(coin_type)
|
||||||
|
return bytes(json.dumps(ci.format_amount(ci.getMWEBBalance())), "UTF-8")
|
||||||
|
elif cmd == "convertmweb":
|
||||||
|
if coin_type not in (Coins.LTC,):
|
||||||
|
raise ValueError("Invalid coin for command")
|
||||||
|
return bytes(
|
||||||
|
json.dumps(swap_client.ci(coin_type).convertMWEBBalance()), "UTF-8"
|
||||||
|
)
|
||||||
elif cmd == "watchaddress":
|
elif cmd == "watchaddress":
|
||||||
post_data = getFormData(post_string, is_json)
|
post_data = getFormData(post_string, is_json)
|
||||||
address = get_data_entry(post_data, "address")
|
address = get_data_entry(post_data, "address")
|
||||||
|
|||||||
@@ -40,6 +40,10 @@
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
confirmMWEBChangeConvert: function() {
|
||||||
|
return confirm('Confirm MWEB change conversion: This will create a tx sending all spendable MWEB outputs in the plain LTC wallet to LTC.');
|
||||||
|
},
|
||||||
|
|
||||||
confirmReseed: function() {
|
confirmReseed: function() {
|
||||||
return confirm('Are you sure you want to reseed the wallet? This will generate new addresses.');
|
return confirm('Are you sure you want to reseed the wallet? This will generate new addresses.');
|
||||||
},
|
},
|
||||||
@@ -60,7 +64,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
fillDonationAddress: function(address, coinType) {
|
fillDonationAddress: function(address, coinType) {
|
||||||
|
|
||||||
let addressInput = null;
|
let addressInput = null;
|
||||||
|
|
||||||
addressInput = window.DOMCache
|
addressInput = window.DOMCache
|
||||||
@@ -188,7 +192,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
lookup_rates: function() {
|
lookup_rates: function() {
|
||||||
|
|
||||||
if (window.lookup_rates && typeof window.lookup_rates === 'function') {
|
if (window.lookup_rates && typeof window.lookup_rates === 'function') {
|
||||||
window.lookup_rates();
|
window.lookup_rates();
|
||||||
} else {
|
} else {
|
||||||
@@ -282,6 +286,16 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
const target = e.target.closest('[data-confirm-mweb-change-convert]');
|
||||||
|
if (target) {
|
||||||
|
if (!this.confirmMWEBChangeConvert()) {
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
const target = e.target.closest('[data-confirm-utxo]');
|
const target = e.target.closest('[data-confirm-utxo]');
|
||||||
if (target) {
|
if (target) {
|
||||||
@@ -398,6 +412,7 @@
|
|||||||
|
|
||||||
window.EventHandlers = EventHandlers;
|
window.EventHandlers = EventHandlers;
|
||||||
window.confirmReseed = EventHandlers.confirmReseed.bind(EventHandlers);
|
window.confirmReseed = EventHandlers.confirmReseed.bind(EventHandlers);
|
||||||
|
window.confirmMWEBChangeConvert = EventHandlers.confirmMWEBChangeConvert.bind(EventHandlers);
|
||||||
window.confirmWithdrawal = EventHandlers.confirmWithdrawal.bind(EventHandlers);
|
window.confirmWithdrawal = EventHandlers.confirmWithdrawal.bind(EventHandlers);
|
||||||
window.confirmUTXOResize = EventHandlers.confirmUTXOResize.bind(EventHandlers);
|
window.confirmUTXOResize = EventHandlers.confirmUTXOResize.bind(EventHandlers);
|
||||||
window.confirmRemoveExpired = EventHandlers.confirmRemoveExpired.bind(EventHandlers);
|
window.confirmRemoveExpired = EventHandlers.confirmRemoveExpired.bind(EventHandlers);
|
||||||
|
|||||||
@@ -185,6 +185,17 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if w.mweb_in_plain %}
|
||||||
|
<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 }} MWEB"> </span>MWEB in Plain Balance: </td>
|
||||||
|
<td class="py-3 px-6 bold">
|
||||||
|
<span>{{ w.mweb_in_plain }} {{ w.ticker }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="py-3 px-6 bold">
|
||||||
|
<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="convertmweb_{{ w.cid }}" value="Convert" data-confirm-mweb-change-convert> Convert </button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% elif w.cid == '13' %} {# FIRO #}
|
{% elif w.cid == '13' %} {# FIRO #}
|
||||||
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
<tr class="opacity-100 text-gray-500 dark:text-gray-100 hover:bg-coolGray-200 dark:hover:bg-gray-600">
|
||||||
<td class="py-3 px-6 bold"> <span class="inline-flex align-middle items-center justify-center w-9 h-10 bg-white-50 rounded"> <img class="h-7" src="/static/images/coins/{{ w.name }}.png" alt="{{ w.name }} Spark"> </span>Spark 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 }} Spark"> </span>Spark Balance: </td>
|
||||||
|
|||||||
@@ -273,6 +273,9 @@ def page_wallet(self, url_split, post_string):
|
|||||||
swap_client.cacheNewAddressForCoin(coin_id)
|
swap_client.cacheNewAddressForCoin(coin_id)
|
||||||
elif have_data_entry(form_data, "forcerefresh"):
|
elif have_data_entry(form_data, "forcerefresh"):
|
||||||
force_refresh = True
|
force_refresh = True
|
||||||
|
elif have_data_entry(form_data, "convertmweb_" + cid):
|
||||||
|
txid = swap_client.ci(coin_id).convertMWEBBalance()
|
||||||
|
messages.append(f"Converted MWEB change to LTC in tx: {txid}")
|
||||||
elif have_data_entry(form_data, "newmwebaddr_" + cid):
|
elif have_data_entry(form_data, "newmwebaddr_" + cid):
|
||||||
swap_client.cacheNewStealthAddressForCoin(coin_id)
|
swap_client.cacheNewStealthAddressForCoin(coin_id)
|
||||||
elif have_data_entry(form_data, "newsparkaddr_" + cid):
|
elif have_data_entry(form_data, "newsparkaddr_" + cid):
|
||||||
@@ -525,6 +528,10 @@ def page_wallet(self, url_split, post_string):
|
|||||||
// page_data["fee_estimate"]["sum_weight"]
|
// page_data["fee_estimate"]["sum_weight"]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if k == Coins.LTC and ci.useBackend() is False:
|
||||||
|
mweb_value: int = ci.getMWEBBalance()
|
||||||
|
if mweb_value > 0:
|
||||||
|
wallet_data["mweb_in_plain"] = ci.format_amount(mweb_value)
|
||||||
if show_utxo_groups:
|
if show_utxo_groups:
|
||||||
utxo_groups = ""
|
utxo_groups = ""
|
||||||
unspent_by_addr = ci.getUnspentsByAddr()
|
unspent_by_addr = ci.getUnspentsByAddr()
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# LTC Notes
|
||||||
|
|
||||||
|
## MWEB
|
||||||
|
|
||||||
|
Sending LTC -> MWEB generates MWEB change outputs in the plain LTC wallet that BSX can't use.
|
||||||
|
A temporary convenience function is provided to convert those MWEB outputs back to plain LTC.
|
||||||
|
|
||||||
@@ -15,14 +15,15 @@ export XMR_RPC_USER=xmr_user
|
|||||||
export XMR_RPC_PWD=xmr_pwd
|
export XMR_RPC_PWD=xmr_pwd
|
||||||
python tests/basicswap/extended/test_xmr_persistent.py
|
python tests/basicswap/extended/test_xmr_persistent.py
|
||||||
|
|
||||||
|
|
||||||
# Copy coin releases to permanent storage for faster subsequent startups
|
# Copy coin releases to permanent storage for faster subsequent startups
|
||||||
cp -r ${TEST_PATH}/bin/* ~/tmp/basicswap_bin/
|
cp -r ${TEST_PATH}/bin/* ~/tmp/basicswap_bin/
|
||||||
|
|
||||||
|
|
||||||
# Continue existing chains with
|
# Continue existing chains with
|
||||||
export RESET_TEST=false
|
export RESET_TEST=false
|
||||||
|
|
||||||
|
# Set coins started
|
||||||
|
export TEST_COINS_LIST="bitcoin,monero,litecoin"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
|||||||
@@ -268,6 +268,62 @@ class TestLTC(BasicSwapTest):
|
|||||||
if "mweb1" in addr:
|
if "mweb1" in addr:
|
||||||
raise ValueError("getUnspentsByAddr should exclude mweb UTXOs.")
|
raise ValueError("getUnspentsByAddr should exclude mweb UTXOs.")
|
||||||
|
|
||||||
|
# Test helper functions to convert MWEB change
|
||||||
|
mweb_change_value = ci0.getMWEBBalance()
|
||||||
|
assert mweb_change_value > 0
|
||||||
|
|
||||||
|
test_lock_utxo = None
|
||||||
|
for utxo in utxos:
|
||||||
|
utxo_address: str = utxo.get("address", "")
|
||||||
|
if any(
|
||||||
|
utxo_address.startswith(prefix) for prefix in ("ltcmweb1", "tmweb1")
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
test_lock_utxo = {"txid": utxo["txid"], "vout": utxo["vout"]}
|
||||||
|
ci0.rpc_wallet(
|
||||||
|
"lockunspent",
|
||||||
|
[
|
||||||
|
False,
|
||||||
|
[
|
||||||
|
test_lock_utxo,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
break
|
||||||
|
assert len(ci0.rpc_wallet("listlockunspent")) == 1
|
||||||
|
|
||||||
|
txid = ci0.convertMWEBBalance()
|
||||||
|
|
||||||
|
# Check utxos locked before conversion are still locked after
|
||||||
|
assert len(ci0.rpc_wallet("listlockunspent")) == 1
|
||||||
|
ci0.rpc_wallet(
|
||||||
|
"lockunspent",
|
||||||
|
[
|
||||||
|
True,
|
||||||
|
[
|
||||||
|
test_lock_utxo,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert len(ci0.rpc_wallet("listlockunspent")) == 0
|
||||||
|
|
||||||
|
txj = ci0.rpc_wallet(
|
||||||
|
"gettransaction",
|
||||||
|
[
|
||||||
|
txid,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert len(txj["details"]) == 2
|
||||||
|
|
||||||
|
fee_amt = -ci0.make_int(txj["fee"])
|
||||||
|
assert txj["details"][0]["category"] == "send"
|
||||||
|
assert ci0.make_int(txj["details"][0]["amount"]) - fee_amt == -mweb_change_value
|
||||||
|
assert txj["details"][1]["category"] == "receive"
|
||||||
|
assert ci0.make_int(txj["details"][1]["amount"]) + fee_amt == mweb_change_value
|
||||||
|
|
||||||
|
mweb_change_value = ci0.getMWEBBalance()
|
||||||
|
assert mweb_change_value == 0
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
def test_22_mweb_balance(self):
|
def test_22_mweb_balance(self):
|
||||||
@@ -362,6 +418,20 @@ class TestLTC(BasicSwapTest):
|
|||||||
json_rv = read_json_api(TEST_HTTP_PORT + 0, "wallets/ltc", post_json)
|
json_rv = read_json_api(TEST_HTTP_PORT + 0, "wallets/ltc", post_json)
|
||||||
assert json_rv["mweb_balance"] <= 20.0
|
assert json_rv["mweb_balance"] <= 20.0
|
||||||
|
|
||||||
|
# Test helper functions to convert MWEB change
|
||||||
|
json_rv = read_json_api(
|
||||||
|
TEST_HTTP_PORT + 0, "wallets/ltc/mwebbalance", post_json
|
||||||
|
)
|
||||||
|
assert float(json_rv) > 0
|
||||||
|
json_rv = read_json_api(
|
||||||
|
TEST_HTTP_PORT + 0, "wallets/ltc/convertmweb", post_json
|
||||||
|
)
|
||||||
|
assert len(json_rv) == 64
|
||||||
|
json_rv = read_json_api(
|
||||||
|
TEST_HTTP_PORT + 0, "wallets/ltc/mwebbalance", post_json
|
||||||
|
)
|
||||||
|
assert float(json_rv) == 0
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user