Skip to content

Commit

Permalink
refactor: builtins (#2907)
Browse files Browse the repository at this point in the history
- refactor print and _abi_encode builtins to use `_has_varargs`
- rename _SimpleBuiltinFunction to BuiltinFunction
- rename validate_inputs to process_inputs
- move abi encoder and decoder tests
  • Loading branch information
charles-cooper authored Jun 18, 2022
1 parent 2f5c585 commit 470c294
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 192 deletions.
4 changes: 2 additions & 2 deletions docs/built-in-functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,11 @@ Vyper has three builtins for contract creation; all three contract creation buil
def foo(_target: address) -> address:
arg1: uint256 = 18
arg2: String = "some string"
return create_with_code_of(_target, arg1, arg2)
return create_from_factory(_target, arg1, arg2)
.. note::

To properly deploy a factory contract, special deploy bytecode must be used. Deploying factory contracts is generally out of scope of this article, but the following preamble, prepended to regular deploy bytecode (output of ``vyper -f bytecode``), should deploy the factory contract in an ordinary contract creation transaction: ``deploy_preamble = "61" + <bytecode len in 4 hex character> + "3d81600a3d39f3"``. To see an example of this, please see the `setup code for testing create_with_code_of <https://github.com/vyperlang/vyper/blob/master/tests/parser/functions/test_create_functions.py>`_.
To properly deploy a factory contract, special deploy bytecode must be used. Deploying factory contracts is generally out of scope of this article, but the following preamble, prepended to regular deploy bytecode (output of ``vyper -f bytecode``), should deploy the factory contract in an ordinary contract creation transaction: ``deploy_preamble = "61" + <bytecode len in 4 hex characters> + "3d81600a3d39f3"``. To see an example of this, please see `the setup code for testing create_from_factory <https://github.com/vyperlang/vyper/blob/2adc34ffd3bee8b6dee90f552bbd9bb844509e19/tests/base_conftest.py#L130-L160>`_.

.. py:function:: raw_call(to: address, data: Bytes, max_outsize: int = 0, gas: uint256 = gasLeft, value: uint256 = 0, is_delegate_call: bool = False, is_static_call: bool = False, revert_on_failure: bool = True) -> Bytes[max_outsize]
Expand Down
File renamed without changes.
File renamed without changes.
56 changes: 37 additions & 19 deletions tests/parser/functions/test_create_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,36 +201,49 @@ def test_create_from_factory_args(
get_contract, deploy_factory_for, w3, keccak, create2_address_of, assert_tx_failed
):
code = """
struct Bar:
x: String[32]
FOO: immutable(String[128])
BAR: immutable(Bar)
@external
def __init__(arg: String[128]):
FOO = arg
def __init__(foo: String[128], bar: Bar):
FOO = foo
BAR = bar
@external
def foo() -> String[128]:
return FOO
@external
def bar() -> Bar:
return BAR
"""

deployer_code = """
struct Bar:
x: String[32]
created_address: public(address)
@external
def test(target: address, arg: String[128]):
self.created_address = create_from_factory(target, arg)
def test(target: address, arg1: String[128], arg2: Bar):
self.created_address = create_from_factory(target, arg1, arg2)
@external
def test2(target: address, arg: String[128], salt: bytes32):
self.created_address = create_from_factory(target, arg, salt=salt)
def test2(target: address, arg1: String[128], arg2: Bar, salt: bytes32):
self.created_address = create_from_factory(target, arg1, arg2, salt=salt)
@external
def should_fail(target: address, arg: String[129]):
self.created_address = create_from_factory(target, arg)
def should_fail(target: address, arg1: String[129], arg2: Bar):
self.created_address = create_from_factory(target, arg1, arg2)
"""
FOO = "hello!"
BAR = ("world!",)

# deploy a foo so we can compare its bytecode with factory deployed version
foo_contract = get_contract(code, FOO)
foo_contract = get_contract(code, FOO, BAR)
expected_runtime_code = w3.eth.get_code(foo_contract.address)

f, FooContract = deploy_factory_for(code)
Expand All @@ -239,37 +252,42 @@ def should_fail(target: address, arg: String[129]):

initcode = w3.eth.get_code(f.address)

d.test(f.address, FOO, transact={})
d.test(f.address, FOO, BAR, transact={})

test = FooContract(d.created_address())
assert w3.eth.get_code(test.address) == expected_runtime_code
assert test.foo() == FOO
assert test.bar() == BAR

# extcodesize check
assert_tx_failed(lambda: d.test("0x" + "00" * 20, FOO))
assert_tx_failed(lambda: d.test("0x" + "00" * 20, FOO, BAR))

# now same thing but with create2
salt = keccak(b"vyper")
d.test2(f.address, FOO, salt, transact={})
d.test2(f.address, FOO, BAR, salt, transact={})

test = FooContract(d.created_address())
assert w3.eth.get_code(test.address) == expected_runtime_code
assert test.foo() == FOO
assert test.bar() == BAR

encoded_foo = encode_single("(string)", (FOO,))
assert HexBytes(test.address) == create2_address_of(d.address, salt, initcode + encoded_foo)
encoded_args = encode_single("(string,(string))", (FOO, BAR))
assert HexBytes(test.address) == create2_address_of(d.address, salt, initcode + encoded_args)

# can't collide addresses
assert_tx_failed(lambda: d.test2(f.address, FOO, salt))
assert_tx_failed(lambda: d.test2(f.address, FOO, BAR, salt))

# but creating a contract with different args is ok
BAR = "bar"
d.test2(f.address, BAR, salt, transact={})
FOO = "bar"
d.test2(f.address, FOO, BAR, salt, transact={})
# just for kicks
assert FooContract(d.created_address()).foo() == BAR
assert FooContract(d.created_address()).foo() == FOO
assert FooContract(d.created_address()).bar() == BAR

# Foo constructor should fail
assert_tx_failed(lambda: d.should_fail(f.address, b"\x01" * 129))
FOO = b"\x01" * 129
BAR = ("",)
assert_tx_failed(lambda: d.should_fail(f.address, FOO, BAR))


def test_create_copy_of(get_contract, w3, keccak, create2_address_of, assert_tx_failed):
Expand Down
6 changes: 3 additions & 3 deletions tests/parser/syntax/test_abi_encode.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from vyper import compiler
from vyper.exceptions import TypeMismatch
from vyper.exceptions import InvalidType, TypeMismatch

fail_list = [
(
Expand Down Expand Up @@ -36,15 +36,15 @@ def foo(x: uint256) -> Bytes[36]:
def foo(x: uint256) -> Bytes[36]:
return _abi_encode(x, method_id=b"abcde")
""",
TypeMismatch, # len(method_id) must be less than 4
InvalidType, # len(method_id) must be less than 4
),
(
"""
@external
def foo(x: uint256) -> Bytes[36]:
return _abi_encode(x, method_id=0x1234567890)
""",
TypeMismatch, # len(method_id) must be less than 4
InvalidType, # len(method_id) must be less than 4
),
]

Expand Down
2 changes: 1 addition & 1 deletion tests/parser/syntax/test_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def test() -> int128:
test_a: constant(uint256) = MAX_UINT256
""",
"""
TEST_C: constant(int128) = 1
TEST_C: constant(uint256) = 1
TEST_WEI: constant(uint256) = 1
@internal
Expand Down
50 changes: 50 additions & 0 deletions tests/parser/syntax/test_print.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import pytest

from vyper import compiler

valid_list = [
"""
@external
def foo(x: uint256):
print(x)
""",
"""
@external
def foo(x: Bytes[1]):
print(x)
""",
"""
struct Foo:
x: Bytes[128]
@external
def foo(foo: Foo):
print(foo)
""",
"""
struct Foo:
x: uint256
@external
def foo(foo: Foo):
print(foo)
""",
"""
BAR: constant(DynArray[uint256, 5]) = [1, 2, 3, 4, 5]
@external
def foo():
print(BAR)
""",
"""
FOO: constant(uint256) = 1
BAR: constant(DynArray[uint256, 5]) = [1, 2, 3, 4, 5]
@external
def foo():
print(FOO, BAR)
""",
]


@pytest.mark.parametrize("good_code", valid_list)
def test_print_syntax(good_code):
assert compiler.compile_code(good_code) is not None
Loading

0 comments on commit 470c294

Please sign in to comment.