feat: network-aware coin name and unit strings
Add COIN_SYMBOL/COIN_NAME to AbstractNet (defaults: BTC/Bitcoin). BitcoinPurple overrides to BTCP/Bitcoin Purple; testnet inherits. Replace module-level base_units/base_units_list in util.py with get_base_units()/get_base_units_list() that read constants.net.COIN_SYMBOL at runtime, producing [BTCP, mBTCP, bits, sat] on BitcoinPurple. Update all UI touch points: Qt window title, watching-only warning, invalid address message, testnet warning, settings unit combo + help text, QML networkName property, and Preferences thousands-separator label
This commit is contained in:
@@ -81,6 +81,9 @@ class AbstractNet:
|
||||
XPUB_HEADERS: Mapping[str, int]
|
||||
XPUB_HEADERS_INV: Mapping[int, str]
|
||||
|
||||
COIN_SYMBOL: str = "BTC"
|
||||
COIN_NAME: str = "Bitcoin"
|
||||
|
||||
# PoW difficulty parameters (Bitcoin defaults; override per chain as needed)
|
||||
DIFFICULTY_ADJUSTMENT_INTERVAL: int = 2016 # blocks per retarget window
|
||||
POW_TARGET_TIMESPAN: int = 14 * 24 * 60 * 60 # target seconds per window
|
||||
@@ -273,6 +276,8 @@ class BitcoinPurple(AbstractNet):
|
||||
|
||||
NET_NAME = "bitcoinpurple"
|
||||
TESTNET = False
|
||||
COIN_SYMBOL = "BTCP"
|
||||
COIN_NAME = "Bitcoin Purple"
|
||||
WIF_PREFIX = 0xb7
|
||||
ADDRTYPE_P2PKH = 56
|
||||
ADDRTYPE_P2SH = 55
|
||||
|
||||
@@ -89,7 +89,7 @@ Pane {
|
||||
}
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr('Add thousands separators to bitcoin amounts')
|
||||
text: qsTr('Add thousands separators to %1 amounts').arg(Network.networkName)
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ class QENetwork(QObject, QtEventListener):
|
||||
|
||||
@pyqtProperty(str, notify=dataChanged)
|
||||
def networkName(self):
|
||||
return constants.net.__name__.replace('Bitcoin', '')
|
||||
return constants.net.COIN_NAME
|
||||
|
||||
@pyqtProperty('QVariantMap', notify=proxyChanged)
|
||||
def proxy(self):
|
||||
|
||||
@@ -629,8 +629,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
@classmethod
|
||||
def get_app_name_and_version_str(cls) -> str:
|
||||
name = "Electrum"
|
||||
if constants.net.TESTNET:
|
||||
name += " " + constants.net.NET_NAME.capitalize()
|
||||
if constants.net.NET_NAME != "mainnet":
|
||||
name += " " + constants.net.COIN_NAME
|
||||
return f"{name} {ELECTRUM_VERSION}"
|
||||
|
||||
def watching_only_changed(self):
|
||||
@@ -648,10 +648,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
|
||||
def warn_if_watching_only(self):
|
||||
if self.wallet.is_watching_only():
|
||||
coin = constants.net.COIN_NAME
|
||||
msg = ' '.join([
|
||||
_("This wallet is watching-only."),
|
||||
_("This means you will not be able to spend Bitcoins with it."),
|
||||
_("Make sure you own the seed phrase or the private keys, before you request Bitcoins to be sent to this wallet.")
|
||||
_("This means you will not be able to spend {coin} with it.").format(coin=coin),
|
||||
_("Make sure you own the seed phrase or the private keys, before you request {coin} to be sent to this wallet.").format(coin=coin),
|
||||
])
|
||||
self.show_warning(msg, title=_('Watch-only wallet'))
|
||||
|
||||
@@ -668,7 +669,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
msg = ''.join([
|
||||
_("You are in testnet mode."), ' ',
|
||||
_("Testnet coins are worthless."), '\n',
|
||||
_("Testnet is separate from the main Bitcoin network. It is used for testing.")
|
||||
_("Testnet is separate from the main {coin} network. It is used for testing.").format(coin=constants.net.COIN_NAME)
|
||||
])
|
||||
cb = QCheckBox(_("Don't show this again."))
|
||||
cb_checked = False
|
||||
@@ -2133,7 +2134,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
address = address.text().strip()
|
||||
message = message.toPlainText().strip()
|
||||
if not bitcoin.is_address(address):
|
||||
self.show_message(_('Invalid Bitcoin address.'))
|
||||
self.show_message(_(f'Invalid {constants.net.COIN_NAME} address.'))
|
||||
return
|
||||
if self.wallet.is_watching_only():
|
||||
self.show_message(_('This is a watching-only wallet.'))
|
||||
@@ -2161,7 +2162,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
address = address.text().strip()
|
||||
message = message.toPlainText().strip().encode('utf-8')
|
||||
if not bitcoin.is_address(address):
|
||||
self.show_message(_('Invalid Bitcoin address.'))
|
||||
self.show_message(_(f'Invalid {constants.net.COIN_NAME} address.'))
|
||||
return
|
||||
try:
|
||||
# This can throw on invalid base64
|
||||
|
||||
@@ -33,7 +33,8 @@ from PyQt6.QtWidgets import (QComboBox, QTabWidget, QDialog, QSpinBox, QCheckB
|
||||
|
||||
from electrum.i18n import _, get_gui_lang_names
|
||||
from electrum import util
|
||||
from electrum.util import base_units_list, event_listener
|
||||
from electrum.util import get_base_units_list, event_listener
|
||||
from electrum import constants
|
||||
|
||||
from electrum.gui.common_qt.util import QtEventListener
|
||||
from electrum.gui import messages
|
||||
@@ -159,9 +160,10 @@ class SettingsDialog(QDialog, QtEventListener):
|
||||
msat_cb.stateChanged.connect(on_msat_checked)
|
||||
|
||||
# units
|
||||
units = base_units_list
|
||||
units = get_base_units_list()
|
||||
sym = constants.net.COIN_SYMBOL
|
||||
msg = (_('Base unit of your wallet.')
|
||||
+ '\n1 BTC = 1000 mBTC. 1 mBTC = 1000 bits. 1 bit = 100 sat.\n'
|
||||
+ f'\n1 {sym} = 1000 m{sym}. 1 m{sym} = 1000 bits. 1 bit = 100 sat.\n'
|
||||
+ _('This setting affects the Send tab, and all balance related fields.'))
|
||||
unit_label = HelpLabel(_('Base unit') + ':', msg)
|
||||
unit_combo = QComboBox()
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Optional
|
||||
|
||||
from electrum.gui import BaseElectrumGui
|
||||
from electrum import util
|
||||
from electrum import constants
|
||||
from electrum import WalletStorage, Wallet
|
||||
from electrum.wallet import Abstract_Wallet
|
||||
from electrum.wallet_db import WalletDB
|
||||
@@ -185,7 +186,7 @@ class ElectrumGui(BaseElectrumGui, EventListener):
|
||||
|
||||
def do_send(self):
|
||||
if not is_address(self.str_recipient):
|
||||
print(_('Invalid Bitcoin address'))
|
||||
print(_(f'Invalid {constants.net.COIN_NAME} address'))
|
||||
return
|
||||
try:
|
||||
amount = int(Decimal(self.str_amount) * COIN)
|
||||
|
||||
@@ -14,6 +14,7 @@ except ImportError: # only use vendored lib as fallback, to allow Linux distros
|
||||
from electrum._vendor import pyperclip
|
||||
|
||||
from electrum.gui import BaseElectrumGui
|
||||
from electrum import constants
|
||||
from electrum.bip21 import parse_bip21_URI
|
||||
from electrum.util import format_time
|
||||
from electrum.util import EventListener, event_listener
|
||||
@@ -641,7 +642,7 @@ class ElectrumGui(BaseElectrumGui, EventListener):
|
||||
URI=None,
|
||||
)
|
||||
else:
|
||||
self.show_message(_('Invalid Bitcoin address'))
|
||||
self.show_message(_(f'Invalid {constants.net.COIN_NAME} address'))
|
||||
return None
|
||||
return invoice
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from copy import deepcopy
|
||||
from . import constants
|
||||
from . import util
|
||||
from . import invoices
|
||||
from .util import base_units, base_unit_name_to_decimal_point, decimal_point_to_base_unit_name, UnknownBaseUnit, DECIMAL_POINT_DEFAULT
|
||||
from .util import get_base_units, base_unit_name_to_decimal_point, decimal_point_to_base_unit_name, UnknownBaseUnit, DECIMAL_POINT_DEFAULT
|
||||
from .util import format_satoshis, format_fee_satoshis, os_chmod
|
||||
from .util import user_dir, make_dir
|
||||
from .util import is_valid_websocket_url
|
||||
@@ -547,7 +547,7 @@ class SimpleConfig(Logger):
|
||||
return decimal_point_to_base_unit_name(self.BTC_AMOUNTS_DECIMAL_POINT)
|
||||
|
||||
def set_base_unit(self, unit):
|
||||
assert unit in base_units.keys()
|
||||
assert unit in get_base_units()
|
||||
self.BTC_AMOUNTS_DECIMAL_POINT = base_unit_name_to_decimal_point(unit)
|
||||
|
||||
def get_nostr_relays(self) -> Sequence[str]:
|
||||
|
||||
+21
-8
@@ -92,29 +92,42 @@ def all_subclasses(cls) -> Set:
|
||||
ca_path = certifi.where()
|
||||
|
||||
|
||||
base_units = {'BTC':8, 'mBTC':5, 'bits':2, 'sat':0}
|
||||
base_units_inverse = inv_dict(base_units)
|
||||
base_units_list = ['BTC', 'mBTC', 'bits', 'sat'] # list(dict) does not guarantee order
|
||||
|
||||
DECIMAL_POINT_DEFAULT = 5 # mBTC
|
||||
|
||||
|
||||
class UnknownBaseUnit(Exception): pass
|
||||
|
||||
|
||||
# Canonical decimal-point map; keys use 'BTC' as placeholder for any coin symbol.
|
||||
_BASE_UNITS_DP = {'BTC': 8, 'mBTC': 5, 'bits': 2, 'sat': 0}
|
||||
_BASE_UNITS_ORDER = ['BTC', 'mBTC', 'bits', 'sat']
|
||||
|
||||
|
||||
def _coin_key(k: str) -> str:
|
||||
from electrum import constants # avoid circular import at module level
|
||||
return k.replace('BTC', constants.net.COIN_SYMBOL)
|
||||
|
||||
|
||||
def get_base_units() -> dict:
|
||||
return {_coin_key(k): v for k, v in _BASE_UNITS_DP.items()}
|
||||
|
||||
|
||||
def get_base_units_list() -> list:
|
||||
return [_coin_key(k) for k in _BASE_UNITS_ORDER]
|
||||
|
||||
|
||||
def decimal_point_to_base_unit_name(dp: int) -> str:
|
||||
# e.g. 8 -> "BTC"
|
||||
inv = {v: k for k, v in get_base_units().items()}
|
||||
try:
|
||||
return base_units_inverse[dp]
|
||||
return inv[dp]
|
||||
except KeyError:
|
||||
raise UnknownBaseUnit(dp) from None
|
||||
|
||||
|
||||
def base_unit_name_to_decimal_point(unit_name: str) -> int:
|
||||
"""Returns the max number of digits allowed after the decimal point."""
|
||||
# e.g. "BTC" -> 8
|
||||
try:
|
||||
return base_units[unit_name]
|
||||
return get_base_units()[unit_name]
|
||||
except KeyError:
|
||||
raise UnknownBaseUnit(unit_name) from None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user