diff --git a/crypto/utils/unit_converter.py b/crypto/utils/unit_converter.py index be99192..dfbdc12 100644 --- a/crypto/utils/unit_converter.py +++ b/crypto/utils/unit_converter.py @@ -1,25 +1,30 @@ # import numpy as np -from decimal import Decimal +from decimal import Decimal, getcontext from typing import Union +# Python's Decimal type defaults to 28 digits of precision, which is not enough +# for blockchain math. The largest possible value (uint256) has 78 digits, so we +# increase the precision to 78 to avoid rounding errors when working with big numbers. +getcontext().prec = 78 + class UnitConverter: WEI_MULTIPLIER = '1' GWEI_MULTIPLIER = '1000000000' # 1e9 ARK_MULTIPLIER = '1000000000000000000' # 1e18 @staticmethod - def parse_units(value: Union[float, int, str, Decimal], unit='ark') -> int: + def parse_units(value: Union[float, int, str, Decimal], unit='ark') -> Decimal: value = Decimal(str(value)) unit = unit.lower() if unit == 'wei': - return int((value * Decimal(UnitConverter.WEI_MULTIPLIER)).normalize()) + return (value * Decimal(UnitConverter.WEI_MULTIPLIER)).normalize() if unit == 'gwei': - return int((value * Decimal(UnitConverter.GWEI_MULTIPLIER)).normalize()) + return (value * Decimal(UnitConverter.GWEI_MULTIPLIER)).normalize() if unit == 'ark': - return int((value * Decimal(UnitConverter.ARK_MULTIPLIER)).normalize()) + return (value * Decimal(UnitConverter.ARK_MULTIPLIER)).normalize() raise ValueError(f"Unsupported unit: {unit}. Supported units are 'wei', 'gwei', and 'ark'.") diff --git a/tests/utils/test_unit_converter.py b/tests/utils/test_unit_converter.py index af8e4c4..de63aee 100644 --- a/tests/utils/test_unit_converter.py +++ b/tests/utils/test_unit_converter.py @@ -2,41 +2,47 @@ from decimal import Decimal def test_it_should_parse_units_into_wei(): - assert UnitConverter.parse_units(1, 'wei') == 1 - assert UnitConverter.parse_units(1.0, 'wei') == 1 - assert UnitConverter.parse_units('1', 'wei') == 1 - assert UnitConverter.parse_units('1.0', 'wei') == 1 + assert UnitConverter.parse_units(1, 'wei') == Decimal('1') + assert UnitConverter.parse_units(1.0, 'wei') == Decimal('1') + assert UnitConverter.parse_units('1', 'wei') == Decimal('1') + assert UnitConverter.parse_units('1.0', 'wei') == Decimal('1') - assert UnitConverter.parse_units(Decimal(1), 'wei') == 1 - assert UnitConverter.parse_units(Decimal(1.0), 'wei') == 1 - assert UnitConverter.parse_units(Decimal('1'), 'wei') == 1 - assert UnitConverter.parse_units(Decimal('1.0'), 'wei') == 1 + assert UnitConverter.parse_units(Decimal(1), 'wei') == Decimal('1') + assert UnitConverter.parse_units(Decimal(1.0), 'wei') == Decimal('1') + assert UnitConverter.parse_units(Decimal('1'), 'wei') == Decimal('1') + assert UnitConverter.parse_units(Decimal('1.0'), 'wei') == Decimal('1') + + assert isinstance(UnitConverter.parse_units(1, 'wei'), Decimal) def test_it_should_parse_units_into_gwei(): - assert UnitConverter.parse_units(1, 'gwei') == 1000000000 - assert UnitConverter.parse_units(1.0, 'gwei') == 1000000000 - assert UnitConverter.parse_units('1', 'gwei') == 1000000000 - assert UnitConverter.parse_units('1.0', 'gwei') == 1000000000 + assert UnitConverter.parse_units(1, 'gwei') == Decimal('1000000000') + assert UnitConverter.parse_units(1.0, 'gwei') == Decimal('1000000000') + assert UnitConverter.parse_units('1', 'gwei') == Decimal('1000000000') + assert UnitConverter.parse_units('1.0', 'gwei') == Decimal('1000000000') + + assert UnitConverter.parse_units(Decimal(1), 'gwei') == Decimal('1000000000') + assert UnitConverter.parse_units(Decimal(1.0), 'gwei') == Decimal('1000000000') + assert UnitConverter.parse_units(Decimal('1'), 'gwei') == Decimal('1000000000') + assert UnitConverter.parse_units(Decimal('1.0'), 'gwei') == Decimal('1000000000') - assert UnitConverter.parse_units(Decimal(1), 'gwei') == 1000000000 - assert UnitConverter.parse_units(Decimal(1.0), 'gwei') == 1000000000 - assert UnitConverter.parse_units(Decimal('1'), 'gwei') == 1000000000 - assert UnitConverter.parse_units(Decimal('1.0'), 'gwei') == 1000000000 + assert isinstance(UnitConverter.parse_units(1, 'gwei'), Decimal) def test_it_should_parse_units_into_ark(): - assert UnitConverter.parse_units(1, 'ark') == 1000000000000000000 - assert UnitConverter.parse_units(1.0, 'ark') == 1000000000000000000 - assert UnitConverter.parse_units('1', 'ark') == 1000000000000000000 - assert UnitConverter.parse_units('1.0', 'ark') == 1000000000000000000 + assert UnitConverter.parse_units(1, 'ark') == Decimal('1000000000000000000') + assert UnitConverter.parse_units(1.0, 'ark') == Decimal('1000000000000000000') + assert UnitConverter.parse_units('1', 'ark') == Decimal('1000000000000000000') + assert UnitConverter.parse_units('1.0', 'ark') == Decimal('1000000000000000000') - assert UnitConverter.parse_units(Decimal(1), 'ark') == 1000000000000000000 - assert UnitConverter.parse_units(Decimal(1.0), 'ark') == 1000000000000000000 - assert UnitConverter.parse_units(Decimal('1'), 'ark') == 1000000000000000000 - assert UnitConverter.parse_units(Decimal('1.0'), 'ark') == 1000000000000000000 + assert UnitConverter.parse_units(Decimal(1), 'ark') == Decimal('1000000000000000000') + assert UnitConverter.parse_units(Decimal(1.0), 'ark') == Decimal('1000000000000000000') + assert UnitConverter.parse_units(Decimal('1'), 'ark') == Decimal('1000000000000000000') + assert UnitConverter.parse_units(Decimal('1.0'), 'ark') == Decimal('1000000000000000000') + + assert isinstance(UnitConverter.parse_units(1, 'ark'), Decimal) def test_it_should_parse_decimal_units_into_ark(): - assert UnitConverter.parse_units(0.1, 'ark') == 100000000000000000 - assert UnitConverter.parse_units('0.1', 'ark') == 100000000000000000 + assert UnitConverter.parse_units(0.1, 'ark') == Decimal('100000000000000000') + assert UnitConverter.parse_units('0.1', 'ark') == Decimal('100000000000000000') def test_it_should_format_units_from_wei(): assert UnitConverter.format_units(1, 'wei') == 1.0 @@ -120,3 +126,13 @@ def test_it_should_convert_gwei_to_ark(): assert UnitConverter.gwei_to_ark(Decimal('1')) == '0.000000001' assert UnitConverter.gwei_to_ark(Decimal('1000000000'), 'DARK') == '1 DARK' assert UnitConverter.gwei_to_ark(Decimal('1000000000')) == '1' + +def test_it_should_handle_large_token_supply(): + large_supply = '999999999999999999999999999999999999999' + result = UnitConverter.format_units(large_supply, 'ark') + assert isinstance(result, Decimal) + assert result == Decimal('999999999999999999999.999999999999999999') + + result = UnitConverter.parse_units(large_supply, 'wei') + assert isinstance(result, Decimal) + assert result == Decimal(large_supply)