-
-
Notifications
You must be signed in to change notification settings - Fork 905
feat: add method_id member to interface functions #4903
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
b5dcdff
4444ef6
532e685
9f51422
ea31b8d
c5759fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| import pytest | ||
|
|
||
| from vyper.utils import method_id | ||
|
|
||
|
|
||
| def test_interface_method_id_basic(get_contract): | ||
| code = """ | ||
| interface Foo: | ||
| def transfer(to: address, amount: uint256): nonpayable | ||
|
|
||
| @external | ||
| def get_method_id() -> bytes4: | ||
| return Foo.transfer.method_id | ||
| """ | ||
| c = get_contract(code) | ||
| result = c.get_method_id() | ||
| expected = method_id("transfer(address,uint256)") | ||
| assert result == expected | ||
|
|
||
|
|
||
| def test_interface_method_id_view_function(get_contract): | ||
| code = """ | ||
| interface Foo: | ||
| def balanceOf(owner: address) -> uint256: view | ||
|
|
||
| @external | ||
| def get_method_id() -> bytes4: | ||
| return Foo.balanceOf.method_id | ||
| """ | ||
| c = get_contract(code) | ||
| result = c.get_method_id() | ||
| expected = method_id("balanceOf(address)") | ||
| assert result == expected | ||
|
|
||
|
|
||
| def test_interface_method_id_no_args(get_contract): | ||
| code = """ | ||
| interface Foo: | ||
| def totalSupply() -> uint256: view | ||
|
|
||
| @external | ||
| def get_method_id() -> bytes4: | ||
| return Foo.totalSupply.method_id | ||
| """ | ||
| c = get_contract(code) | ||
| result = c.get_method_id() | ||
| expected = method_id("totalSupply()") | ||
| assert result == expected | ||
|
|
||
|
|
||
| def test_interface_method_id_in_raw_call(get_contract, env): | ||
| called_code = """ | ||
| @external | ||
| def double(x: uint256) -> uint256: | ||
| return x * 2 | ||
| """ | ||
| caller_code = """ | ||
| interface Doubler: | ||
| def double(x: uint256) -> uint256: view | ||
|
|
||
| @external | ||
| def call_double(target: address, x: uint256) -> uint256: | ||
| response: Bytes[32] = raw_call( | ||
| target, | ||
| concat(Doubler.double.method_id, convert(x, bytes32)), | ||
| max_outsize=32 | ||
| ) | ||
| return convert(convert(response, bytes32), uint256) | ||
| """ | ||
| callee = get_contract(called_code) | ||
| caller = get_contract(caller_code) | ||
| assert caller.call_double(callee.address, 5) == 10 | ||
|
|
||
|
|
||
| def test_interface_method_id_assign_to_variable(get_contract): | ||
| code = """ | ||
| interface Foo: | ||
| def transfer(to: address, amount: uint256): nonpayable | ||
|
|
||
| @external | ||
| def get_method_id() -> bytes4: | ||
| m: bytes4 = Foo.transfer.method_id | ||
| return m | ||
| """ | ||
| c = get_contract(code) | ||
| result = c.get_method_id() | ||
| expected = method_id("transfer(address,uint256)") | ||
| assert result == expected | ||
|
|
||
|
|
||
| def test_interface_method_id_compare(get_contract): | ||
| code = """ | ||
| interface Foo: | ||
| def transfer(to: address, amount: uint256): nonpayable | ||
|
|
||
| @external | ||
| def check() -> bool: | ||
| return Foo.transfer.method_id == method_id('transfer(address,uint256)', output_type=bytes4) | ||
| """ | ||
| c = get_contract(code) | ||
| assert c.check() is True | ||
|
|
||
|
|
||
| def test_interface_method_id_default_args(get_contract, make_input_bundle): | ||
| iface_code = """ | ||
| @external | ||
| def take(auction_id: uint256, max_take_amount: uint256 = ...) -> uint256: | ||
| ... | ||
| """ | ||
| input_bundle = make_input_bundle({"ifoo.vyi": iface_code}) | ||
|
|
||
| code = """ | ||
| import ifoo as IFoo | ||
|
|
||
| @external | ||
| def get_method_id() -> bytes4: | ||
| return IFoo.take.method_id | ||
| """ | ||
| c = get_contract(code, input_bundle=input_bundle) | ||
| result = c.get_method_id() | ||
| # should return the full signature selector (all args) | ||
| expected = method_id("take(uint256,uint256)") | ||
| assert result == expected | ||
|
|
||
|
|
||
| def test_interface_method_id_default_args_view(get_contract, make_input_bundle): | ||
| iface_code = """ | ||
| @view | ||
| @external | ||
| def get_amount(token: address, receiver: address = ...) -> uint256: | ||
| ... | ||
| """ | ||
| input_bundle = make_input_bundle({"ifoo.vyi": iface_code}) | ||
|
|
||
| code = """ | ||
| import ifoo as IFoo | ||
|
|
||
| @external | ||
| def get_method_id() -> bytes4: | ||
| return IFoo.get_amount.method_id | ||
| """ | ||
| c = get_contract(code, input_bundle=input_bundle) | ||
| result = c.get_method_id() | ||
| expected = method_id("get_amount(address,address)") | ||
| assert result == expected |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import pytest | ||
|
|
||
| from vyper.compiler import compile_code | ||
| from vyper.exceptions import StructureException | ||
|
|
||
|
|
||
| valid_list = [ | ||
| # basic method_id access | ||
| """ | ||
| interface Foo: | ||
| def transfer(to: address, amount: uint256): nonpayable | ||
|
|
||
| @external | ||
| def foo() -> bytes4: | ||
| return Foo.transfer.method_id | ||
| """, | ||
| # use in raw_call | ||
| """ | ||
| interface Foo: | ||
| def bar(x: uint256) -> uint256: view | ||
|
|
||
| @external | ||
| def foo(): | ||
| x: Bytes[32] = raw_call( | ||
| msg.sender, | ||
| concat(Foo.bar.method_id, convert(1, bytes32)), | ||
| max_outsize=32 | ||
| ) | ||
| """, | ||
| ] | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("code", valid_list) | ||
| def test_interface_method_id_pass(code): | ||
| assert compile_code(code) is not None | ||
|
|
||
|
|
||
| def test_interface_method_id_no_instance_access(): | ||
| code = """ | ||
| interface Foo: | ||
| def transfer(to: address, amount: uint256): nonpayable | ||
|
|
||
| @external | ||
| def foo(addr: address) -> bytes4: | ||
| return Foo(addr).transfer.method_id | ||
| """ | ||
| with pytest.raises(StructureException): | ||
| compile_code(code) | ||
|
|
||
|
|
||
| def test_interface_method_id_default_args(make_input_bundle): | ||
| iface_code = """ | ||
| @external | ||
| def take( | ||
| auction_id: uint256, | ||
| max_take_amount: uint256 = ..., | ||
| ) -> uint256: | ||
| ... | ||
| """ | ||
| input_bundle = make_input_bundle({"ifoo.vyi": iface_code}) | ||
|
|
||
| code = """ | ||
| import ifoo as IFoo | ||
|
|
||
| @external | ||
| def foo() -> bytes4: | ||
| return IFoo.take.method_id | ||
| """ | ||
| assert compile_code(code, input_bundle=input_bundle) is not None |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,7 +67,9 @@ def __init__( | |
| self.decl_node = decl_node | ||
|
|
||
| def get_type_member(self, attr, node): | ||
| # get an event, struct or flag from this interface | ||
| # get a function, event, struct or flag from this interface | ||
| if attr in self.functions: | ||
| return TYPE_T(self.functions[attr]) | ||
|
Comment on lines
+76
to
+77
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Exposing interface functions from Useful? React with 👍 / 👎. |
||
| return TYPE_T(self._helper.get_member(attr, node)) | ||
|
|
||
| @property | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Case 1 should be modified to Case 1a then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed here 532e685