Repository: vyperlang/vyper

Issue:
Cannot convert directly from uint256 to address
It is not possible to convert directly from `uint256` to `address`.  The following code raises a `TypeMismatch`:

```python
foo: uint256 = 0
foo_address: address = convert(foo, address)
```

However, the conversion is possible if you use `bytes32` as an intermediary type:

```python
foo: uint256 = 0
foo_address: address = convert(convert(foo, bytes32), address)
```

This feels unnecessarily verbose, it should be possible to perform the conversion directly.


Discussion:

Resolving patch:
diff --git a/vyper/functions/convert.py b/vyper/functions/convert.py
--- a/vyper/functions/convert.py
+++ b/vyper/functions/convert.py
@@ -5,7 +5,7 @@
 from vyper import ast as vy_ast
 from vyper.exceptions import InvalidLiteral, StructureException, TypeMismatch
 from vyper.functions.signatures import signature
-from vyper.parser.arg_clamps import int128_clamp
+from vyper.parser.arg_clamps import address_clamp, int128_clamp
 from vyper.parser.parser_utils import LLLnode, byte_array_to_num, getpos
 from vyper.types import BaseType, ByteArrayType, StringType, get_type
 from vyper.utils import DECIMAL_DIVISOR, MemoryPositions, SizeLimits
@@ -254,11 +254,10 @@ def to_bytes32(expr, args, kwargs, context):
         )
 
 
-@signature(("bytes32"), "*")
+@signature(("bytes32", "uint256"), "*")
 def to_address(expr, args, kwargs, context):
-    in_arg = args[0]
-
-    return LLLnode(value=in_arg.value, args=in_arg.args, typ=BaseType("address"), pos=getpos(expr))
+    lll_node = ["with", "_in_arg", args[0], ["seq", address_clamp("_in_arg"), "_in_arg"]]
+    return LLLnode.from_list(lll_node, typ=BaseType("address"), pos=getpos(expr))
 
 
 def _to_bytelike(expr, args, kwargs, context, bytetype):
diff --git a/vyper/parser/arg_clamps.py b/vyper/parser/arg_clamps.py
--- a/vyper/parser/arg_clamps.py
+++ b/vyper/parser/arg_clamps.py
@@ -61,11 +61,9 @@ def make_arg_clamper(datapos, mempos, typ, is_init=False):
         return LLLnode.from_list(lll, typ=typ, annotation="checking bool input")
     # Addresses: make sure they're in range
     elif is_base_type(typ, "address"):
-        if version_check(begin="constantinople"):
-            lll = ["assert", ["iszero", ["shr", 160, data_decl]]]
-        else:
-            lll = ["uclamplt", data_decl, ["mload", MemoryPositions.ADDRSIZE]]
-        return LLLnode.from_list(lll, typ=typ, annotation="checking address input")
+        return LLLnode.from_list(
+            address_clamp(data_decl), typ=typ, annotation="checking address input"
+        )
     # Bytes: make sure they have the right size
     elif isinstance(typ, ByteArrayLike):
         return LLLnode.from_list(
@@ -124,6 +122,13 @@ def make_arg_clamper(datapos, mempos, typ, is_init=False):
         return LLLnode.from_list("pass")
 
 
+def address_clamp(lll_node):
+    if version_check(begin="constantinople"):
+        return ["assert", ["iszero", ["shr", 160, lll_node]]]
+    else:
+        return ["uclamplt", lll_node, ["mload", MemoryPositions.ADDRSIZE]]
+
+
 def int128_clamp(lll_node):
     if version_check(begin="constantinople"):
         return [


Test patch:
diff --git a/tests/parser/functions/test_convert_to_address.py b/tests/parser/functions/test_convert_to_address.py
--- a/tests/parser/functions/test_convert_to_address.py
+++ b/tests/parser/functions/test_convert_to_address.py
@@ -10,3 +10,49 @@ def test_bytes_to_address(x: bytes32) -> address:
 
     c = get_contract_with_gas_estimation(test_bytes_to_address)
     assert c.test_bytes_to_address(test_bytes) == test_address
+
+
+def test_bytes32_clamping(get_contract, assert_tx_failed):
+    test_passing = ("ff" * 20).zfill(64)
+    test_fails = ("1" + "ff" * 20).zfill(64)
+
+    test_bytes_to_address = """
+@external
+def foo(x: bytes32) -> address:
+    return convert(x, address)
+    """
+
+    c = get_contract(test_bytes_to_address)
+
+    assert_tx_failed(lambda: c.foo(test_fails))
+    assert c.foo(test_passing) == "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF"
+
+
+def test_convert_from_uint256(get_contract_with_gas_estimation):
+    test_address = "0xF5D4020dCA6a62bB1efFcC9212AAF3c9819E30D7"
+    test_int = int(test_address, 16)
+
+    test_bytes_to_address = """
+@external
+def foo(x: uint256) -> address:
+    return convert(x, address)
+    """
+
+    c = get_contract_with_gas_estimation(test_bytes_to_address)
+    assert c.foo(test_int) == test_address
+
+
+def test_uint256_clamping(get_contract, assert_tx_failed):
+    test_fails = 2 ** 160
+    test_passing = test_fails - 1
+
+    test_bytes_to_address = """
+@external
+def foo(x: uint256) -> address:
+    return convert(x, address)
+    """
+
+    c = get_contract(test_bytes_to_address)
+
+    assert_tx_failed(lambda: c.foo(test_fails))
+    assert c.foo(test_passing) == "0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF"