Repository: vyperlang/vyper

Issue:
"send" as a reserved keyword collides with the ERC777 specification
### Version Information

* vyper Version: 0.1.0b4
* pyethereum Version: none
* OS: 4.18.16-arch1-1-ARCH
* Python Version (python --version): 3.7.1

### What's your issue about?

While implementing the https://github.com/ethereum/EIPs/issues/777 specification as a Vyper contract I was unable to due to the collision of the specification's `send` method with the reserved keyword `send` in Vyper used for sending ether. Trying to declare a `send` method on a contract throws the following exception: 
```
vyper.exceptions.FunctionDeclarationException: Function name invalid: send
```

### How can it be fixed?

I see three options:
- Vyper changes the `send` keyword to something else.
- ERC777 changes the `send` method to something else.
- As @mcdee suggested in the ERC777 issue, Vyper could allow for a contract method `send` that is different from the built-in function `send`. These two would be disambiguated by the way in which they are invoked. The built-in function could retain the naked invocation `send <address, amount>`  while a method declared on the contract would be invoked with `self.send()`. From the external point of view only the contract method would be accessible.
 
#### Cute Animal Picture

![image](https://user-images.githubusercontent.com/25539605/48189251-0bca0080-e340-11e8-93e3-e26e2f80a20f.png)




Discussion:
Thanks for raising that up. Personally, I think ERC-777 should change that in order to keep things more clear, as `send` is used both in Vyper and Solidity and I think it would be best to reserve that.
I'm not sure about that but I haven't seen much usage of this currently in production so I think changing the proposed ERC should be a problem.

Any thought from others here?
@ben-kaufman a quick scan of 4byte shows the following submitted signatures with the name send:

```
send()
send(address)
send(address[])
send(address,address,address[],uint256[])
send(address,address[],address,uint256[])
send(address,address,uint256)
send(address,address[],uint256[])
send(address,bytes)
send(address,string)
send(address,uint256)
send(address[],uint256[])
send(address,uint256,bytes)
send(address[],uint256[],uint256)
send(address,uint256,uint256,uint256)
```

There might be other databases out there that could provide details of the prevalence of these methods, but it's far from just a problem with ERC-777 and blocking keywords from being method names is going to be a general compatibility issue.
@mcdee Yeah I agree it might be a problem with compatibility. Maybe as you said we can remove it from the reserve as the `self.` could be sufficient. I'm still not sure about that but personally both solutions sound to me better than the first option.
Removing `send` from ERC777 is the best option.

But whitelisting `send` as a function name is our best  realistic option - and probably the on we will go with. There are no other keywords we can use for the `send` functionality, and programmers will naturally want to create methods named `send`.

Resolving patch:
diff --git a/vyper/signatures/function_signature.py b/vyper/signatures/function_signature.py
--- a/vyper/signatures/function_signature.py
+++ b/vyper/signatures/function_signature.py
@@ -19,6 +19,7 @@
 from vyper.utils import (
     fourbytes_to_int,
     is_varname_valid,
+    function_whitelist,
     sha3,
 )
 from vyper.parser.lll_node import LLLnode
@@ -76,7 +77,7 @@ def from_definition(cls, code, sigs=None, custom_units=None, custom_structs=None
         name = code.name
         pos = 0
 
-        if not is_varname_valid(name, custom_units=custom_units, custom_structs=custom_structs):
+        if (not name.lower() in function_whitelist) and (not is_varname_valid(name, custom_units=custom_units, custom_structs=custom_structs)):
             raise FunctionDeclarationException("Function name invalid: " + name)
         # Determine the arguments, expects something of the form def foo(arg1: int128, arg2: int128 ...
         args = []
diff --git a/vyper/utils.py b/vyper/utils.py
--- a/vyper/utils.py
+++ b/vyper/utils.py
@@ -154,6 +154,11 @@ def in_bounds(cls, type_str, value):
     'zero_address', 'empty_bytes32' 'max_int128', 'min_int128', 'max_decimal', 'min_decimal', 'max_uint256',  # constants
 ]
 
+# Otherwise reserved words that are whitelisted for function declarations
+function_whitelist = {
+    'send'
+}
+
 # List of valid LLL macros.
 valid_lll_macros = [
     'assert', 'break', 'ceil32', 'clamp', 'clamp', 'clamp_nonzero', 'clampge',


Test patch:
diff --git a/tests/parser/syntax/test_send.py b/tests/parser/syntax/test_send.py
--- a/tests/parser/syntax/test_send.py
+++ b/tests/parser/syntax/test_send.py
@@ -82,6 +82,17 @@ def foo():
 @public
 def foo():
     send(0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe, 5)
+    """,
+    """
+# Test custom send method
+@public
+def send(a: address, w: wei_value):
+    send(0xde0B295669a9FD93d5F28D9Ec85E40f4cb697BAe, 1)
+
+@public
+@payable
+def foo():
+    self.send(msg.sender, msg.value)
     """
 ]