util.format_satoshis: fix for amounts with higher than sat precision
Previously, msat precision was leaking through format_satoshis if the
user's base unit was set to "sat". This was a bug.
Some features of format_satoshis did not work well with such values, such
as the "whitespaces" param.
Old code:
>>> util.format_satoshis(Decimal('45831275.748'), decimal_point=2)
'458312.76'
>>> util.format_satoshis(Decimal('45831275.748'), decimal_point=0)
'45831275.748'
New code:
>>> util.format_satoshis(Decimal('45831275.748'), decimal_point=2)
'458312.76'
>>> util.format_satoshis(Decimal('45831275.748'), decimal_point=0)
'45831276.'
This commit is contained in:
@@ -23,6 +23,22 @@ class TestUtil(ElectrumTestCase):
|
||||
def test_format_satoshis_decimal(self):
|
||||
self.assertEqual("0.00001234", format_satoshis(Decimal(1234)))
|
||||
|
||||
def test_format_satoshis_msat_resolution(self):
|
||||
self.assertEqual("45831276.", format_satoshis(Decimal("45831276"), decimal_point=0))
|
||||
self.assertEqual("45831276.", format_satoshis(Decimal("45831275.748"), decimal_point=0))
|
||||
self.assertEqual("45831275.75", format_satoshis(Decimal("45831275.748"), decimal_point=0, precision=2))
|
||||
self.assertEqual("45831275.748", format_satoshis(Decimal("45831275.748"), decimal_point=0, precision=3))
|
||||
|
||||
self.assertEqual("458312.76", format_satoshis(Decimal("45831276"), decimal_point=2))
|
||||
self.assertEqual("458312.76", format_satoshis(Decimal("45831275.748"), decimal_point=2))
|
||||
self.assertEqual("458312.7575", format_satoshis(Decimal("45831275.748"), decimal_point=2, precision=2))
|
||||
self.assertEqual("458312.75748", format_satoshis(Decimal("45831275.748"), decimal_point=2, precision=3))
|
||||
|
||||
self.assertEqual("458.31276", format_satoshis(Decimal("45831276"), decimal_point=5))
|
||||
self.assertEqual("458.31276", format_satoshis(Decimal("45831275.748"), decimal_point=5))
|
||||
self.assertEqual("458.3127575", format_satoshis(Decimal("45831275.748"), decimal_point=5, precision=2))
|
||||
self.assertEqual("458.31275748", format_satoshis(Decimal("45831275.748"), decimal_point=5, precision=3))
|
||||
|
||||
def test_format_fee_float(self):
|
||||
self.assertEqual("1.7", format_fee_satoshis(1700/1000))
|
||||
|
||||
@@ -48,11 +64,12 @@ class TestUtil(ElectrumTestCase):
|
||||
format_satoshis(-1234, whitespaces=True))
|
||||
|
||||
def test_format_satoshis_diff_positive(self):
|
||||
self.assertEqual("+0.00001234",
|
||||
format_satoshis(1234, is_diff=True))
|
||||
self.assertEqual("+0.00001234", format_satoshis(1234, is_diff=True))
|
||||
self.assertEqual("+456789.00001234", format_satoshis(45678900001234, is_diff=True))
|
||||
|
||||
def test_format_satoshis_diff_negative(self):
|
||||
self.assertEqual("-0.00001234", format_satoshis(-1234, is_diff=True))
|
||||
self.assertEqual("-456789.00001234", format_satoshis(-45678900001234, is_diff=True))
|
||||
|
||||
def test_format_satoshis_plain(self):
|
||||
self.assertEqual("0.00001234", format_satoshis_plain(1234))
|
||||
|
||||
@@ -647,37 +647,37 @@ DECIMAL_POINT = localeconv()['decimal_point'] # type: str
|
||||
|
||||
|
||||
def format_satoshis(
|
||||
x, # in satoshis
|
||||
x: Union[int, float, Decimal, str, None], # amount in satoshis
|
||||
*,
|
||||
num_zeros=0,
|
||||
decimal_point=8,
|
||||
precision=None,
|
||||
is_diff=False,
|
||||
whitespaces=False,
|
||||
num_zeros: int = 0,
|
||||
decimal_point: int = 8, # how much to shift decimal point to left (default: sat->BTC)
|
||||
precision: int = 0, # extra digits after satoshi precision
|
||||
is_diff: bool = False, # if True, enforce a leading sign (+/-)
|
||||
whitespaces: bool = False, # if True, add whitespaces, to align numbers in a column
|
||||
) -> str:
|
||||
if x is None:
|
||||
return 'unknown'
|
||||
if x == '!':
|
||||
return 'max'
|
||||
if precision is None:
|
||||
precision = decimal_point
|
||||
assert isinstance(x, (int, float, Decimal)), f"{x!r} should be a number"
|
||||
# lose redundant precision
|
||||
x = Decimal(x).quantize(Decimal(10) ** (-precision))
|
||||
# format string
|
||||
decimal_format = "." + str(precision) if precision > 0 else ""
|
||||
overall_precision = decimal_point + precision # max digits after final decimal point
|
||||
decimal_format = "." + str(overall_precision) if overall_precision > 0 else ""
|
||||
if is_diff:
|
||||
decimal_format = '+' + decimal_format
|
||||
# initial result
|
||||
scale_factor = pow(10, decimal_point)
|
||||
if not isinstance(x, Decimal):
|
||||
x = Decimal(x).quantize(Decimal('1E-8'))
|
||||
result = ("{:" + decimal_format + "f}").format(x / scale_factor)
|
||||
if "." not in result: result += "."
|
||||
result = result.rstrip('0')
|
||||
# extra decimal places
|
||||
# add extra decimal places (zeros)
|
||||
integer_part, fract_part = result.split(".")
|
||||
if len(fract_part) < num_zeros:
|
||||
fract_part += "0" * (num_zeros - len(fract_part))
|
||||
result = integer_part + DECIMAL_POINT + fract_part
|
||||
# leading/trailing whitespaces
|
||||
# add leading/trailing whitespaces
|
||||
if whitespaces:
|
||||
result += " " * (decimal_point - len(fract_part))
|
||||
result = " " * (15 - len(result)) + result
|
||||
|
||||
Reference in New Issue
Block a user