1
0

commands: use format_satoshis consistently. don't use sci-notation

old behaviour:
```
>>> from electrum.commands import format_satoshis
>>> format_satoshis(1)
'1E-8'
```
This commit is contained in:
SomberNight
2025-08-21 17:38:27 +00:00
parent 07d69bd876
commit 74cd2b4ac3
3 changed files with 34 additions and 12 deletions

View File

@@ -36,7 +36,7 @@ import inspect
from collections import defaultdict from collections import defaultdict
from functools import wraps from functools import wraps
from decimal import Decimal, InvalidOperation from decimal import Decimal, InvalidOperation
from typing import Optional, TYPE_CHECKING, Dict, List, Any from typing import Optional, TYPE_CHECKING, Dict, List, Any, Union
import os import os
import re import re
@@ -101,8 +101,14 @@ def satoshis(amount):
return int(COIN*to_decimal(amount)) if amount is not None else None return int(COIN*to_decimal(amount)) if amount is not None else None
def format_satoshis(x): def format_satoshis(x: Union[str, float, int, Decimal, None]) -> Optional[str]:
return str(to_decimal(x)/COIN) if x is not None else None """
input: satoshis as a Number
output: str formatted as bitcoin amount
"""
if x is None:
return None
return util.format_satoshis_plain(x, is_max_allowed=False)
class Command: class Command:
@@ -476,7 +482,7 @@ class Commands(Logger):
for txin in wallet.get_utxos(): for txin in wallet.get_utxos():
d = txin.to_json() d = txin.to_json()
v = d.pop("value_sats") v = d.pop("value_sats")
d["value"] = str(to_decimal(v)/COIN) if v is not None else None d["value"] = format_satoshis(v)
coins.append(d) coins.append(d)
return coins return coins
@@ -719,13 +725,13 @@ class Commands(Logger):
"""Return the balance of your wallet. """ """Return the balance of your wallet. """
c, u, x = wallet.get_balance() c, u, x = wallet.get_balance()
l = wallet.lnworker.get_balance() if wallet.lnworker else None l = wallet.lnworker.get_balance() if wallet.lnworker else None
out = {"confirmed": str(to_decimal(c)/COIN)} out = {"confirmed": format_satoshis(c)}
if u: if u:
out["unconfirmed"] = str(to_decimal(u)/COIN) out["unconfirmed"] = format_satoshis(u)
if x: if x:
out["unmatured"] = str(to_decimal(x)/COIN) out["unmatured"] = format_satoshis(x)
if l: if l:
out["lightning"] = str(to_decimal(l)/COIN) out["lightning"] = format_satoshis(l)
return out return out
@command('n') @command('n')
@@ -738,8 +744,8 @@ class Commands(Logger):
""" """
sh = bitcoin.address_to_scripthash(address) sh = bitcoin.address_to_scripthash(address)
out = await self.network.get_balance_for_scripthash(sh) out = await self.network.get_balance_for_scripthash(sh)
out["confirmed"] = str(to_decimal(out["confirmed"])/COIN) out["confirmed"] = format_satoshis(out["confirmed"])
out["unconfirmed"] = str(to_decimal(out["unconfirmed"])/COIN) out["unconfirmed"] = format_satoshis(out["unconfirmed"])
return out return out
@command('n') @command('n')
@@ -1183,7 +1189,7 @@ class Commands(Logger):
if labels or balance: if labels or balance:
item = (item,) item = (item,)
if balance: if balance:
item += (util.format_satoshis(sum(wallet.get_addr_balance(addr))),) item += (format_satoshis(sum(wallet.get_addr_balance(addr))),)
if labels: if labels:
item += (repr(wallet.get_label_for_address(addr)),) item += (repr(wallet.get_label_for_address(addr)),)
out.append(item) out.append(item)

View File

@@ -793,10 +793,11 @@ def format_satoshis_plain(
x: Union[int, float, Decimal, str], # amount in satoshis, x: Union[int, float, Decimal, str], # amount in satoshis,
*, *,
decimal_point: int = 8, # how much to shift decimal point to left (default: sat->BTC) decimal_point: int = 8, # how much to shift decimal point to left (default: sat->BTC)
is_max_allowed: bool = True,
) -> str: ) -> str:
"""Display a satoshi amount scaled. Always uses a '.' as a decimal """Display a satoshi amount scaled. Always uses a '.' as a decimal
point and has no thousands separator""" point and has no thousands separator"""
if parse_max_spend(x): if is_max_allowed and 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" assert isinstance(x, (int, float, Decimal)), f"{x!r} should be a number"
scale_factor = pow(10, decimal_point) scale_factor = pow(10, decimal_point)

View File

@@ -171,6 +171,21 @@ class TestCommands(ElectrumTestCase):
with self.assertRaises(binascii.Error): # perhaps it should raise some nice UserFacingException instead with self.assertRaises(binascii.Error): # perhaps it should raise some nice UserFacingException instead
await cmds.decrypt(pubkey, ciphertext+"trailinggarbage", wallet=wallet) await cmds.decrypt(pubkey, ciphertext+"trailinggarbage", wallet=wallet)
def test_format_satoshis(self):
format_satoshis = electrum.commands.format_satoshis
# input type is highly polymorphic:
self.assertEqual(format_satoshis(None), None)
self.assertEqual(format_satoshis(1), "0.00000001")
self.assertEqual(format_satoshis(1.0), "0.00000001")
self.assertEqual(format_satoshis(Decimal(1)), "0.00000001")
# trailing zeroes are cut
self.assertEqual(format_satoshis(51000), "0.00051")
self.assertEqual(format_satoshis(123456_12345670), "123456.1234567")
# sub-satoshi precision is rounded
self.assertEqual(format_satoshis(Decimal(123.456)), "0.00000123")
self.assertEqual(format_satoshis(Decimal(123.5)), "0.00000124")
self.assertEqual(format_satoshis(Decimal(123.789)), "0.00000124")
class TestCommandsTestnet(ElectrumTestCase): class TestCommandsTestnet(ElectrumTestCase):
TESTNET = True TESTNET = True