Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 45 additions & 20 deletions electrum/gui/qt/amountedit.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
from .util import char_width_in_lineedit, ColorScheme

from electrum.util import (format_satoshis_plain, decimal_point_to_base_unit_name,
FEERATE_PRECISION, quantize_feerate, DECIMAL_POINT, UI_UNIT_NAME_FEERATE_SAT_PER_VBYTE)
FEERATE_PRECISION, quantize_feerate, DECIMAL_POINT, UI_UNIT_NAME_FEERATE_SAT_PER_VBYTE,
to_decimal)
from electrum.bitcoin import COIN, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC

_NOT_GIVEN = object() # sentinel value
Expand Down Expand Up @@ -44,22 +45,22 @@ def sizeHint(self) -> QSize:
class AmountEdit(SizedFreezableLineEdit):
shortcut = pyqtSignal()

def __init__(self, base_unit, is_int=False, parent=None, *, max_amount=None):
def __init__(self, parent=None, *, base_unit=None, is_int=False, max_amount=None, extra_precision=None):
# This seems sufficient for hundred-BTC amounts with 8 decimals
width = 16 * char_width_in_lineedit()
super().__init__(width=width, parent=parent)
self.base_unit = base_unit
self.textChanged.connect(self.numbify)
self.is_int = is_int
self.is_shortcut = False
self.extra_precision = 0
self.extra_precision = extra_precision if extra_precision else lambda: 0
self.max_amount = max_amount

def decimal_point(self):
return 8
return 0

def max_precision(self):
return self.decimal_point() + self.extra_precision
return self.decimal_point() + self.extra_precision()

def numbify(self):
text = self.text().strip()
Expand Down Expand Up @@ -106,22 +107,32 @@ def get_amount(self) -> Union[None, Decimal, int]:
amt = self._get_amount_from_text(str(self.text()))
if self.max_amount and amt and amt >= self.max_amount:
return self.max_amount
return amt
if amt is None:
return amt
# return as int if no millisats (Decimal otherwise)
return int(amt) if (int(amt) * 1000 == int(amt * 1000)) else amt

def _get_text_from_amount(self, amount) -> str:
return "%d" % amount
x = to_decimal(amount)
scale_factor = pow(10, self.decimal_point())
nfmt = "{:." + str(self.decimal_point() + self.extra_precision()) + "f}"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When calling .setAmount() on a FeerateEdit with a 0 extra_precision currency like KRW this swallows trailing 0, though nothing seems to be affected by this:

>>> from electrum.gui.qt import amountedit
>>> fiat = amountedit.FiatAmountEdit(fx=daemon.fx)  # fx is KRW
>>> fiat.setAmount(100000)
>>> fiat.get_amount()
1

text = nfmt.format(x / scale_factor).rstrip('0').rstrip('.')
text = text.replace('.', DECIMAL_POINT)
return text

def setAmount(self, amount):
text = self._get_text_from_amount(amount)
text = self._get_text_from_amount(amount) if amount is not None else " "
self.setText(text)
self.repaint() # macOS hack for #6269


class BTCAmountEdit(AmountEdit):

def __init__(self, decimal_point, is_int=False, parent=None, *, max_amount=_NOT_GIVEN):
def __init__(self, decimal_point, parent=None, *, is_int=False, max_amount=_NOT_GIVEN, millisat_precision=False):
if max_amount is _NOT_GIVEN:
max_amount = TOTAL_COIN_SUPPLY_LIMIT_IN_BTC * COIN
AmountEdit.__init__(self, self._base_unit, is_int, parent, max_amount=max_amount)
extra_precision = lambda: 3 if millisat_precision else 0
AmountEdit.__init__(self, parent, base_unit=self._base_unit, is_int=is_int, max_amount=max_amount, extra_precision=extra_precision)
self.decimal_point = decimal_point

def _base_unit(self):
Expand All @@ -145,7 +156,8 @@ def _get_amount_from_text(self, text):
return Decimal(amount) if not self.is_int else int(amount)

def _get_text_from_amount(self, amount_sat):
text = format_satoshis_plain(amount_sat, decimal_point=self.decimal_point())
text = format_satoshis_plain(amount_sat, decimal_point=self.decimal_point(),
precision=self.extra_precision())
text = text.replace('.', DECIMAL_POINT)
return text

Expand All @@ -159,19 +171,32 @@ def setAmount(self, amount_sat):
self.repaint() # macOS hack for #6269


class FeerateEdit(BTCAmountEdit):
Comment thread
accumulator marked this conversation as resolved.

def __init__(self, decimal_point, is_int=False, parent=None, *, max_amount=_NOT_GIVEN):
super().__init__(decimal_point, is_int, parent, max_amount=max_amount)
self.extra_precision = FEERATE_PRECISION
class FeerateEdit(AmountEdit):
def __init__(self, parent=None, *, max_amount=None):
super().__init__(parent, base_unit=lambda: self._base_unit(), max_amount=max_amount)
self.extra_precision = lambda: FEERATE_PRECISION
self.decimal_point = lambda: 0

def _base_unit(self):
return UI_UNIT_NAME_FEERATE_SAT_PER_VBYTE

def _get_amount_from_text(self, text):
sat_per_byte_amount = super()._get_amount_from_text(text)
return quantize_feerate(sat_per_byte_amount)
amount = super()._get_amount_from_text(text)
if amount and amount > TOTAL_COIN_SUPPLY_LIMIT_IN_BTC * COIN:
return TOTAL_COIN_SUPPLY_LIMIT_IN_BTC * COIN
return quantize_feerate(amount)

def _get_text_from_amount(self, amount):
amount = quantize_feerate(amount)
return super()._get_text_from_amount(amount)
return super()._get_text_from_amount(quantize_feerate(amount))


class FiatAmountEdit(AmountEdit):
def __init__(self, fx=None, parent=None, *, max_amount=None):
super().__init__(parent, base_unit=self._baseunit, max_amount=max_amount, extra_precision=self._extraprecision)
self.fx = fx

def _baseunit(self):
return self.fx.get_currency() if self.fx else ''

def _extraprecision(self):
return self.fx.ccy_precision() if self.fx else 0
2 changes: 1 addition & 1 deletion electrum/gui/qt/confirm_tx_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ def create_fee_controls(self):
self.fiat_fee_label.setAmount(0)
self.fiat_fee_label.setStyleSheet(ColorScheme.DEFAULT.as_stylesheet())

self.feerate_e = FeerateEdit(lambda: 0)
self.feerate_e = FeerateEdit()
self.feerate_e.textEdited.connect(partial(self.on_fee_or_feerate, self.feerate_e, False))
self.feerate_e.editingFinished.connect(partial(self.on_fee_or_feerate, self.feerate_e, True))
self.update_feerate_label()
Expand Down
4 changes: 2 additions & 2 deletions electrum/gui/qt/receive_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from electrum.invoices import pr_expiration_values
from electrum.logging import Logger

from .amountedit import AmountEdit, BTCAmountEdit, SizedFreezableLineEdit
from .amountedit import BTCAmountEdit, SizedFreezableLineEdit, FiatAmountEdit
from .qrcodewidget import QRCodeWidget
from .util import read_QIcon, WWLabel, MessageBoxMixin, MONOSPACE_FONT, get_icon_qrcode

Expand Down Expand Up @@ -56,7 +56,7 @@ def __init__(self, window: 'ElectrumWindow'):
grid.addWidget(QLabel(_('Requested amount')), 1, 0)
grid.addWidget(self.receive_amount_e, 1, 1)

self.fiat_receive_e = AmountEdit(self.fx.get_currency if self.fx else '')
self.fiat_receive_e = FiatAmountEdit(self.fx)
if not self.fx or not self.fx.is_enabled():
self.fiat_receive_e.setVisible(False)
grid.addWidget(self.fiat_receive_e, 1, 2, Qt.AlignmentFlag.AlignLeft)
Expand Down
4 changes: 2 additions & 2 deletions electrum/gui/qt/send_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from electrum.fee_policy import FeePolicy, FixedFeePolicy
from electrum.lnurl import LNURL3Data, request_lnurl_withdraw_callback, LNURLError

from .amountedit import AmountEdit, BTCAmountEdit, SizedFreezableLineEdit
from .amountedit import BTCAmountEdit, SizedFreezableLineEdit, FiatAmountEdit
from .paytoedit import InvalidPaymentIdentifier
from .util import (WaitingDialog, HelpLabel, MessageBoxMixin, EnterButton, char_width_in_lineedit,
get_icon_camera, read_QIcon, ColorScheme, IconLabel, Spinner, Buttons, WWLabel,
Expand Down Expand Up @@ -119,7 +119,7 @@ def __init__(self, window: 'ElectrumWindow'):
amount_widgets = QHBoxLayout()
amount_widgets.addWidget(self.amount_e)

self.fiat_send_e = AmountEdit(self.fx.get_currency if self.fx else '')
self.fiat_send_e = FiatAmountEdit(self.fx)
if not self.fx or not self.fx.is_enabled():
self.fiat_send_e.setVisible(False)
amount_widgets.addWidget(self.fiat_send_e)
Expand Down
3 changes: 2 additions & 1 deletion electrum/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,7 @@ def format_satoshis_plain(
x: Union[int, float, Decimal, str], # amount in satoshis,
*,
decimal_point: int = 8, # how much to shift decimal point to left (default: sat->BTC)
precision: int = 0, # extra digits after satoshi precision (e.g. for msat)
is_max_allowed: bool = True,
) -> str:
"""Display a satoshi amount scaled. Always uses a '.' as a decimal
Expand All @@ -789,7 +790,7 @@ def format_satoshis_plain(
# TODO(ghost43) just hard-fail if x is a float. do we even use floats for money anywhere?
x = to_decimal(x)
scale_factor = pow(10, decimal_point)
return "{:.8f}".format(x / scale_factor).rstrip('0').rstrip('.')
return "{:.{}f}".format(x / scale_factor, 8 + precision).rstrip('0').rstrip('.')


# Check that Decimal precision is sufficient.
Expand Down