diff --git a/electrum/commands.py b/electrum/commands.py index 9ff0eaa60..419d1c4b5 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -2016,7 +2016,7 @@ class Commands(Logger): result = {} for offer in offers: result[offer.server_npub] = { - "percentage_fee": offer.pairs.percentage, + "percentage_fee": float(offer.pairs.percentage), "max_forward_sat": offer.pairs.max_forward, "max_reverse_sat": offer.pairs.max_reverse, "min_amount_sat": offer.pairs.min_amount, diff --git a/electrum/gui/qml/qeswaphelper.py b/electrum/gui/qml/qeswaphelper.py index 984befb27..7475216c4 100644 --- a/electrum/gui/qml/qeswaphelper.py +++ b/electrum/gui/qml/qeswaphelper.py @@ -75,7 +75,7 @@ class QESwapServerNPubListModel(QAbstractListModel): return { 'npub': x.server_npub, 'server_pubkey': x.server_pubkey, - 'percentage_fee': x.pairs.percentage, + 'percentage_fee': float(x.pairs.percentage), 'mining_fee': x.pairs.mining_fee, 'min_amount': x.pairs.min_amount, 'max_forward_amount': x.pairs.max_forward, diff --git a/electrum/gui/qt/swap_dialog.py b/electrum/gui/qt/swap_dialog.py index 3cdc0843f..0f2d7c06b 100644 --- a/electrum/gui/qt/swap_dialog.py +++ b/electrum/gui/qt/swap_dialog.py @@ -534,8 +534,8 @@ class SwapServerDialog(WindowModalDialog, QtEventListener): vbox = QVBoxLayout() self.setLayout(vbox) vbox.addWidget(WWLabel(msg)) - vbox.addWidget(self.servers_list) - vbox.addStretch() + vbox.addWidget(self.servers_list, stretch=1) + vbox.addSpacing(10) self.ok_button = OkButton(self) vbox.addLayout(Buttons(CancelButton(self), self.ok_button)) self.setMinimumWidth(650) diff --git a/electrum/plugins/swapserver/__init__.py b/electrum/plugins/swapserver/__init__.py index d8b1934a5..a67041759 100644 --- a/electrum/plugins/swapserver/__init__.py +++ b/electrum/plugins/swapserver/__init__.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, List from electrum.simple_config import ConfigVar, SimpleConfig from electrum.commands import plugin_command +from electrum.address_synchronizer import TX_HEIGHT_UNCONFIRMED if TYPE_CHECKING: from electrum.commands import Commands @@ -19,7 +20,7 @@ SimpleConfig.SWAPSERVER_ANN_POW_NONCE = ConfigVar('plugins.swapserver.ann_pow_no @plugin_command('wl', plugin_name) async def get_history(self: 'Commands', wallet: 'Abstract_Wallet' = None, plugin = None) -> List[dict]: """ - Get a list of all swaps provided by this swapserver. + Get a list of all confirmed swaps provided by this swapserver. Single elements can potentially cover multiple swaps if transactions have been batched. Example result: @@ -42,12 +43,20 @@ async def get_history(self: 'Commands', wallet: 'Abstract_Wallet' = None, plugin assert wallet.lnworker, "lightning not available" assert wallet.lnworker.swap_manager, "swap manager not available" - full_history = wallet.get_full_history() - swap_group_ids = set( - x['group_id'] for x in wallet.lnworker.swap_manager.get_groups_for_onchain_history().values() - ) + sm = wallet.lnworker.swap_manager + swap_group_ids = set() + for swap in sm._swaps.values(): + group_id = swap.spending_txid if swap.is_reverse else swap.funding_txid + if group_id is None: + continue + if swap.spending_txid is None \ + or wallet.adb.get_tx_height(swap.spending_txid).height() <= TX_HEIGHT_UNCONFIRMED: + # get only final swaps so the history is stable and doesn't include pending swaps + continue + swap_group_ids.add(group_id) swap_history_items = [] + full_history = wallet.get_full_history() for swap_group_id in swap_group_ids: if swap_history_item := full_history.get('group:' + swap_group_id): swap_history_items.append(swap_history_item) @@ -66,7 +75,7 @@ async def get_history(self: 'Commands', wallet: 'Abstract_Wallet' = None, plugin @plugin_command('wl', plugin_name) async def get_summary(self: 'Commands', wallet: 'Abstract_Wallet' = None, plugin = None) -> dict: - """Get a summary of all swaps provided by this swapserver. + """Get a summary of all confirmed swaps provided by this swapserver. Can become incorrect if closed lightning channels have been deleted in this wallet. Example result: diff --git a/electrum/plugins/swapserver/server.py b/electrum/plugins/swapserver/server.py index b2163736e..7d645e7e8 100644 --- a/electrum/plugins/swapserver/server.py +++ b/electrum/plugins/swapserver/server.py @@ -95,7 +95,7 @@ class HttpSwapServer(Logger, EventListener): "minimal": sm._min_amount, }, "fees": { - "percentage": sm.percentage, + "percentage": float(sm.percentage), # cast to float for <= 4.7.1 backwards compatibility "minerFees": { "baseAsset": { "normal": sm.mining_fee, diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index 1a761230d..ef55aa632 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -168,7 +168,7 @@ def now(): @attr.s(frozen=True) class SwapFees: - percentage = attr.ib(type=int) + percentage = attr.ib(type=Decimal) mining_fee = attr.ib(type=int) min_amount = attr.ib(type=int) max_forward = attr.ib(type=int) @@ -235,7 +235,7 @@ class SwapManager(Logger): def __init__(self, *, wallet: 'Abstract_Wallet', lnworker: 'LNWallet'): Logger.__init__(self) self.mining_fee = None - self.percentage = None + self.percentage = None # type: Optional[Decimal] self._min_amount = None self._max_forward = None self._max_reverse = None @@ -1193,7 +1193,7 @@ class SwapManager(Logger): def server_update_pairs(self) -> None: """ for server """ - self.percentage = float(self.config.SWAPSERVER_FEE_MILLIONTHS) / 10000 # type: ignore + self.percentage = Decimal(self.config.SWAPSERVER_FEE_MILLIONTHS) / 10000 # type: ignore self._min_amount = MIN_SWAP_AMOUNT_SAT oc_balance_sat: int = self.wallet.get_spendable_balance_sat() max_forward: int = min(int(self.lnworker.num_sats_can_receive()), oc_balance_sat, 10000000) @@ -1259,7 +1259,7 @@ class SwapManager(Logger): if send_amount is None: return None x = Decimal(send_amount) - percentage = Decimal(self.percentage) + percentage = self.percentage if is_reverse: if not self.check_invoice_amount(x, is_reverse): return None @@ -1289,7 +1289,7 @@ class SwapManager(Logger): if not recv_amount: return None x = Decimal(recv_amount) - percentage = Decimal(self.percentage) + percentage = self.percentage if is_reverse: # see/ref: # https://github.com/BoltzExchange/boltz-backend/blob/e7e2d30f42a5bea3665b164feb85f84c64d86658/lib/service/Service.ts#L928 @@ -1638,7 +1638,7 @@ class HttpTransport(SwapServerTransport): fees = response['pairs']['BTC/BTC']['fees'] limits = response['pairs']['BTC/BTC']['limits'] pairs = SwapFees( - percentage=fees['percentage'], + percentage=Decimal(str(fees['percentage'])), mining_fee=fees['minerFees']['baseAsset']['mining_fee'], min_amount=limits['minimal'], max_forward=limits['max_forward_amount'], @@ -1777,7 +1777,7 @@ class NostrTransport(SwapServerTransport): self.logger.warning(f"not publishing swap offer, no liquidity available: {sm._max_forward=}, {sm._max_reverse=}") return offer = { - 'percentage_fee': sm.percentage, + 'percentage_fee': float(sm.percentage), # cast to float for <= 4.7.1 backwards compatibility 'mining_fee': sm.mining_fee, 'min_amount': sm._min_amount, 'max_forward_amount': sm._max_forward, @@ -1847,7 +1847,6 @@ class NostrTransport(SwapServerTransport): "#d": [f"electrum-swapserver-{self.NOSTR_EVENT_VERSION}"], "#r": [f"net:{constants.net.NET_NAME}"], "since": int(time.time()) - 60 * 60, - "until": int(time.time()) + 60 * 60, } async for event in self.relay_manager.get_events(query, single_event=False, only_stored=False): try: @@ -1884,7 +1883,7 @@ class NostrTransport(SwapServerTransport): continue try: pairs = SwapFees( - percentage=content['percentage_fee'], + percentage=Decimal(str(content['percentage_fee'])), mining_fee=content['mining_fee'], min_amount=content['min_amount'], max_forward=content['max_forward_amount'], diff --git a/tests/test_commands.py b/tests/test_commands.py index dddf293fd..69b833b7a 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -678,7 +678,7 @@ class TestCommandsTestnet(ElectrumTestCase): offer1 = SwapOffer( pairs=SwapFees( - percentage=0.5, + percentage=Decimal('0.5'), mining_fee=2000, min_amount=10000, max_forward=1000000, @@ -692,7 +692,7 @@ class TestCommandsTestnet(ElectrumTestCase): offer2 = SwapOffer( pairs=SwapFees( - percentage=1.0, + percentage=Decimal('1.0'), mining_fee=3000, min_amount=20000, max_forward=2000000,