Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/vyperlang/vyper into tool…
Browse files Browse the repository at this point in the history
…/storage_layout_json
  • Loading branch information
tserg committed Dec 31, 2024
2 parents 7265a43 + a29b49d commit a6786bc
Show file tree
Hide file tree
Showing 47 changed files with 2,876 additions and 324 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
# docs: documentation
# test: test suite
# lang: language changes
# stdlib: changes to the stdlib
# ux: language changes (UX)
# tool: integration
# ir: (old) IR/codegen changes
Expand All @@ -43,6 +44,7 @@ jobs:
docs
test
lang
stdlib
ux
tool
ir
Expand Down
24 changes: 22 additions & 2 deletions docs/control-structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,16 @@ External functions (marked with the ``@external`` decorator) are a part of the c
A Vyper contract cannot call directly between two external functions. If you must do this, you can use an :ref:`interface <interfaces>`.

.. note::
For external functions with default arguments like ``def my_function(x: uint256, b: uint256 = 1)`` the Vyper compiler will generate ``N+1`` overloaded function selectors based on ``N`` default arguments.
For external functions with default arguments like ``def my_function(x: uint256, b: uint256 = 1)`` the Vyper compiler will generate ``N+1`` overloaded function selectors based on ``N`` default arguments. Consequently, the ABI signature for a function (this includes interface functions) excludes optional arguments when their default values are used in the function call.

.. code-block:: vyper
from ethereum.ercs import IERC4626
@external
def foo(x: IERC4626):
extcall x.withdraw(0, self, self) # keccak256("withdraw(uint256,address,address)")[:4] = 0xb460af94
extcall x.withdraw(0) # keccak256("withdraw(uint256)")[:4] = 0x2e1a7d4d
.. _structure-functions-internal:

Expand All @@ -75,6 +84,14 @@ Or for internal functions which are defined in :ref:`imported modules <modules>`
def calculate(amount: uint256) -> uint256:
return calculator_library._times_two(amount)
Marking an internal function as ``payable`` specifies that the function can interact with ``msg.value``. A ``nonpayable`` internal function can be called from an external ``payable`` function, but it cannot access ``msg.value``.

.. code-block:: vyper
@payable
def _foo() -> uint256:
return msg.value % 2
.. note::
As of v0.4.0, the ``@internal`` decorator is optional. That is, functions with no visibility decorator default to being ``internal``.

Expand Down Expand Up @@ -110,7 +127,7 @@ You can optionally declare a function's mutability by using a :ref:`decorator <f
* ``@pure``: does not read from the contract state or any environment variables.
* ``@view``: may read from the contract state, but does not alter it.
* ``@nonpayable`` (default): may read from and write to the contract state, but cannot receive Ether.
* ``@payable``: may read from and write to the contract state, and can receive Ether.
* ``@payable``: may read from and write to the contract state, and can receive and access Ether via ``msg.value``.

.. code-block:: vyper
Expand All @@ -132,6 +149,9 @@ Functions marked with ``@view`` cannot call mutable (``payable`` or ``nonpayable

Functions marked with ``@pure`` cannot call non-``pure`` functions.

.. note::
The ``@nonpayable`` decorator is not strictly enforced on ``internal`` functions when they are invoked through an ``external`` ``payable`` function. As a result, an ``external`` ``payable`` function can invoke an ``internal`` ``nonpayable`` function. However, the ``nonpayable`` ``internal`` function cannot have access to ``msg.value``.

Re-entrancy Locks
-----------------

Expand Down
4 changes: 4 additions & 0 deletions docs/interfaces.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ This imports the defined interface from the vyper file at ``an_interface.vyi`` (

Prior to v0.4.0, ``implements`` required that events defined in an interface were re-defined in the "implementing" contract. As of v0.4.0, this is no longer required because events can be used just by importing them. Any events used in a contract will automatically be exported in the ABI output.

.. note::

An interface function with default parameters (e.g. ``deposit(assets: uint256, receiver: address = msg.sender)``) implies that the contract being interfaced with supports these default arguments via the ABI-encoded function signatures (e.g. ``keccak256("deposit(uint256,address)")[:4]`` and ``keccak256("deposit(uint256)")[:4]``). It is the responsibility of the callee to implement the behavior associated with these defaults.

Standalone Interfaces
=====================

Expand Down
105 changes: 105 additions & 0 deletions tests/functional/codegen/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,3 +876,108 @@ def bar() -> uint256:
input_bundle = make_input_bundle({"lib1.vy": lib1})
c = get_contract(main, input_bundle=input_bundle)
assert c.bar() == 1


def test_interface_with_flags():
code = """
struct MyStruct:
a: address
flag Foo:
BOO
MOO
POO
event Transfer:
sender: indexed(address)
@external
def bar():
pass
flag BAR:
BIZ
BAZ
BOO
@external
@view
def foo(s: MyStruct) -> MyStruct:
return s
"""

out = compile_code(code, contract_path="code.vy", output_formats=["interface"])["interface"]

assert "# Flags" in out
assert "flag Foo:" in out
assert "flag BAR" in out
assert "BOO" in out
assert "MOO" in out

compile_code(out, contract_path="code.vyi", output_formats=["interface"])


vyi_filenames = [
"test__test.vyi",
"test__t.vyi",
"t__test.vyi",
"t__t.vyi",
"t_t.vyi",
"test_test.vyi",
"t_test.vyi",
"test_t.vyi",
"_test_t__t_tt_.vyi",
"foo_bar_baz.vyi",
]


@pytest.mark.parametrize("vyi_filename", vyi_filenames)
def test_external_interface_names(vyi_filename):
code = """
@external
def foo():
...
"""

compile_code(code, contract_path=vyi_filename, output_formats=["external_interface"])


def test_external_interface_with_flag():
code = """
flag Foo:
Blah
@external
def foo() -> Foo:
...
"""

out = compile_code(code, contract_path="test__test.vyi", output_formats=["external_interface"])[
"external_interface"
]
assert "-> Foo:" in out


def test_external_interface_compiles_again():
code = """
@external
def foo() -> uint256:
...
@external
def bar(a:int32) -> uint256:
...
"""

out = compile_code(code, contract_path="test.vyi", output_formats=["external_interface"])[
"external_interface"
]
compile_code(out, contract_path="test.vyi", output_formats=["external_interface"])


@pytest.mark.xfail
def test_weird_interface_name():
# based on comment https://github.com/vyperlang/vyper/pull/4290#discussion_r1884137428
# we replace "_" for "" which results in an interface without name
out = compile_code("", contract_path="_.vyi", output_formats=["external_interface"])[
"external_interface"
]
assert "interface _:" in out
Loading

0 comments on commit a6786bc

Please sign in to comment.