Repository: vyperlang/vyper

Issue:
update `method_id` output_type
remove `bytes32` output_type, `bytes4` should be the default output type now.


Discussion:

Resolving patch:
diff --git a/vyper/builtin_functions/functions.py b/vyper/builtin_functions/functions.py
--- a/vyper/builtin_functions/functions.py
+++ b/vyper/builtin_functions/functions.py
@@ -73,6 +73,7 @@
     StringDefinition,
     StringPrimitive,
 )
+from vyper.semantics.types.value.bytes_fixed import Bytes4Definition  # type: ignore
 from vyper.semantics.types.value.bytes_fixed import Bytes32Definition
 from vyper.semantics.types.value.numeric import Int256Definition  # type: ignore
 from vyper.semantics.types.value.numeric import Uint256Definition  # type: ignore
@@ -699,25 +700,22 @@ def evaluate(self, node):
 
         if node.keywords:
             return_type = get_type_from_annotation(node.keywords[0].value, DataLocation.UNSET)
-            if isinstance(return_type, Bytes32Definition):
-                length = 32
+            if isinstance(return_type, Bytes4Definition):
+                is_bytes4 = True
             elif isinstance(return_type, BytesArrayDefinition) and return_type.length == 4:
-                length = 4
+                is_bytes4 = False
             else:
-                raise ArgumentException("output_type must be bytes[4] or bytes32", node.keywords[0])
+                raise ArgumentException("output_type must be Bytes[4] or bytes4", node.keywords[0])
         else:
-            # if `output_type` is not given, default to `bytes[4]`
-            length = 4
+            # If `output_type` is not given, default to `Bytes[4]`
+            is_bytes4 = False
 
-        method_id = fourbytes_to_int(keccak256(args[0].value.encode())[:4])
-        value = method_id.to_bytes(length, "big")
+        value = abi_method_id(args[0].value)
 
-        if length == 32:
-            return vy_ast.Hex.from_node(node, value=f"0x{value.hex()}")
-        elif length == 4:
-            return vy_ast.Bytes.from_node(node, value=value)
+        if is_bytes4:
+            return vy_ast.Hex.from_node(node, value=hex(value))
         else:
-            raise CompilerPanic
+            return vy_ast.Bytes.from_node(node, value=value.to_bytes(4, "big"))
 
     def fetch_call_return(self, node):
         raise CompilerPanic("method_id should always be folded")


Test patch:
diff --git a/tests/examples/tokens/test_erc721.py b/tests/examples/tokens/test_erc721.py
--- a/tests/examples/tokens/test_erc721.py
+++ b/tests/examples/tokens/test_erc721.py
@@ -257,8 +257,8 @@ def onERC721Received(
         _from: address,
         _tokenId: uint256,
         _data: Bytes[1024]
-    ) -> bytes32:
-    return method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes32)
+    ) -> bytes4:
+    return method_id("onERC721Received(address,address,uint256,bytes)", output_type=bytes4)
     """
     )
     tx_hash = c.safeTransferFrom(
diff --git a/tests/parser/functions/test_method_id.py b/tests/parser/functions/test_method_id.py
--- a/tests/parser/functions/test_method_id.py
+++ b/tests/parser/functions/test_method_id.py
@@ -13,20 +13,19 @@ def returnten() -> int128:
     assert c.returnten() == 10
 
 
-def test_method_id_bytes32(get_contract):
+def test_method_id_bytes4(get_contract):
     code = """
 @external
-def sig() -> bytes32:
-    return method_id('transfer(address,uint256)', output_type=bytes32)
+def sig() -> bytes4:
+    return method_id('transfer(address,uint256)', output_type=bytes4)
     """
     c = get_contract(code)
     sig = c.sig()
 
-    assert len(sig) == 32
-    assert sig[-4:] == b"\xa9\x05\x9c\xbb"
+    assert sig == b"\xa9\x05\x9c\xbb"
 
 
-def test_method_id_bytes4(get_contract):
+def test_method_id_Bytes4(get_contract):
     code = """
 @external
 def sig() -> Bytes[4]:
@@ -55,8 +54,8 @@ def sig() -> Bytes[4]:
 def test_method_id_invalid_space(get_contract, assert_compile_failed):
     code = """
 @external
-def sig() -> bytes32:
-    return method_id('transfer(address, uint256)', output_type=bytes32)
+def sig() -> bytes4:
+    return method_id('transfer(address, uint256)', output_type=bytes4)
     """
     assert_compile_failed(lambda: get_contract(code))
 
@@ -64,7 +63,7 @@ def sig() -> bytes32:
 def test_method_id_invalid_type(get_contract, assert_compile_failed):
     code = """
 @external
-def sig() -> int128:
-    return method_id('transfer(address,uint256)', output_type=int128)
+def sig() -> bytes32:
+    return method_id('transfer(address,uint256)', output_type=bytes32)
     """
     assert_compile_failed(lambda: get_contract(code))
diff --git a/tests/parser/syntax/test_abi_encode.py b/tests/parser/syntax/test_abi_encode.py
--- a/tests/parser/syntax/test_abi_encode.py
+++ b/tests/parser/syntax/test_abi_encode.py
@@ -25,7 +25,7 @@ def foo(x: Bytes[1]) -> Bytes[32]:
 @external
 def foo(x: uint256) -> Bytes[36]:
     _ensure_tuple: bool = False
-    _method_id: Bytes[4] = method_id("foo()")
+    _method_id: Bytes[4] = method_id("foo()", output_type=Bytes[4])
     return _abi_encode(x, ensure_tuple=_ensure_tuple, method_id=_method_id)
     """,
         TypeMismatch,  # ensure_tuple and method_id both must be literals