Repository: vyperlang/vyper

Issue:
Non-checksummed address within literal array produces unexpected error
Using a non-checksummed address within a literal address array produces an incorrect and hard-to-grok error:

Example contract:

```python
foo: constant(address[1]) = [0x6b175474e89094c44da98b954eedeac495271d0f]
```

Exception:

```python
OverflowException: Numeric literal is outside of allowable range for number types
       3
  ---> 4 foo: constant(address[1]) = [0x6b175474e89094c44da98b954eedeac495271d0f]
  ------------------------------------^
       5
```


Discussion:

Resolving patch:
diff --git a/vyper/ast/nodes.py b/vyper/ast/nodes.py
--- a/vyper/ast/nodes.py
+++ b/vyper/ast/nodes.py
@@ -15,7 +15,12 @@
     ZeroDivisionException,
 )
 from vyper.settings import VYPER_ERROR_CONTEXT_LINES, VYPER_ERROR_LINE_NUMBERS
-from vyper.utils import MAX_DECIMAL_PLACES, SizeLimits, annotate_source_code
+from vyper.utils import (
+    MAX_DECIMAL_PLACES,
+    SizeLimits,
+    annotate_source_code,
+    checksum_encode,
+)
 
 NODE_BASE_ATTRIBUTES = (
     "_children",
@@ -712,7 +717,7 @@ def validate(self):
         super().validate()
 
 
-class Hex(Num):
+class Hex(Constant):
     """
     A hexadecimal value, e.g. `0xFF`
 
@@ -723,10 +728,17 @@ class Hex(Num):
     """
 
     __slots__ = ()
+    _translated_fields = {"n": "value"}
 
     def validate(self):
         if len(self.value) % 2:
             raise InvalidLiteral("Hex notation requires an even number of digits", self)
+        if len(self.value) == 42 and checksum_encode(self.value) != self.value:
+            raise InvalidLiteral(
+                "Address checksum mismatch. If you are sure this is the right "
+                f"address, the correct checksummed form is: {checksum_encode(self.value)}",
+                self,
+            )
 
 
 class Str(Constant):
diff --git a/vyper/context/types/value/address.py b/vyper/context/types/value/address.py
--- a/vyper/context/types/value/address.py
+++ b/vyper/context/types/value/address.py
@@ -3,7 +3,7 @@
 from vyper.context.types.value.boolean import BoolDefinition
 from vyper.context.types.value.bytes_fixed import Bytes32Definition
 from vyper.context.types.value.numeric import Uint256Definition
-from vyper.exceptions import InvalidLiteral
+from vyper.exceptions import CompilerPanic, InvalidLiteral
 from vyper.utils import checksum_encode
 
 
@@ -30,9 +30,6 @@ def from_literal(cls, node: vy_ast.Constant) -> AddressDefinition:
         if len(addr) != 42:
             raise InvalidLiteral("Invalid literal for type 'address'", node)
         if checksum_encode(addr) != addr:
-            raise InvalidLiteral(
-                "Address checksum mismatch. If you are sure this is the right "
-                f"address, the correct checksummed form is: {checksum_encode(addr)}",
-                node,
-            )
+            # this should have been caught in `vyper.ast.nodes.Hex.validate`
+            raise CompilerPanic("Address checksum mismatch")
         return AddressDefinition()


Test patch:
diff --git a/tests/ast/nodes/test_hex.py b/tests/ast/nodes/test_hex.py
new file mode 100644
--- /dev/null
+++ b/tests/ast/nodes/test_hex.py
@@ -0,0 +1,37 @@
+import pytest
+
+from vyper import ast as vy_ast
+from vyper.exceptions import InvalidLiteral
+
+code_invalid_checksum = [
+    """
+foo: constant(address) = 0x6b175474e89094c44da98b954eedeac495271d0f
+    """,
+    """
+foo: constant(address[1]) = [0x6b175474e89094c44da98b954eedeac495271d0f]
+    """,
+    """
+@external
+def foo():
+    bar: address = 0x6b175474e89094c44da98b954eedeac495271d0f
+    """,
+    """
+@external
+def foo():
+    bar: address[1] = [0x6b175474e89094c44da98b954eedeac495271d0f]
+    """,
+    """
+@external
+def foo():
+    for i in [0x6b175474e89094c44da98b954eedeac495271d0f]:
+        pass
+    """,
+]
+
+
+@pytest.mark.parametrize("code", code_invalid_checksum)
+def test_invalid_checksum(code):
+    vyper_module = vy_ast.parse_to_ast(code)
+
+    with pytest.raises(InvalidLiteral):
+        vy_ast.validation.validate_literal_nodes(vyper_module)