Skip to content

Commit

Permalink
add a new feature to validate ethereum address
Browse files Browse the repository at this point in the history
  • Loading branch information
msamsami committed Jun 25, 2024
1 parent 2f300bc commit cc921f7
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 3 deletions.
59 changes: 58 additions & 1 deletion pdm.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ classifiers = [
]
requires-python = ">=3.8"
dynamic = ["version"]
dependencies = []
dependencies = [
"eth-hash[pycryptodome]>=0.7.0",
]

[project.urls]
Homepage = "https://python-validators.github.io/validators"
Expand Down
3 changes: 2 additions & 1 deletion src/validators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .card import amex, card_number, diners, discover, jcb, mastercard, unionpay, visa
from .country import calling_code, country_code, currency
from .cron import cron
from .crypto_addresses.btc_address import btc_address
from .crypto_addresses import btc_address, eth_address
from .domain import domain
from .email import email
from .encoding import base58, base64
Expand Down Expand Up @@ -37,6 +37,7 @@
# ...
"between",
"btc_address",
"eth_address",
# cards
"amex",
"card_number",
Expand Down
6 changes: 6 additions & 0 deletions src/validators/crypto_addresses/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
"""Crypto addresses."""

# local
from .btc_address import btc_address
from .eth_address import eth_address

__all__ = ("btc_address", "eth_address")
54 changes: 54 additions & 0 deletions src/validators/crypto_addresses/eth_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""ETH Address."""

# standard
import re

# external
from eth_hash.auto import keccak

# local
from validators.utils import validator


def _validate_eth_checksum_address(addr: str):
"""Validate ETH type checksum address."""
addr = addr.replace("0x", "")
addr_hash = keccak.new(addr.lower().encode("ascii")).digest().hex()

if len(addr) != 40:
return False

for i in range(0, 40):
if (int(addr_hash[i], 16) > 7 and addr[i].upper() != addr[i]) or (
int(addr_hash[i], 16) <= 7 and addr[i].lower() != addr[i]
):
return False
return True


@validator
def eth_address(value: str, /):
"""Return whether or not given value is a valid ethereum address.
Full validation is implemented for ERC20 addresses.
Examples:
>>> eth_address('0x9cc14ba4f9f68ca159ea4ebf2c292a808aaeb598')
# Output: True
>>> eth_address('0x8Ba1f109551bD432803012645Ac136ddd64DBa72')
# Output: ValidationError(func=eth_address, args=...)
Args:
value:
Ethereum address string to validate.
Returns:
(Literal[True]): If `value` is a valid ethereum address.
(ValidationError): If `value` is an invalid ethereum address.
"""
if not value:
return False

return re.compile(r"^0x[0-9a-f]{40}$|^0x[0-9A-F]{40}$").match(
value
) or _validate_eth_checksum_address(value)
44 changes: 44 additions & 0 deletions tests/crypto_addresses/test_eth_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Test ETH address."""

# external
import pytest

# local
from validators import ValidationError, eth_address


@pytest.mark.parametrize(
"value",
[
"0x8ba1f109551bd432803012645ac136ddd64dba72",
"0x9cc14ba4f9f68ca159ea4ebf2c292a808aaeb598",
"0x5AEDA56215b167893e80B4fE645BA6d5Bab767DE",
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
"0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984",
"0x1234567890123456789012345678901234567890",
"0x57Ab1ec28D129707052df4dF418D58a2D46d5f51",
],
)
def test_returns_true_on_valid_eth_address(value: str):
"""Test returns true on valid eth address."""
assert eth_address(value)


@pytest.mark.parametrize(
"value",
[
"0x742d35Cc6634C0532925a3b844Bc454e4438f44g",
"0x742d35Cc6634C0532925a3b844Bc454e4438f44",
"0xAbcdefg1234567890Abcdefg1234567890Abcdefg",
"0x7c8EE9977c6f96b6b9774b3e8e4Cc9B93B12b2c72",
"0x80fBD7F8B3f81D0e1d6EACAb69AF104A6508AFB1",
"0x7c8EE9977c6f96b6b9774b3e8e4Cc9B93B12b2c7g",
"0x7c8EE9977c6f96b6b9774b3e8e4Cc9B93B12b2c",
"0x7Fb21a171205f3B8d8E4d88A2d2f8A56E45DdB5c",
"validators.eth",
],
)
def test_returns_failed_validation_on_invalid_eth_address(value: str):
"""Test returns failed validation on invalid eth address."""
assert isinstance(eth_address(value), ValidationError)

0 comments on commit cc921f7

Please sign in to comment.