Repository: ethereum/web3.py

Issue:
Web3.isAddress doesn't work for non prefixed checksumed values
* Version: 4.0.0b11
* Python: 3.6
* OS: linux

### What was wrong?

As stated in the docs http://web3py.readthedocs.io/en/latest/overview.html#Web3.isAddress the function Web3.isAddress(value) should **allow both 0x prefixed and non prefixed values**.

If the address is not checksumed, it's ok not to have the **0x**:

```
>>> Web3.isAddress('d3cda913deb6f67967b99d67acdfa1712c293601')
>>> True
```

But if it's checksumed

```
>>> Web3.isAddress('d3CdA913deB6f67967B99D67aCDFa1712C293601')
>>> False
```

No problem if we add the **0x**:

```
>>> Web3.isAddress('0xd3CdA913deB6f67967B99D67aCDFa1712C293601')
>>> True
```

### How can it be fixed?

Changing the documentation to state that checksumed addresses must have 0x or changing the function to accept checksumed addresses with 0x. I would just remove 0x at the beginning of the function (if found) and work with the address as that.  


Discussion:
pacrob: I think your newsfragments got crossed :grin: 

Patch:
diff --git a/docs/index.rst b/docs/index.rst
index 149310ac..69a26fd8 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,17 +1,6 @@
 .. meta::
    :description: Python Web3 SDK for Ethereum and EVM blockchains
 
-.. important::
-
-    For **ENS (Ethereum Name Service)** users, web3.py ``v6.6.0`` introduced ENS name
-    normalization standard
-    `ENSIP-15 <https://docs.ens.domains/ens-improvement-proposals/ensip-15-normalization-standard>`_.
-    This update to ENS name validation and normalization won't affect ~99%
-    of names but may prevent invalid names from being created and from interacting with
-    the ENS contracts via web3.py. We feel strongly that this change, though breaking,
-    is in the best interest of our users as it ensures compatibility with the latest ENS
-    standards.
-
 gm
 ==
 
diff --git a/newsfragments/3248.misc.rst b/newsfragments/3248.misc.rst
new file mode 100644
index 00000000..c9d5b73b
--- /dev/null
+++ b/newsfragments/3248.misc.rst
@@ -0,0 +1,1 @@
+Upgrade ``eth-utils`` to ``v4.0.0``.
diff --git a/newsfragments/3254.docs.rst b/newsfragments/3254.docs.rst
new file mode 100644
index 00000000..e27b049b
--- /dev/null
+++ b/newsfragments/3254.docs.rst
@@ -0,0 +1,1 @@
+Remove ENS v6 breaking change warning from v7
diff --git a/newsfragments/3257.performance.rst b/newsfragments/3257.performance.rst
new file mode 100644
index 00000000..0a0d109d
--- /dev/null
+++ b/newsfragments/3257.performance.rst
@@ -0,0 +1,1 @@
+Remove call to ``parse_block_identifier`` when initializing ``ContractCaller`` functions.
diff --git a/setup.py b/setup.py
index 2716cb1c..89bf0b34 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ from setuptools import (
 
 extras_require = {
     "tester": [
-        "eth-tester[py-evm]==v0.10.0-b.1",
+        "eth-tester[py-evm]==v0.10.0-b.3",
         "py-geth>=4.1.0",
     ],
     "linter": [
@@ -73,7 +73,7 @@ setup(
         "eth-account>=0.8.0",
         "eth-hash[pycryptodome]>=0.5.1",
         "eth-typing>=3.0.0",
-        "eth-utils>=2.1.0",
+        "eth-utils>=4.0.0",
         "hexbytes>=0.1.0,<0.4.0",
         "jsonschema>=4.0.0",
         "protobuf>=4.21.6",
diff --git a/web3/_utils/contracts.py b/web3/_utils/contracts.py
index 93a49af1..3871bb3b 100644
--- a/web3/_utils/contracts.py
+++ b/web3/_utils/contracts.py
@@ -438,23 +438,6 @@ def parse_block_identifier_int(w3: "Web3", block_identifier_int: int) -> BlockNu
     return BlockNumber(block_num)
 
 
-def parse_block_identifier_no_extra_call(
-    w3: Union["Web3", "AsyncWeb3"], block_identifier: BlockIdentifier
-) -> BlockIdentifier:
-    if block_identifier is None:
-        return w3.eth.default_block
-    elif isinstance(block_identifier, int) and block_identifier >= 0:
-        return block_identifier
-    elif block_identifier in ["latest", "earliest", "pending", "safe", "finalized"]:
-        return block_identifier
-    elif isinstance(block_identifier, bytes):
-        return HexBytes(block_identifier)
-    elif is_hex_encoded_block_hash(block_identifier):
-        return HexStr(str(block_identifier))
-    else:
-        raise BlockNumberOutofRange
-
-
 async def async_parse_block_identifier(
     async_w3: "AsyncWeb3", block_identifier: BlockIdentifier
 ) -> BlockIdentifier:
diff --git a/web3/contract/async_contract.py b/web3/contract/async_contract.py
index e6de2baa..9d828c6a 100644
--- a/web3/contract/async_contract.py
+++ b/web3/contract/async_contract.py
@@ -39,7 +39,6 @@ from web3._utils.compat import (
 )
 from web3._utils.contracts import (
     async_parse_block_identifier,
-    parse_block_identifier_no_extra_call,
 )
 from web3._utils.datatypes import (
     PropertyCheckingFactory,
@@ -584,15 +583,11 @@ class AsyncContractCaller(BaseContractCaller):
                     decode_tuples=decode_tuples,
                 )
 
-                # TODO: The no_extra_call method gets around the fact that we can't call
-                #  the full async method from within a class's __init__ method. We need
-                #  to see if there's a way to account for all desired elif cases.
-                block_id = parse_block_identifier_no_extra_call(w3, block_identifier)
                 caller_method = partial(
                     self.call_function,
                     fn,
                     transaction=transaction,
-                    block_identifier=block_id,
+                    block_identifier=block_identifier,
                     ccip_read_enabled=ccip_read_enabled,
                 )
 
diff --git a/web3/contract/contract.py b/web3/contract/contract.py
index e6c1ab17..900b25de 100644
--- a/web3/contract/contract.py
+++ b/web3/contract/contract.py
@@ -588,12 +588,11 @@ class ContractCaller(BaseContractCaller):
                     decode_tuples=decode_tuples,
                 )
 
-                block_id = parse_block_identifier(w3, block_identifier)
                 caller_method = partial(
                     self.call_function,
                     fn,
                     transaction=transaction,
-                    block_identifier=block_id,
+                    block_identifier=block_identifier,
                     ccip_read_enabled=ccip_read_enabled,
                 )
 


Test patch:
diff --git a/tests/core/contracts/test_contract_caller_interface.py b/tests/core/contracts/test_contract_caller_interface.py
index 046aa384..a25ef4bf 100644
--- a/tests/core/contracts/test_contract_caller_interface.py
+++ b/tests/core/contracts/test_contract_caller_interface.py
@@ -1,6 +1,7 @@
 import pytest
 
 from web3.exceptions import (
+    BlockNumberOutofRange,
     MismatchedABI,
     NoABIFound,
     NoABIFunctionsFound,
@@ -130,19 +131,56 @@ def test_caller_with_a_nonexistent_function(math_contract):
         contract.caller.thisFunctionDoesNotExist()
 
 
+def test_caller_with_invalid_block_identifier(w3, math_contract):
+    w3.provider.make_request(method="evm_mine", params=[5])
+    math_contract.functions.incrementCounter().transact()
+    math_contract.functions.incrementCounter().transact()
+
+    # Invalid `block_identifier` should not raise when passed to `caller` directly.
+    # The function call itself parses the value.
+    caller = math_contract.caller(block_identifier="abc")
+    with pytest.raises(BlockNumberOutofRange):
+        caller.counter()
+
+    # Calling the function with an invalid `block_identifier` will raise.
+    default_caller = math_contract.caller()
+    with pytest.raises(BlockNumberOutofRange):
+        default_caller.counter(block_identifier="abc")
+
+
 def test_caller_with_block_identifier(w3, math_contract):
-    start_num = w3.eth.get_block("latest").number
+    start = w3.eth.get_block("latest")
+    start_num = start.number
+    block_hash = start.hash
+    block_hex = w3.to_hex(block_hash)
+
     assert math_contract.caller.counter() == 0
 
     w3.provider.make_request(method="evm_mine", params=[5])
     math_contract.functions.incrementCounter().transact()
     math_contract.functions.incrementCounter().transact()
 
-    output1 = math_contract.caller(block_identifier=start_num + 6).counter()
-    output2 = math_contract.caller(block_identifier=start_num + 7).counter()
-
-    assert output1 == 1
-    assert output2 == 2
+    assert_ids = [
+        (block_hash, 0),
+        (block_hex, 0),
+        (start_num, 0),
+        (start_num + 6, 1),
+        (start_num + 7, 2),
+        ("latest", 2),
+        (None, 2),
+    ]
+
+    for block_identifier, expected_value in assert_ids:
+        assert (
+            math_contract.caller(block_identifier=block_identifier).counter()
+            == expected_value
+        )
+
+        # Function arg `block_identifier` produces same result
+        assert (
+            math_contract.caller().counter(block_identifier=block_identifier)
+            == expected_value
+        )
 
 
 def test_caller_with_block_identifier_and_transaction_dict(
@@ -298,21 +336,64 @@ async def test_async_caller_with_a_nonexistent_function(async_math_contract):
         await contract.caller.thisFunctionDoesNotExist()
 
 
+@pytest.mark.asyncio
+async def test_async_caller_with_invalid_block_identifier(
+    async_w3, async_math_contract
+):
+    await async_w3.provider.make_request(method="evm_mine", params=[5])
+    await async_math_contract.functions.incrementCounter().transact()
+    await async_math_contract.functions.incrementCounter().transact()
+
+    # Invalid `block_identifier` should not raise when passed to `caller` directly.
+    # The function call itself parses the value.
+    caller = async_math_contract.caller(block_identifier="abc")
+    with pytest.raises(BlockNumberOutofRange):
+        await caller.counter()
+
+    # Calling the function with an invalid `block_identifier` will raise.
+    default_caller = async_math_contract.caller()
+    with pytest.raises(BlockNumberOutofRange):
+        await default_caller.counter(block_identifier="abc")
+
+
 @pytest.mark.asyncio
 async def test_async_caller_with_block_identifier(async_w3, async_math_contract):
     start = await async_w3.eth.get_block("latest")
     start_num = start.number
+    block_hash = start.hash
+    block_hex = async_w3.to_hex(block_hash)
+
     assert await async_math_contract.caller.counter() == 0
 
     await async_w3.provider.make_request(method="evm_mine", params=[5])
     await async_math_contract.functions.incrementCounter().transact()
     await async_math_contract.functions.incrementCounter().transact()
 
-    output1 = await async_math_contract.caller(block_identifier=start_num + 6).counter()
-    output2 = await async_math_contract.caller(block_identifier=start_num + 7).counter()
-
-    assert output1 == 1
-    assert output2 == 2
+    assert_ids = [
+        (block_hash, 0),
+        (block_hex, 0),
+        (start_num, 0),
+        (start_num + 6, 1),
+        (start_num + 7, 2),
+        ("latest", 2),
+        (None, 2),
+    ]
+
+    for block_identifier, expected_value in assert_ids:
+        assert (
+            await async_math_contract.caller(
+                block_identifier=block_identifier
+            ).counter()
+            == expected_value
+        )
+
+        # Function arg `block_identifier` produces same result
+        assert (
+            await async_math_contract.caller().counter(
+                block_identifier=block_identifier
+            )
+            == expected_value
+        )
 
 
 @pytest.mark.asyncio