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 addr_hash = keccak.new(addr.lower().encode("ascii")).digest().hex() # type: ignore
21
22 if len(addr) != 40:
23 return False
24
25 for i in range(0, 40):
26 if (int(addr_hash[i], 16) > 7 and addr[i].upper() != addr[i]) or (
27 int(addr_hash[i], 16) <= 7 and addr[i].lower() != addr[i]
28 ):
29 return False
30 return True
31
32
33@validator
34def eth_address(value: str, /):
35 """Return whether or not given value is a valid ethereum address.
36
37 Full validation is implemented for ERC20 addresses.
38
39 Examples:
40 >>> eth_address('0x9cc14ba4f9f68ca159ea4ebf2c292a808aaeb598')
41 True
42 >>> eth_address('0x8Ba1f109551bD432803012645Ac136ddd64DBa72')
43 ValidationError(func=eth_address, args={'value': '0x8Ba1f109551bD432803012645Ac136ddd64DBa72'})
44
45 Args:
46 value:
47 Ethereum address string to validate.
48
49 Returns:
50 (Literal[True]): If `value` is a valid ethereum address.
51 (ValidationError): If `value` is an invalid ethereum address.
52 """ # noqa: E501
53 if not _keccak_flag:
54 raise ImportError(
55 "Do `pip install validators[crypto-eth-addresses]` to perform `eth_address` validation."
56 )
57
58 if not value:
59 return False
60
61 return re.compile(r"^0x[0-9a-f]{40}$|^0x[0-9A-F]{40}$").match(
62 value
63 ) or _validate_eth_checksum_address(value)