follow-up prev: some clean-ups
re https://github.com/spesmilo/electrum/pull/7492
This commit is contained in:
@@ -18,7 +18,7 @@ from electrum.plugin import run_hook
|
||||
from electrum import util
|
||||
from electrum.util import (profiler, InvalidPassword, send_exception_to_crash_reporter,
|
||||
format_satoshis, format_satoshis_plain, format_fee_satoshis,
|
||||
maybe_extract_bolt11_invoice)
|
||||
maybe_extract_bolt11_invoice, parse_max_spend)
|
||||
from electrum.invoices import PR_PAID, PR_FAILED
|
||||
from electrum import blockchain
|
||||
from electrum.network import Network, TxBroadcastError, BestEffortRequestFailed
|
||||
@@ -988,8 +988,8 @@ class ElectrumWindow(App, Logger):
|
||||
def format_amount_and_units(self, x) -> str:
|
||||
if x is None:
|
||||
return 'none'
|
||||
if x == '!':
|
||||
return 'max'
|
||||
if parse_max_spend(x):
|
||||
return f'max({x})'
|
||||
# FIXME this is using format_satoshis_plain instead of config.format_amount
|
||||
# as we sometimes convert the returned string back to numbers,
|
||||
# via self.get_amount()... the need for converting back should be removed
|
||||
|
||||
@@ -62,7 +62,7 @@ from electrum.util import (format_time,
|
||||
UserFacingException,
|
||||
get_new_wallet_name, send_exception_to_crash_reporter,
|
||||
InvalidBitcoinURI, maybe_extract_bolt11_invoice, NotEnoughFunds,
|
||||
NoDynamicFeeEstimates, MultipleSpendMaxTxOutputs,
|
||||
NoDynamicFeeEstimates,
|
||||
AddTransactionException, BITCOIN_BIP21_URI_SCHEME,
|
||||
InvoiceError, parse_max_spend)
|
||||
from electrum.invoices import PR_TYPE_ONCHAIN, PR_TYPE_LN, PR_DEFAULT_EXPIRATION_WHEN_CREATING, Invoice
|
||||
@@ -1351,8 +1351,16 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
self.amount_e = BTCAmountEdit(self.get_decimal_point)
|
||||
self.payto_e = PayToEdit(self)
|
||||
self.payto_e.addPasteButton(self.app)
|
||||
msg = _('Recipient of the funds.') + '\n\n'\
|
||||
+ _('You may enter a Bitcoin address, a label from your list of contacts (a list of completions will be proposed), or an alias (email-like address that forwards to a Bitcoin address)')
|
||||
msg = (_("Recipient of the funds.") + "\n\n"
|
||||
+ _("You may enter a Bitcoin address, a label from your list of contacts "
|
||||
"(a list of completions will be proposed), "
|
||||
"or an alias (email-like address that forwards to a Bitcoin address)") + ". "
|
||||
+ _("Lightning invoices are also supported.") + "\n\n"
|
||||
+ _("You can also pay to many outputs in a single transaction, "
|
||||
"specifying one output per line.") + "\n" + _("Format: address, amount") + "\n"
|
||||
+ _("To set the amount to 'max', use the '!' special character.") + "\n"
|
||||
+ _("Integers weights can also be used in conjunction with '!', "
|
||||
"e.g. set one amount to '2!' and another to '3!' to split your coins 40-60."))
|
||||
payto_label = HelpLabel(_('Pay to'), msg)
|
||||
grid.addWidget(payto_label, 1, 0)
|
||||
grid.addWidget(self.payto_e, 1, 1, 1, -1)
|
||||
@@ -1451,10 +1459,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
# Check if we had enough funds excluding fees,
|
||||
# if so, still provide opportunity to set lower fees.
|
||||
tx = make_tx(0)
|
||||
except MultipleSpendMaxTxOutputs as e:
|
||||
self.max_button.setChecked(False)
|
||||
self.show_error(str(e))
|
||||
return
|
||||
except NotEnoughFunds as e:
|
||||
self.max_button.setChecked(False)
|
||||
text = self.get_text_not_enough_funds_mentioning_frozen()
|
||||
|
||||
@@ -136,7 +136,7 @@ class OnchainInvoice(Invoice):
|
||||
if not (0 <= value <= TOTAL_COIN_SUPPLY_LIMIT_IN_BTC * COIN):
|
||||
raise InvoiceError(f"amount is out-of-bounds: {value!r} sat")
|
||||
elif isinstance(value, str):
|
||||
if value != "!":
|
||||
if value != '!':
|
||||
raise InvoiceError(f"unexpected amount: {value!r}")
|
||||
else:
|
||||
raise InvoiceError(f"unexpected amount: {value!r}")
|
||||
|
||||
@@ -42,7 +42,7 @@ import copy
|
||||
|
||||
from . import ecc, bitcoin, constants, segwit_addr, bip32
|
||||
from .bip32 import BIP32Node
|
||||
from .util import profiler, to_bytes, bh2u, bfh, chunks, is_hex_str
|
||||
from .util import profiler, to_bytes, bh2u, bfh, chunks, is_hex_str, parse_max_spend
|
||||
from .bitcoin import (TYPE_ADDRESS, TYPE_SCRIPT, hash_160,
|
||||
hash160_to_p2sh, hash160_to_p2pkh, hash_to_segwit_addr,
|
||||
var_int, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC, COIN,
|
||||
@@ -109,7 +109,9 @@ class TxOutput:
|
||||
|
||||
def __init__(self, *, scriptpubkey: bytes, value: Union[int, str]):
|
||||
self.scriptpubkey = scriptpubkey
|
||||
self.value = value # str when the output is set to max: '!' # in satoshis
|
||||
if not (isinstance(value, int) or parse_max_spend(value) is not None):
|
||||
raise ValueError(f"bad txout value: {value!r}")
|
||||
self.value = value # int in satoshis; or spend-max-like str
|
||||
|
||||
@classmethod
|
||||
def from_address_and_value(cls, address: str, value: Union[int, str]) -> Union['TxOutput', 'PartialTxOutput']:
|
||||
|
||||
@@ -111,6 +111,7 @@ def base_unit_name_to_decimal_point(unit_name: str) -> int:
|
||||
def parse_max_spend(amt: Any) -> Optional[int]:
|
||||
"""Checks if given amount is "spend-max"-like.
|
||||
Returns None or the positive integer weight for "max". Never raises.
|
||||
|
||||
When creating invoices and on-chain txs, the user can specify to send "max".
|
||||
This is done by setting the amount to '!'. Splitting max between multiple
|
||||
tx outputs is also possible, and custom weights (positive ints) can also be used.
|
||||
@@ -143,11 +144,6 @@ class NoDynamicFeeEstimates(Exception):
|
||||
return _('Dynamic fee estimates not available')
|
||||
|
||||
|
||||
class MultipleSpendMaxTxOutputs(Exception):
|
||||
def __str__(self):
|
||||
return _('At most one output can be set to spend max')
|
||||
|
||||
|
||||
class InvalidPassword(Exception):
|
||||
def __str__(self):
|
||||
return _("Incorrect password")
|
||||
@@ -658,8 +654,8 @@ def format_satoshis_plain(
|
||||
) -> str:
|
||||
"""Display a satoshi amount scaled. Always uses a '.' as a decimal
|
||||
point and has no thousands separator"""
|
||||
if x == '!':
|
||||
return 'max'
|
||||
if parse_max_spend(x):
|
||||
return f'max({x})'
|
||||
assert isinstance(x, (int, float, Decimal)), f"{x!r} should be a number"
|
||||
scale_factor = pow(10, decimal_point)
|
||||
return "{:.8f}".format(Decimal(x) / scale_factor).rstrip('0').rstrip('.')
|
||||
@@ -688,7 +684,7 @@ def format_satoshis(
|
||||
if x is None:
|
||||
return 'unknown'
|
||||
if parse_max_spend(x):
|
||||
return f'max ({x}) '
|
||||
return f'max({x})'
|
||||
assert isinstance(x, (int, float, Decimal)), f"{x!r} should be a number"
|
||||
# lose redundant precision
|
||||
x = Decimal(x).quantize(Decimal(10) ** (-precision))
|
||||
|
||||
@@ -54,7 +54,7 @@ from .crypto import sha256
|
||||
from . import util
|
||||
from .util import (NotEnoughFunds, UserCancelled, profiler,
|
||||
format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates,
|
||||
WalletFileException, BitcoinException, MultipleSpendMaxTxOutputs,
|
||||
WalletFileException, BitcoinException,
|
||||
InvalidPassword, format_time, timestamp_to_datetime, Satoshis,
|
||||
Fiat, bfh, bh2u, TxMinedInfo, quantize_feerate, create_bip21_uri, OrderedDictWithIndex, parse_max_spend)
|
||||
from .simple_config import SimpleConfig, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE
|
||||
@@ -1342,7 +1342,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
|
||||
weight = parse_max_spend(o.value)
|
||||
if weight:
|
||||
i_max_sum += weight
|
||||
i_max.append((weight,i))
|
||||
i_max.append((weight, i))
|
||||
|
||||
if fee is None and self.config.fee_per_kb() is None:
|
||||
raise NoDynamicFeeEstimates()
|
||||
@@ -1412,8 +1412,8 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
|
||||
if amount < 0:
|
||||
raise NotEnoughFunds()
|
||||
distr_amount = 0
|
||||
for (x,i) in i_max:
|
||||
val = int((amount/i_max_sum)*x)
|
||||
for (weight, i) in i_max:
|
||||
val = int((amount/i_max_sum) * weight)
|
||||
outputs[i].value = val
|
||||
distr_amount += val
|
||||
|
||||
|
||||
Reference in New Issue
Block a user