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 functools import wraps
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 re
@@ -101,8 +101,14 @@ def satoshis(amount):
return int(COIN*to_decimal(amount)) if amount is not None else None
def format_satoshis(x):
return str(to_decimal(x)/COIN) if x is not None else None
def format_satoshis(x: Union[str, float, int, Decimal, None]) -> Optional[str]:
"""
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:
@@ -476,7 +482,7 @@ class Commands(Logger):
for txin in wallet.get_utxos():
d = txin.to_json()
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)
return coins
@@ -719,13 +725,13 @@ class Commands(Logger):
"""Return the balance of your wallet. """
c, u, x = wallet.get_balance()
l = wallet.lnworker.get_balance() if wallet.lnworker else None
out = {"confirmed": str(to_decimal(c)/COIN)}
out = {"confirmed": format_satoshis(c)}
if u:
out["unconfirmed"] = str(to_decimal(u)/COIN)
out["unconfirmed"] = format_satoshis(u)
if x:
out["unmatured"] = str(to_decimal(x)/COIN)
out["unmatured"] = format_satoshis(x)
if l:
out["lightning"] = str(to_decimal(l)/COIN)
out["lightning"] = format_satoshis(l)
return out
@command('n')
@@ -738,8 +744,8 @@ class Commands(Logger):
"""
sh = bitcoin.address_to_scripthash(address)
out = await self.network.get_balance_for_scripthash(sh)
out["confirmed"] = str(to_decimal(out["confirmed"])/COIN)
out["unconfirmed"] = str(to_decimal(out["unconfirmed"])/COIN)
out["confirmed"] = format_satoshis(out["confirmed"])
out["unconfirmed"] = format_satoshis(out["unconfirmed"])
return out
@command('n')
@@ -1183,7 +1189,7 @@ class Commands(Logger):
if labels or balance:
item = (item,)
if balance:
item += (util.format_satoshis(sum(wallet.get_addr_balance(addr))),)
item += (format_satoshis(sum(wallet.get_addr_balance(addr))),)
if labels:
item += (repr(wallet.get_label_for_address(addr)),)
out.append(item)

View File

@@ -793,10 +793,11 @@ 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)
is_max_allowed: bool = True,
) -> str:
"""Display a satoshi amount scaled. Always uses a '.' as a decimal
point and has no thousands separator"""
if parse_max_spend(x):
if is_max_allowed and 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)

View File

@@ -171,6 +171,21 @@ class TestCommands(ElectrumTestCase):
with self.assertRaises(binascii.Error): # perhaps it should raise some nice UserFacingException instead
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):
TESTNET = True