Repository: ethereum/eth-utils

Issue:
Add `humanize_wei` helper
### What was wrong?

The humanize utilities could potentially benefit from a `humanize_wei` function for easy human readable ether values


### How can it be fixed?

Exact behavior probably has some room for opinions on the most readable format but at a high level, the function should convert the value to the representation that reduces trailing zeros.  

It *might* be good to prioritize certain more commonly used units like `ether/gwei/wei` when the value is *close enough*

- `100,000,000,000,000,000` aka `100 finney` might be better displayed as `0.1 ether`

Discussion:

Patch:
diff --git a/docs/utilities.rst b/docs/utilities.rst
index 4fd5ec5..30c54ce 100644
--- a/docs/utilities.rst
+++ b/docs/utilities.rst
@@ -1383,6 +1383,21 @@ ellipsis, only showing the first and last four characters of the hash.
     'ipfs://QmTK..7r8u'
 
 
+``humanize_wei(int)`` -> string
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Returns a human-friendly form of units given an amount of wei.
+
+.. doctest::
+
+   >>> from eth_utils import humanize_wei
+   >>> humanize_wei(0)
+   '0 wei'
+   >>> humanize_wei(1000000000000000000000)
+   '1000 ether'
+   >>> humanize_wei(9876543)
+   '0.009876543 gwei'
+
 Logging Utils
 ~~~~~~~~~~~~~~
 
diff --git a/eth_utils/__init__.py b/eth_utils/__init__.py
index 129e6e8..3925554 100644
--- a/eth_utils/__init__.py
+++ b/eth_utils/__init__.py
@@ -68,6 +68,7 @@ from .humanize import (  # noqa: F401
     humanize_integer_sequence,
     humanize_ipfs_uri,
     humanize_seconds,
+    humanize_wei,
 )
 from .logging import (  # noqa: F401
     DEBUG2_LEVEL_NUM,
diff --git a/eth_utils/curried/__init__.py b/eth_utils/curried/__init__.py
index 8d2595e..5f47127 100644
--- a/eth_utils/curried/__init__.py
+++ b/eth_utils/curried/__init__.py
@@ -46,6 +46,7 @@ from eth_utils import (
     humanize_integer_sequence,
     humanize_ipfs_uri,
     humanize_seconds,
+    humanize_wei,
     import_string,
     int_to_big_endian,
     is_0x_prefixed,
diff --git a/eth_utils/humanize.py b/eth_utils/humanize.py
index abc1e75..feaa0e2 100644
--- a/eth_utils/humanize.py
+++ b/eth_utils/humanize.py
@@ -3,6 +3,8 @@ from urllib import parse
 
 from eth_typing import URI, Hash32
 
+from eth_utils.currency import denoms, from_wei
+
 from .toolz import sliding_window, take
 
 
@@ -157,3 +159,15 @@ def humanize_integer_sequence(values_iter: Iterable[int]) -> str:
         return "(empty)"
     else:
         return "|".join(map(_humanize_range, _extract_integer_ranges(*values)))
+
+
+def humanize_wei(number: int) -> str:
+    if number >= denoms.finney:
+        unit = "ether"
+    elif number >= denoms.mwei:
+        unit = "gwei"
+    else:
+        unit = "wei"
+    amount = from_wei(number, unit)
+    x = f"{str(amount)} {unit}"
+    return x
diff --git a/newsfragments/194.feature.rst b/newsfragments/194.feature.rst
new file mode 100644
index 0000000..364068c
--- /dev/null
+++ b/newsfragments/194.feature.rst
@@ -0,0 +1,1 @@
+Added ``humanize_wei`` utility to convert common values to more readable units.
\ No newline at end of file


Test patch:
diff --git a/tests/humanize-utils/test_humanize_wei.py b/tests/humanize-utils/test_humanize_wei.py
new file mode 100644
index 0000000..80fd332
--- /dev/null
+++ b/tests/humanize-utils/test_humanize_wei.py
@@ -0,0 +1,53 @@
+import pytest
+
+from eth_utils import denoms, humanize_wei
+
+
+@pytest.mark.parametrize(
+    "value,expected",
+    (
+        (1, "1 wei"),
+        (denoms.wei, "1 wei"),
+        (denoms.kwei, "1000 wei"),
+        (denoms.babbage, "1000 wei"),
+        (denoms.femtoether, "1000 wei"),
+        (denoms.mwei, "0.001 gwei"),
+        (denoms.lovelace, "0.001 gwei"),
+        (denoms.picoether, "0.001 gwei"),
+        (denoms.gwei, "1 gwei"),
+        (denoms.shannon, "1 gwei"),
+        (denoms.nanoether, "1 gwei"),
+        (denoms.nano, "1 gwei"),
+        (denoms.szabo, "1000 gwei"),
+        (denoms.microether, "1000 gwei"),
+        (denoms.micro, "1000 gwei"),
+        (denoms.finney, "0.001 ether"),
+        (denoms.milliether, "0.001 ether"),
+        (denoms.milli, "0.001 ether"),
+        (denoms.ether, "1 ether"),
+        (denoms.kether, "1000 ether"),
+        (denoms.grand, "1000 ether"),
+        (denoms.mether, "1000000 ether"),
+        (denoms.gether, "1000000000 ether"),
+        (denoms.tether, "1000000000000 ether"),
+        (891, "891 wei"),
+        (987654, "987654 wei"),
+        (9876543, "0.009876543 gwei"),
+        (100 * denoms.finney, "0.1 ether"),
+        (denoms.finney - 1, "999999.999999999 gwei"),
+        (denoms.finney + 1, "0.001000000000000001 ether"),
+        (0, "0 wei"),
+    ),
+)
+def test_humanize_wei(value, expected):
+    assert humanize_wei(value) == expected
+
+
+def test_humanize_error_signed_int():
+    with pytest.raises(ValueError):
+        humanize_wei(-1)
+
+
+def test_humanize_error_string():
+    with pytest.raises(TypeError):
+        humanize_wei("hey")
diff --git a/tests/type-checks/mypy_typing_of_curried_utils.py b/tests/type-checks/mypy_typing_of_curried_utils.py
index 5694482..b45ca9c 100644
--- a/tests/type-checks/mypy_typing_of_curried_utils.py
+++ b/tests/type-checks/mypy_typing_of_curried_utils.py
@@ -37,6 +37,7 @@ from eth_utils.curried import (  # noqa: F401
     humanize_integer_sequence,
     humanize_ipfs_uri,
     humanize_seconds,
+    humanize_wei,
     import_string,
     int_to_big_endian,
     is_0x_prefixed,