swaps: make SwapManager.percentage Decimal

If SwapManager.percentage was a 0.2 float, rounding differences would
cause an exception in the fee calculation inverse sanity check when entering 20
000 sats into the SwapDialog. By making self.percentage a decimal we can
prevent this kind of issue.

```
File "/home/user/code/vibecoding_vm/electrum/electrum/gui/qt/swap_dialog.py", line 294, in on_send_edited
    recv_amount = self.swap_manager.get_recv_amount(send_amount, is_reverse=self.is_reverse)
  File "/home/user/code/vibecoding_vm/electrum/electrum/submarine_swaps.py", line 1320, in get_recv_amount
    if abs(send_amount - inverted_send_amount) > 1:
           ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
TypeError: unsupported operand type(s) for -: 'int' and 'NoneType'
```
This commit is contained in:
f321x
2026-03-16 13:42:53 +01:00
parent 022a1bf0e7
commit f56e6318e3
5 changed files with 13 additions and 13 deletions
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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,
+8 -8
View File
@@ -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,
@@ -1883,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'],
+2 -2
View File
@@ -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,