Skip to content

Commit

Permalink
Merge branch 'master' into feat/improve-event-kwargs-recommendation
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-cooper authored Jan 9, 2025
2 parents def7bcb + 9db1546 commit 5eae19a
Show file tree
Hide file tree
Showing 19 changed files with 398 additions and 87 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ jobs:
# lang: language changes
# stdlib: changes to the stdlib
# ux: language changes (UX)
# parser: parser changes
# tool: integration
# ir: (old) IR/codegen changes
# codegen: lowering from vyper AST to codegen
Expand All @@ -46,6 +47,7 @@ jobs:
lang
stdlib
ux
parser
tool
ir
codegen
Expand Down
23 changes: 20 additions & 3 deletions docs/compiling-a-contract.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Online Compilers
Try VyperLang!
-----------------

`Try VyperLang! <https://try.vyperlang.org>`_ is a JupterHub instance hosted by the Vyper team as a sandbox for developing and testing contracts in Vyper. It requires github for login, and supports deployment via the browser.
`Try VyperLang! <https://try.vyperlang.org>`_ is a JupyterHub instance hosted by the Vyper team as a sandbox for developing and testing contracts in Vyper. It requires github for login, and supports deployment via the browser.

Remix IDE
---------
Expand Down Expand Up @@ -203,7 +203,7 @@ The following is a list of supported EVM versions, and changes in the compiler i
Integrity Hash
==============

To help tooling detect whether two builds are the same, Vyper provides the ``-f integrity`` output, which outputs the integrity hash of a contract. The integrity hash is recursively defined as the sha256 of the source code with the integrity hashes of its dependencies (imports).
To help tooling detect whether two builds are the same, Vyper provides the ``-f integrity`` output, which outputs the integrity hash of a contract. The integrity hash is recursively defined as the sha256 of the source code with the integrity hashes of its dependencies (imports) and storage layout overrides (if provided).

.. _vyper-archives:

Expand All @@ -219,15 +219,17 @@ A Vyper archive is a compileable bundle of input sources and settings. Technical
├── compilation_targets
├── compiler_version
├── integrity
├── settings.json
├── searchpaths
└── settings.json
└── storage_layout.json [OPTIONAL]

* ``cli_settings.txt`` is a text representation of the settings that were used on the compilation run that generated this archive.
* ``compilation_targets`` is a newline separated list of compilation targets. Currently only one compilation is supported
* ``compiler_version`` is a text representation of the compiler version used to generate this archive
* ``integrity`` is the :ref:`integrity hash <integrity-hash>` of the input contract
* ``searchpaths`` is a newline-separated list of the search paths used on this compilation run
* ``settings.json`` is a json representation of the settings used on this compilation run. It is 1:1 with ``cli_settings.txt``, but both are provided as they are convenient for different workflows (typically, manually vs automated).
* ``storage_layout.json`` is a json representation of the storage layout overrides to be used on this compilation run. It is optional.

A Vyper archive file can be produced by requesting the ``-f archive`` output format. The compiler can also produce the archive in base64 encoded form using the ``--base64`` flag. The Vyper compiler can accept both ``.vyz`` and base64-encoded Vyper archives directly as input.

Expand Down Expand Up @@ -281,6 +283,14 @@ The following example describes the expected input format of ``vyper-json``. (Co
}
},
// Optional
// Storage layout overrides for the contracts that are compiled
"storage_layout_overrides": {
"contracts/foo.vy": {
"a": {"type": "uint256", "slot": 1, "n_slots": 1},
"b": {"type": "uint256", "slot": 0, "n_slots": 1},
}
},
// Optional
"settings": {
"evmVersion": "cancun", // EVM version to compile for. Can be london, paris, shanghai or cancun (default).
// optional, optimization mode
Expand Down Expand Up @@ -364,6 +374,13 @@ The following example describes the output format of ``vyper-json``. Comments ar
"formattedMessage": "line 5:11 Unsupported type conversion: int128 to bool"
}
],
// Optional: not present if there are no storage layout overrides
"storage_layout_overrides": {
"contracts/foo.vy": {
"a": {"type": "uint256", "slot": 1, "n_slots": 1},
"b": {"type": "uint256", "slot": 0, "n_slots": 1},
}
},
// This contains the file-level outputs. Can be limited/filtered by the outputSelection settings.
"sources": {
"source_file.vy": {
Expand Down
15 changes: 15 additions & 0 deletions tests/functional/codegen/types/test_bytes.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest

from vyper.compiler import compile_code
from vyper.exceptions import TypeMismatch


Expand Down Expand Up @@ -281,6 +282,20 @@ def test2(l: Bytes[{m}] = x"{val}") -> bool:
assert c.test2(vyper_literal) is True


def test_hex_literal_parser_edge_case():
# see GH issue 4405 example 2
code = """
interface FooBar:
def test(a: Bytes[2], b: String[4]): payable
@deploy
def __init__(ext: FooBar):
extcall ext.test(x'6161', x'6161') #ext.test(b'\x61\61', '6161') gets called
"""
with pytest.raises(TypeMismatch):
compile_code(code)


def test_zero_padding_with_private(get_contract):
code = """
counter: uint256
Expand Down
30 changes: 28 additions & 2 deletions tests/functional/syntax/exceptions/test_syntax_exception.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest

from vyper.compiler import compile_code
from vyper.exceptions import SyntaxException

fail_list = [
Expand Down Expand Up @@ -107,5 +108,30 @@ def foo():


@pytest.mark.parametrize("bad_code", fail_list)
def test_syntax_exception(assert_compile_failed, get_contract, bad_code):
assert_compile_failed(lambda: get_contract(bad_code), SyntaxException)
def test_syntax_exception(bad_code):
with pytest.raises(SyntaxException):
compile_code(bad_code)


def test_bad_staticcall_keyword():
bad_code = """
from ethereum.ercs import IERC20Detailed
def foo():
staticcall ERC20(msg.sender).transfer(msg.sender, staticall IERC20Detailed(msg.sender).decimals())
""" # noqa
with pytest.raises(SyntaxException) as e:
compile_code(bad_code)

expected_error = """
invalid syntax. Perhaps you forgot a comma? (<unknown>, line 5)
contract "<unknown>:5", line 5:54
4 def foo():
---> 5 staticcall ERC20(msg.sender).transfer(msg.sender, staticall IERC20Detailed(msg.sender).decimals())
-------------------------------------------------------------^
6
(hint: did you mean `staticcall`?)
""" # noqa
assert str(e.value) == expected_error.strip()
30 changes: 29 additions & 1 deletion tests/functional/syntax/test_bytes.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,17 @@ def test() -> Bytes[1]:
"""
@external
def test() -> Bytes[2]:
a: Bytes[2] = x"abc"
a: Bytes[2] = x"abc" # non-hex nibbles
return a
""",
SyntaxException,
),
(
"""
@external
def test() -> Bytes[10]:
# GH issue 4405 example 1
a: Bytes[10] = x x x x x x"61" # messed up hex prefix
return a
""",
SyntaxException,
Expand All @@ -107,6 +117,24 @@ def test_bytes_fail(bad_code):
compiler.compile_code(bad_code)


@pytest.mark.xfail
def test_hexbytes_offset():
good_code = """
event X:
a: Bytes[2]
@deploy
def __init__():
# GH issue 4405, example 1
#
# log changes offset of HexString, and the hex_string_locations tracked
# location is incorrect when visiting ast
log X(a = x"6161")
"""
# move this to valid list once it passes.
assert compiler.compile_code(good_code) is not None


valid_list = [
"""
@external
Expand Down
9 changes: 6 additions & 3 deletions tests/functional/syntax/test_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ def foo():
)

file_input = input_bundle.load_file("top.vy")
with pytest.raises(ModuleNotFound):
with pytest.raises(ModuleNotFound) as e:
compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert "lib0.vy:" in str(e.value)


def test_implicitly_relative_import_crashes_2(make_input_bundle):
Expand All @@ -44,8 +45,9 @@ def foo():
)

file_input = input_bundle.load_file("top.vy")
with pytest.raises(ModuleNotFound):
with pytest.raises(ModuleNotFound) as e:
compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert "lib0.vy:" in str(e.value)


def test_relative_import_searches_only_current_path(make_input_bundle):
Expand All @@ -70,8 +72,9 @@ def foo():
input_bundle = make_input_bundle({"top.vy": top, "a.vy": a, "subdir/b.vy": b})
file_input = input_bundle.load_file("top.vy")

with pytest.raises(ModuleNotFound):
with pytest.raises(ModuleNotFound) as e:
compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert "b.vy:" in str(e.value)


def test_absolute_import_within_relative_import(make_input_bundle):
Expand Down
15 changes: 10 additions & 5 deletions tests/functional/syntax/test_interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,26 +411,31 @@ def foobar():
assert compiler.compile_code(code, input_bundle=input_bundle) is not None


def test_builtins_not_found():
def test_builtins_not_found(make_input_bundle):
code = """
from vyper.interfaces import foobar
"""
input_bundle = make_input_bundle({"code.vy": code})
file_input = input_bundle.load_file("code.vy")
with pytest.raises(ModuleNotFound) as e:
compiler.compile_code(code)

compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert e.value._message == "vyper.interfaces.foobar"
assert e.value._hint == "try renaming `vyper.interfaces` to `ethereum.ercs`"
assert "code.vy:" in str(e.value)


@pytest.mark.parametrize("erc", ("ERC20", "ERC721", "ERC4626"))
def test_builtins_not_found2(erc):
def test_builtins_not_found2(erc, make_input_bundle):
code = f"""
from ethereum.ercs import {erc}
"""
input_bundle = make_input_bundle({"code.vy": code})
file_input = input_bundle.load_file("code.vy")
with pytest.raises(ModuleNotFound) as e:
compiler.compile_code(code)
compiler.compile_from_file_input(file_input, input_bundle=input_bundle)
assert e.value._message == f"ethereum.ercs.{erc}"
assert e.value._hint == f"try renaming `{erc}` to `I{erc}`"
assert "code.vy:" in str(e.value)


def test_interface_body_check(make_input_bundle):
Expand Down
28 changes: 27 additions & 1 deletion tests/unit/cli/storage_layout/test_storage_layout_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest

from vyper.cli.vyper_json import compile_json
from vyper.compiler import compile_code
from vyper.evm.opcodes import version_check
from vyper.exceptions import StorageLayoutException
Expand All @@ -13,7 +14,7 @@ def test_storage_layout_overrides():
b: uint256"""

storage_layout_overrides = {
"a": {"type": "uint256", "slot": 1, "n_slots": 1},
"a": {"type": "uint256", "slot": 5, "n_slots": 1},
"b": {"type": "uint256", "slot": 0, "n_slots": 1},
}

Expand All @@ -26,6 +27,31 @@ def test_storage_layout_overrides():
assert out["layout"] == expected_output


def test_storage_layout_overrides_json():
code = """
a: uint256
b: uint256"""

storage_layout_overrides = {
"a": {"type": "uint256", "slot": 5, "n_slots": 1},
"b": {"type": "uint256", "slot": 0, "n_slots": 1},
}

input_json = {
"language": "Vyper",
"sources": {"contracts/foo.vy": {"content": code}},
"storage_layout_overrides": {"contracts/foo.vy": storage_layout_overrides},
"settings": {"outputSelection": {"*": ["*"]}},
}

out = compile_code(
code, output_formats=["layout"], storage_layout_override=storage_layout_overrides
)
assert (
compile_json(input_json)["contracts"]["contracts/foo.vy"]["foo"]["layout"] == out["layout"]
)


def test_storage_layout_for_more_complex():
code = """
foo: HashMap[address, uint256]
Expand Down
Loading

0 comments on commit 5eae19a

Please sign in to comment.