Skip to content

Commit

Permalink
fix: raw_call type when max_outsize=0 is set (#3572)
Browse files Browse the repository at this point in the history
prior to this commit, when `raw_call` is used with `max_outsize`
explicitly set to 0 (`max_outsize=0`) the compiler incorrectly infers that
raw_call has no return type

```vyper
@external
@payable
def foo(_target: address):
    # compiles
    a: bool = raw_call(_target, method_id("foo()"), revert_on_failure=False)
    # should have same behavior, but prior to this commit does not compile:
    b: bool = raw_call(_target, method_id("foo()"), max_outsize=0, revert_on_failure=False)
```

chainsec june 2023 review 5.16

---------

Co-authored-by: tserg <[email protected]>
  • Loading branch information
charles-cooper and tserg authored Sep 1, 2023
1 parent a19cdea commit 572b38c
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 1 deletion.
63 changes: 63 additions & 0 deletions tests/parser/functions/test_raw_call.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
from hexbytes import HexBytes

from vyper import compile_code
from vyper.builtins.functions import eip1167_bytecode
from vyper.exceptions import ArgumentException, InvalidType, StateAccessViolation

Expand Down Expand Up @@ -260,6 +261,68 @@ def __default__():
w3.eth.send_transaction({"to": caller.address, "data": sig})


# check max_outsize=0 does same thing as not setting max_outsize.
# compile to bytecode and compare bytecode directly.
def test_max_outsize_0():
code1 = """
@external
def test_raw_call(_target: address):
raw_call(_target, method_id("foo()"))
"""
code2 = """
@external
def test_raw_call(_target: address):
raw_call(_target, method_id("foo()"), max_outsize=0)
"""
output1 = compile_code(code1, ["bytecode", "bytecode_runtime"])
output2 = compile_code(code2, ["bytecode", "bytecode_runtime"])
assert output1 == output2


# check max_outsize=0 does same thing as not setting max_outsize,
# this time with revert_on_failure set to False
def test_max_outsize_0_no_revert_on_failure():
code1 = """
@external
def test_raw_call(_target: address) -> bool:
# compile raw_call both ways, with revert_on_failure
a: bool = raw_call(_target, method_id("foo()"), revert_on_failure=False)
return a
"""
# same code, but with max_outsize=0
code2 = """
@external
def test_raw_call(_target: address) -> bool:
a: bool = raw_call(_target, method_id("foo()"), max_outsize=0, revert_on_failure=False)
return a
"""
output1 = compile_code(code1, ["bytecode", "bytecode_runtime"])
output2 = compile_code(code2, ["bytecode", "bytecode_runtime"])
assert output1 == output2


# test functionality of max_outsize=0
def test_max_outsize_0_call(get_contract):
target_source = """
@external
@payable
def bar() -> uint256:
return 123
"""

caller_source = """
@external
@payable
def foo(_addr: address) -> bool:
success: bool = raw_call(_addr, method_id("bar()"), max_outsize=0, revert_on_failure=False)
return success
"""

target = get_contract(target_source)
caller = get_contract(caller_source)
assert caller.foo(target.address) is True


def test_static_call_fails_nonpayable(get_contract, assert_tx_failed):
target_source = """
baz: int128
Expand Down
2 changes: 1 addition & 1 deletion vyper/builtins/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ def fetch_call_return(self, node):
revert_on_failure = kwargz.get("revert_on_failure")
revert_on_failure = revert_on_failure.value if revert_on_failure is not None else True

if outsize is None:
if outsize is None or outsize.value == 0:
if revert_on_failure:
return None
return BoolT()
Expand Down

0 comments on commit 572b38c

Please sign in to comment.