Repository: vyperlang/vyper

Issue:
Interface implementation enforcing units?
### Version Information

* vyper Version (output of `vyper --version`): 0.1.0b8
* OS: linux
* Python Version (output of `python --version`): 3.6
* Environment (output of `pip freeze`):
pkg-resources==0.0.0
pycryptodome==3.7.3
vyper==0.1.0b8


### What's your issue about?
Compiler erroneously enforcing units on interface method returns

* full output of the error you received:
vyper.exceptions.StructureException: Contract does not comply to supplied Interface(s).
Missing interface functions:
... ( any method that does not match the return unit. Returning wei_value vs uint256 for example )
* what command you ran: vyper
* the code that caused the failure (see [this link](https://help.github.com/articles/basic-writing-and-formatting-syntax/) for help with formatting code)

    @public
    @constant
    def balanceOf(owner: address) -> wei_value:
        return self.balances[owner]


### How can it be fixed?
Wave a magic wand whilst yelling *Non Expecto Metrum!*



Discussion:
Thanks @robrobbins ! I think we should just fix it - ABI can't specify units - so unit checking at this level should be ignored.

Resolving patch:
diff --git a/vyper/parser/parser.py b/vyper/parser/parser.py
--- a/vyper/parser/parser.py
+++ b/vyper/parser/parser.py
@@ -52,6 +52,9 @@
     bytes_to_int,
     calc_mem_gas,
 )
+from vyper.signatures.interface import (
+    check_valid_contract_interface
+)
 
 
 if not hasattr(ast, 'AnnAssign'):
@@ -328,44 +331,8 @@ def parse_tree_to_lll(code, origcode, runtime_only=False, interface_codes=None):
             o, otherfuncs, sigs, external_contracts, origcode, global_ctx, defaultfunc, runtime_only
         )
 
-    # Check interface.
-    if global_ctx._interface:
-        funcs_left = global_ctx._interface.copy()
-
-        for sig, func_sig in sigs.items():
-            if isinstance(func_sig, FunctionSignature):
-                if (
-                    sig in funcs_left and  # noqa: W504
-                    not func_sig.private and  # noqa: W504
-                    funcs_left[sig].output_type == func_sig.output_type
-                ):
-                    del funcs_left[sig]
-            if isinstance(func_sig, EventSignature) and func_sig.sig in funcs_left:
-                del funcs_left[func_sig.sig]
-
-        if funcs_left:
-            error_message = 'Contract does not comply to supplied Interface(s).\n'
-            missing_functions = [
-                str(func_sig)
-                for sig_name, func_sig
-                in funcs_left.items()
-                if isinstance(func_sig, FunctionSignature)
-            ]
-            missing_events = [
-                sig_name
-                for sig_name, func_sig
-                in funcs_left.items()
-                if isinstance(func_sig, EventSignature)
-            ]
-            if missing_functions:
-                error_message += 'Missing interface functions:\n\t{}'.format(
-                    '\n\t'.join(missing_functions)
-                )
-            if missing_events:
-                error_message += 'Missing interface events:\n\t{}'.format(
-                    '\n\t'.join(missing_events)
-                )
-            raise StructureException(error_message)
+    # Check if interface of contract is correct.
+    check_valid_contract_interface(global_ctx, sigs) 
 
     return LLLnode.from_list(o, typ=None)
 
diff --git a/vyper/signatures/interface.py b/vyper/signatures/interface.py
--- a/vyper/signatures/interface.py
+++ b/vyper/signatures/interface.py
@@ -1,4 +1,5 @@
 import ast
+import copy
 import os
 
 import importlib
@@ -201,3 +202,49 @@ def extract_file_interface_imports(code):
                     )
                 imports_dict[a_name.asname] = a_name.name
     return imports_dict
+
+
+def check_valid_contract_interface(global_ctx, contract_sigs):
+
+    if global_ctx._interface:
+        funcs_left = global_ctx._interface.copy()
+
+        for sig, func_sig in contract_sigs.items():
+            if isinstance(func_sig, FunctionSignature):
+                # Remove units, as inteface signatures should not enforce units.
+                clean_sig_output_type = func_sig.output_type
+                if func_sig.output_type:
+                    clean_sig_output_type = copy.deepcopy(func_sig.output_type)
+                    clean_sig_output_type.unit = {}
+                if (
+                    sig in funcs_left and  # noqa: W504
+                    not func_sig.private and  # noqa: W504
+                    funcs_left[sig].output_type == clean_sig_output_type
+                ):
+                    del funcs_left[sig]
+            if isinstance(func_sig, EventSignature) and func_sig.sig in funcs_left:
+                del funcs_left[func_sig.sig]
+
+        if funcs_left:
+            error_message = 'Contract does not comply to supplied Interface(s).\n'
+            missing_functions = [
+                str(func_sig)
+                for sig_name, func_sig
+                in funcs_left.items()
+                if isinstance(func_sig, FunctionSignature)
+            ]
+            missing_events = [
+                sig_name
+                for sig_name, func_sig
+                in funcs_left.items()
+                if isinstance(func_sig, EventSignature)
+            ]
+            if missing_functions:
+                error_message += 'Missing interface functions:\n\t{}'.format(
+                    '\n\t'.join(missing_functions)
+                )
+            if missing_events:
+                error_message += 'Missing interface events:\n\t{}'.format(
+                    '\n\t'.join(missing_events)
+                )
+            raise StructureException(error_message)


Test patch:
diff --git a/tests/parser/functions/test_interfaces.py b/tests/parser/functions/test_interfaces.py
--- a/tests/parser/functions/test_interfaces.py
+++ b/tests/parser/functions/test_interfaces.py
@@ -290,3 +290,31 @@ def test2(a: uint256):
     c = get_contract(code, interface_codes=interface_codes)
 
     assert c.test(2) == 3
+
+
+def test_units_interface(w3, get_contract):
+    code = """
+import balanceof as BalanceOf
+
+implements: BalanceOf
+
+@public
+@constant
+def balanceOf(owner: address) -> wei_value:
+    return as_wei_value(1, "ether")
+    """
+    interface_code = """
+@public
+@constant
+def balanceOf(owner: address) -> uint256:
+    pass
+    """
+    interface_codes = {
+        "BalanceOf": {
+            'type': 'vyper',
+            'code': interface_code
+        }
+    }
+    c = get_contract(code, interface_codes=interface_codes)
+
+    assert c.balanceOf(w3.eth.accounts[0]) == w3.toWei(1, "ether")