1"""ETH Address."""
2
3# standard
4import re
5
6# local
7from validators.utils import validator
8
9_keccak_flag = True
10try:
11 # external
12 from eth_hash.auto import keccak
13except ImportError:
14 _keccak_flag = False
15
16
17def _validate_eth_checksum_address(addr: str):
18 """Validate ETH type checksum address."""
19 addr = addr.replace("0x", "")
20
21 if not re.fullmatch(r"[0-9a-fA-F]{40}", addr):
22 return False
23
24 addr_hash = keccak.new(addr.lower().encode("ascii")).digest().hex() # type: ignore
25
26 for i in range(0, 40):
27 if (int(addr_hash[i], 16) > 7 and addr[i].upper() != addr[i]) or (
28 int(addr_hash[i], 16) <= 7 and addr[i].lower() != addr[i]
29 ):
30 return False
31 return True
32
33
34@validator
35def eth_address(value: str, /):
36 """Return whether or not given value is a valid ethereum address.
37
38 Full validation is implemented for ERC20 addresses.
39
40 Examples:
41 >>> eth_address('0x9cc14ba4f9f68ca159ea4ebf2c292a808aaeb598')
42 True
43 >>> eth_address('0x8Ba1f109551bD432803012645Ac136ddd64DBa72')
44 ValidationError(func=eth_address, args={'value': '0x8Ba1f109551bD432803012645Ac136ddd64DBa72'})
45
46 Args:
47 value:
48 Ethereum address string to validate.
49
50 Returns:
51 (Literal[True]): If `value` is a valid ethereum address.
52 (ValidationError): If `value` is an invalid ethereum address.
53 """ # noqa: E501
54 if not _keccak_flag:
55 raise ImportError(
56 "Do `pip install validators[crypto-eth-addresses]` to perform `eth_address` validation."
57 )
58
59 if not value:
60 return False
61
62 return re.compile(r"^0x[0-9a-f]{40}$|^0x[0-9A-F]{40}$").fullmatch(
63 value
64 ) or _validate_eth_checksum_address(value)