diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 94eb5ec04c..baa8decacc 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -6,7 +6,7 @@
### Commit message
-Commit message for the final, squashed PR. (Optional, but reviewers will appreciate it! Please see [our commit message style guide](../../blob/master/docs/style-guide.rst#best-practices-1) for what we would ideally like to see in a commit message.)
+Commit message for the final, squashed PR. (Optional, but reviewers will appreciate it! Please see [our commit message style guide](../../master/docs/style-guide.rst#best-practices-1) for what we would ideally like to see in a commit message.)
### Description for the changelog
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7243a05408..c8d7f7d6c4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -100,7 +100,7 @@ jobs:
-X POST \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}"\
-H "Content-Type: application/octet-stream" \
- "https://uploads.github.com/repos/${{ github.repository }}/releases/${{ github.event.release.id }}/assets?name=${BIN_NAME}" \
+ "https://uploads.github.com/repos/${{ github.repository }}/releases/${{ github.event.release.id }}/assets?name=${BIN_NAME/+/%2B}" \
--data-binary "@${BIN_NAME}"
done
diff --git a/.github/workflows/era-tester.yml b/.github/workflows/era-tester.yml
index 187b5c03a2..3e0bb3e941 100644
--- a/.github/workflows/era-tester.yml
+++ b/.github/workflows/era-tester.yml
@@ -98,6 +98,7 @@ jobs:
- name: Run tester (fast)
# Run era tester with no LLVM optimizations
+ continue-on-error: true
if: ${{ github.ref != 'refs/heads/master' }}
run: |
cd era-compiler-tester
@@ -105,7 +106,12 @@ jobs:
- name: Run tester (slow)
# Run era tester across the LLVM optimization matrix
+ continue-on-error: true
if: ${{ github.ref == 'refs/heads/master' }}
run: |
cd era-compiler-tester
cargo run --release --bin compiler-tester -- --path=tests/vyper/ --mode="M*B* ${{ env.VYPER_VERSION }}"
+
+ - name: Mark as success
+ run: |
+ exit 0
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index fd78e2fff8..8d23368eb0 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -104,7 +104,7 @@ jobs:
run: pip install tox
- name: Run Tox
- run: TOXENV=py${{ matrix.python-version[1] }} tox -r -- --optimize ${{ matrix.opt-mode }} ${{ matrix.debug && '--enable-compiler-debug-mode' || '' }} --reruns 10 --reruns-delay 1 -r aR tests/
+ run: TOXENV=py${{ matrix.python-version[1] }} tox -r -- --optimize ${{ matrix.opt-mode }} ${{ matrix.debug && '--enable-compiler-debug-mode' || '' }} -r aR tests/
- name: Upload Coverage
uses: codecov/codecov-action@v1
@@ -148,12 +148,12 @@ jobs:
# fetch test durations
# NOTE: if the tests get poorly distributed, run this and commit the resulting `.test_durations` file to the `vyper-test-durations` repo.
- # `TOXENV=fuzzing tox -r -- --store-durations --reruns 10 --reruns-delay 1 -r aR tests/`
+ # `TOXENV=fuzzing tox -r -- --store-durations -r aR tests/`
- name: Fetch test-durations
run: curl --location "https://raw.githubusercontent.com/vyperlang/vyper-test-durations/5982755ee8459f771f2e8622427c36494646e1dd/test_durations" -o .test_durations
- name: Run Tox
- run: TOXENV=fuzzing tox -r -- --splits 60 --group ${{ matrix.group }} --splitting-algorithm least_duration --reruns 10 --reruns-delay 1 -r aR tests/
+ run: TOXENV=fuzzing tox -r -- --splits 60 --group ${{ matrix.group }} --splitting-algorithm least_duration -r aR tests/
- name: Upload Coverage
uses: codecov/codecov-action@v1
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4b416a4414..b943b5d31d 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,27 +1,33 @@
repos:
- repo: https://github.com/pycqa/isort
- rev: 5.9.3
+ rev: 5.13.2
hooks:
- id: isort
name: isort
- repo: https://github.com/psf/black
- rev: 21.9b0
+ rev: 23.12.0
hooks:
- id: black
name: black
- repo: https://github.com/PyCQA/flake8
- rev: 3.9.2
+ rev: 6.1.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v0.910
+ rev: v1.7.1
hooks:
- id: mypy
additional_dependencies:
- "types-setuptools"
+ args: # settings from tox.ini
+ - --install-types
+ - --non-interactive
+ - --follow-imports=silent
+ - --ignore-missing-imports
+ - --implicit-optional
default_language_version:
python: python3.10
diff --git a/FUNDING.yml b/FUNDING.yml
index 81e82160d0..efb9eb01b7 100644
--- a/FUNDING.yml
+++ b/FUNDING.yml
@@ -1 +1 @@
-custom: https://gitcoin.co/grants/200/vyper-smart-contract-language-2
+custom: https://etherscan.io/address/0x70CCBE10F980d80b7eBaab7D2E3A73e87D67B775
diff --git a/README.md b/README.md
index af987ffd4f..33c4557cc8 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+**Vyper compiler security audit competition starts 14th September with $150k worth of bounties.** [See the competition on CodeHawks](https://www.codehawks.com/contests/cll5rujmw0001js08menkj7hc) and find [more details in this blog post](https://mirror.xyz/0xBA41A04A14aeaEec79e2D694B21ba5Ab610982f1/WTZ3l3MLhTz9P4avq6JqipN5d4HJNiUY-d8zT0pfmXg).
@@ -53,6 +54,23 @@ make dev-init
python setup.py test
```
+## Developing (working on the compiler)
+
+A useful script to have in your PATH is something like the following:
+```bash
+$ cat ~/.local/bin/vyc
+#!/usr/bin/env bash
+PYTHONPATH=. python vyper/cli/vyper_compile.py "$@"
+```
+
+To run a python performance profile (to find compiler perf hotspots):
+```bash
+PYTHONPATH=. python -m cProfile -s tottime vyper/cli/vyper_compile.py "$@"
+```
+
+To get a call graph from a python profile, https://stackoverflow.com/a/23164271/ is helpful.
+
+
# Contributing
* See Issues tab, and feel free to submit your own issues
* Add PRs if you discover a solution to an existing issue
diff --git a/SECURITY.md b/SECURITY.md
index c7bdad4ee7..0a054b2c93 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -48,7 +48,7 @@ https://github.com/vyperlang/vyper/security/advisories
If you think you have found a security vulnerability with a project that has used Vyper,
please report the vulnerability to the relevant project's security disclosure program prior
-to reporting to us. If one is not available, please email your vulnerability to security@vyperlang.org.
+to reporting to us. If one is not available, submit it at https://github.com/vyperlang/vyper/security/advisories.
**Please Do Not Log An Issue** mentioning the vulnerability.
diff --git a/docs/built-in-functions.rst b/docs/built-in-functions.rst
index bfaa8fdd5e..45cf9ec8c2 100644
--- a/docs/built-in-functions.rst
+++ b/docs/built-in-functions.rst
@@ -184,13 +184,14 @@ Vyper has three built-ins for contract creation; all three contract creation bui
The implementation of ``create_copy_of`` assumes that the code at ``target`` is smaller than 16MB. While this is much larger than the EIP-170 constraint of 24KB, it is a conservative size limit intended to future-proof deployer contracts in case the EIP-170 constraint is lifted. If the code at ``target`` is larger than 16MB, the behavior of ``create_copy_of`` is undefined.
-.. py:function:: create_from_blueprint(target: address, *args, value: uint256 = 0, code_offset=0, [, salt: bytes32]) -> address
+.. py:function:: create_from_blueprint(target: address, *args, value: uint256 = 0, raw_args: bool = False, code_offset: int = 0, [, salt: bytes32]) -> address
Copy the code of ``target`` into memory and execute it as initcode. In other words, this operation interprets the code at ``target`` not as regular runtime code, but directly as initcode. The ``*args`` are interpreted as constructor arguments, and are ABI-encoded and included when executing the initcode.
* ``target``: Address of the blueprint to invoke
* ``*args``: Constructor arguments to forward to the initcode.
* ``value``: The wei value to send to the new contract address (Optional, default 0)
+ * ``raw_args``: If ``True``, ``*args`` must be a single ``Bytes[...]`` argument, which will be interpreted as a raw bytes buffer to forward to the create operation (which is useful for instance, if pre- ABI-encoded data is passed in from elsewhere). (Optional, default ``False``)
* ``code_offset``: The offset to start the ``EXTCODECOPY`` from (Optional, default 0)
* ``salt``: A ``bytes32`` value utilized by the deterministic ``CREATE2`` opcode (Optional, if not supplied, ``CREATE`` is used)
@@ -201,7 +202,7 @@ Vyper has three built-ins for contract creation; all three contract creation bui
@external
def foo(blueprint: address) -> address:
arg1: uint256 = 18
- arg2: String = "some string"
+ arg2: String[32] = "some string"
return create_from_blueprint(blueprint, arg1, arg2, code_offset=1)
.. note::
@@ -226,7 +227,7 @@ Vyper has three built-ins for contract creation; all three contract creation bui
* ``to``: Destination address to call to
* ``data``: Data to send to the destination address
* ``max_outsize``: Maximum length of the bytes array returned from the call. If the returned call data exceeds this length, only this number of bytes is returned. (Optional, default ``0``)
- * ``gas``: The amount of gas to attach to the call. If not set, all remaining gas is forwarded.
+ * ``gas``: The amount of gas to attach to the call. (Optional, defaults to ``msg.gas``).
* ``value``: The wei value to send to the address (Optional, default ``0``)
* ``is_delegate_call``: If ``True``, the call will be sent as ``DELEGATECALL`` (Optional, default ``False``)
* ``is_static_call``: If ``True``, the call will be sent as ``STATICCALL`` (Optional, default ``False``)
@@ -264,6 +265,10 @@ Vyper has three built-ins for contract creation; all three contract creation bui
assert success
return response
+ .. note::
+
+ Regarding "forwarding all gas", note that, while Vyper will provide ``msg.gas`` to the call, in practice, there are some subtleties around forwarding all remaining gas on the EVM which are out of scope of this documentation and could be subject to change. For instance, see the language in EIP-150 around "all but one 64th".
+
.. py:function:: raw_log(topics: bytes32[4], data: Union[Bytes, bytes32]) -> None
Provides low level access to the ``LOG`` opcodes, emitting a log without having to specify an ABI type.
@@ -500,7 +505,7 @@ Data Manipulation
* ``b``: ``Bytes`` list to extract from
* ``start``: Start point to extract from
- * ``output_type``: Type of output (``bytes32``, ``integer``, or ``address``). Defaults to ``bytes32``.
+ * ``output_type``: Type of output (``bytesM``, ``integer``, or ``address``). Defaults to ``bytes32``.
Returns a value of the type specified by ``output_type``.
diff --git a/docs/compiling-a-contract.rst b/docs/compiling-a-contract.rst
index 6d1cdf98d7..b529d1efb1 100644
--- a/docs/compiling-a-contract.rst
+++ b/docs/compiling-a-contract.rst
@@ -197,6 +197,7 @@ The following is a list of supported EVM versions, and changes in the compiler i
- The ``transient`` keyword allows declaration of variables which live in transient storage
- Functions marked with ``@nonreentrant`` are protected with TLOAD/TSTORE instead of SLOAD/SSTORE
+ - The ``MCOPY`` opcode will be generated automatically by the compiler for most memory operations.
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 6dc57b26c3..221600f930 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -75,4 +75,4 @@ If you are making a larger change, please consult first with the `Vyper (Smart C
Although we do CI testing, please make sure that the tests pass for supported Python version and ensure that it builds locally before submitting a pull request.
-Thank you for your help!
+Thank you for your help!
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index fc8a472ff6..2f890bcb2f 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -273,14 +273,25 @@ Ranges are created using the ``range`` function. The following examples are vali
.. code-block:: python
- for i in range(START, STOP):
+ for i in range(stop, bound=N):
...
-``START`` and ``STOP`` are literal integers, with ``STOP`` being a greater value than ``START``. ``i`` begins as ``START`` and increments by one until it is equal to ``STOP``.
+Here, ``stop`` can be a variable with integer type, greater than zero. ``N`` must be a compile-time constant. ``i`` begins as zero and increments by one until it is equal to ``stop``. If ``stop`` is larger than ``N``, execution will revert at runtime. In certain cases, you may not have a guarantee that ``stop`` is less than ``N``, but still want to avoid the possibility of runtime reversion. To accomplish this, use the ``bound=`` keyword in combination with ``min(stop, N)`` as the argument to ``range``, like ``range(min(stop, N), bound=N)``. This is helpful for use cases like chunking up operations on larger arrays across multiple transactions.
+
+Another use of range can be with ``START`` and ``STOP`` bounds.
.. code-block:: python
- for i in range(a, a + N):
+ for i in range(START, STOP):
...
-``a`` is a variable with an integer type and ``N`` is a literal integer greater than zero. ``i`` begins as ``a`` and increments by one until it is equal to ``a + N``.
+Here, ``START`` and ``STOP`` are literal integers, with ``STOP`` being a greater value than ``START``. ``i`` begins as ``START`` and increments by one until it is equal to ``STOP``.
+
+Finally, it is possible to use ``range`` with runtime `start` and `stop` values as long as a constant `bound` value is provided.
+In this case, Vyper checks at runtime that `end - start <= bound`.
+``N`` must be a compile-time constant.
+
+.. code-block:: python
+
+ for i in range(start, end, bound=N):
+ ...
diff --git a/docs/event-logging.rst b/docs/event-logging.rst
index c6e20954f8..904b179e70 100644
--- a/docs/event-logging.rst
+++ b/docs/event-logging.rst
@@ -66,11 +66,19 @@ Let's look at an event declaration in more detail.
receiver: indexed(address)
value: uint256
+The EVM currently has five opcodes for emitting event logs: ``LOG0``, ``LOG1``, ``LOG2``, ``LOG3``, and ``LOG4``.
+These opcodes can be used to create log records, where each log record consists of both **topics** and **data**.
+Topics are 32-byte ''words'' that are used to describe what is happening in an event.
+While topics are searchable, data is not.
+Event data is however not limited, which means that you can include large or complicated data like arrays or strings.
+Different opcodes (``LOG0`` through ``LOG4``) allow for different numbers of topics.
+For instance, ``LOG1`` includes one topic, ``LOG2`` includes two topics, and so on.
Event declarations look similar to struct declarations, containing one or more arguments that are passed to the event. Typical events will contain two kinds of arguments:
- * **Indexed** arguments, which can be searched for by listeners. Each indexed argument is identified by the ``indexed`` keyword. Here, each indexed argument is an address. You can have any number of indexed arguments, but indexed arguments are not passed directly to listeners, although some of this information (such as the sender) may be available in the listener's `results` object.
- * **Value** arguments, which are passed through to listeners. You can have any number of value arguments and they can have arbitrary names, but each is limited by the EVM to be no more than 32 bytes.
+ * **Indexed** arguments (topics), which can be searched for by listeners. Each indexed argument is identified by the ``indexed`` keyword. Here, each indexed argument is an address. You can have up to four indexed arguments (``LOG4``), but indexed arguments are not passed directly to listeners, although some of this information (such as the sender) may be available in the listener's `results` object.
+ * **Value** arguments (data), which are passed through to listeners. You can have any number of value arguments and they can have arbitrary names, but each is limited by the EVM to be no more than 32 bytes.
+Note that the first topic of a log record consists of the signature of the name of the event that occurred, including the types of its parameters.
It is also possible to create an event with no arguments. In this case, use the ``pass`` statement:
.. code-block:: python
diff --git a/docs/release-notes.rst b/docs/release-notes.rst
index da86c5c0ce..3db11dc451 100644
--- a/docs/release-notes.rst
+++ b/docs/release-notes.rst
@@ -14,17 +14,13 @@ Release Notes
for advisory links:
:'<,'>s/\v(https:\/\/github.com\/vyperlang\/vyper\/security\/advisories\/)([-A-Za-z0-9]+)/(`\2 <\1\2>`_)/g
-..
- v0.3.10 ("Black Adder")
- ***********************
-
-v0.3.10rc1
-**********
+v0.3.10 ("Black Adder")
+***********************
-Date released: 2023-09-06
+Date released: 2023-10-04
=========================
-v0.3.10 is a performance focused release. It adds a ``codesize`` optimization mode (`#3493 `_), adds new vyper-specific ``#pragma`` directives (`#3493 `_), uses Cancun's ``MCOPY`` opcode for some compiler generated code (`#3483 `_), and generates selector tables which now feature O(1) performance (`#3496 `_).
+v0.3.10 is a performance focused release that additionally ships numerous bugfixes. It adds a ``codesize`` optimization mode (`#3493 `_), adds new vyper-specific ``#pragma`` directives (`#3493 `_), uses Cancun's ``MCOPY`` opcode for some compiler generated code (`#3483 `_), and generates selector tables which now feature O(1) performance (`#3496 `_).
Breaking changes:
-----------------
@@ -32,6 +28,7 @@ Breaking changes:
- add runtime code layout to initcode (`#3584 `_)
- drop evm versions through istanbul (`#3470 `_)
- remove vyper signature from runtime (`#3471 `_)
+- only allow valid identifiers to be nonreentrant keys (`#3605 `_)
Non-breaking changes and improvements:
--------------------------------------
@@ -46,12 +43,15 @@ Notable fixes:
- fix ``ecrecover()`` behavior when signature is invalid (`GHSA-f5x6-7qgp-jhf3 `_, `#3586 `_)
- fix: order of evaluation for some builtins (`#3583 `_, `#3587 `_)
+- fix: memory allocation in certain builtins using ``msize`` (`#3610 `_)
+- fix: ``_abi_decode()`` input validation in certain complex expressions (`#3626 `_)
- fix: pycryptodome for arm builds (`#3485 `_)
- let params of internal functions be mutable (`#3473 `_)
- typechecking of folded builtins in (`#3490 `_)
- update tload/tstore opcodes per latest 1153 EIP spec (`#3484 `_)
- fix: raw_call type when max_outsize=0 is set (`#3572 `_)
- fix: implements check for indexed event arguments (`#3570 `_)
+- fix: type-checking for ``_abi_decode()`` arguments (`#3626 `_)
Other docs updates, chores and fixes:
-------------------------------------
diff --git a/docs/resources.rst b/docs/resources.rst
index 7f0d0600a9..a3dfa480ed 100644
--- a/docs/resources.rst
+++ b/docs/resources.rst
@@ -3,45 +3,47 @@
Other resources and learning material
#####################################
-Vyper has an active community. You can find third party tutorials,
-examples, courses and other learning material.
+Vyper has an active community. You can find third-party tutorials, examples, courses, and other learning material.
General
-------
-- `Ape Academy - Learn how to build vyper projects `__ by ApeWorX
-- `More Vyper by Example `__ by Smart Contract Engineer
-- `Vyper cheat Sheet `__
-- `Vyper Hub for development `__
-- `Vyper greatest hits smart contract examples `__
+- `Ape Academy – Learn how to build Vyper projects `_ by ApeWorX
+- `More Vyper by Example `_ by Smart Contract Engineer
+- `Vyper cheat Sheet `_
+- `Vyper Hub for development `_
+- `Vyper greatest hits smart contract examples `_
+- `A curated list of Vyper resources, libraries, tools, and more `_
Frameworks and tooling
----------------------
-- `ApeWorX - The Ethereum development framework for Python Developers, Data Scientists, and Security Professionals `__
-- `Foundry x Vyper - Foundry template to compile Vyper contracts `__
-- `Snekmate - Vyper smart contract building blocks `__
-- `Serpentor - A set of smart contracts tools for governance `__
-- `Smart contract development frameworks and tools for Vyper on Ethreum.org `__
+- `Titanoboa – An experimental Vyper interpreter with pretty tracebacks, forking, debugging features and more `_
+- `ApeWorX – The Ethereum development framework for Python Developers, Data Scientists, and Security Professionals `_
+- `VyperDeployer – A helper smart contract to compile and test Vyper contracts in Foundry `_
+- `🐍 snekmate – Vyper smart contract building blocks `_
+- `Serpentor – A set of smart contracts tools for governance `_
+- `Smart contract development frameworks and tools for Vyper on Ethreum.org `_
Security
--------
-- `VyperPunk - learn to secure and hack Vyper smart contracts `__
-- `VyperExamples - Vyper vulnerability examples `__
+- `VyperPunk – learn to secure and hack Vyper smart contracts `_
+- `VyperExamples – Vyper vulnerability examples `_
Conference presentations
------------------------
-- `Vyper Smart Contract Programming Language by Patrick Collins (2022, 30 mins) `__
-- `Python and DeFi by Curve Finance (2022, 15 mins) `__
-- `My experience with Vyper over the years by Benjamin Scherrey (2022, 15 mins) `__
-- `Short introduction to Vyper by Edison Que (3 mins) `__
+- `Vyper Smart Contract Programming Language by Patrick Collins (2022, 30 mins) `_
+- `Python and DeFi by Curve Finance (2022, 15 mins) `_
+- `My experience with Vyper over the years by Benjamin Scherrey (2022, 15 mins) `_
+- `Short introduction to Vyper by Edison Que (3 mins) `_
Unmaintained
------------
These resources have not been updated for a while, but may still offer interesting content.
-- `Awesome Vyper curated resources `__
-- `Brownie - Python framework for developing smart contracts (deprecated) `__
+- `Awesome Vyper curated resources `_
+- `Brownie – Python framework for developing smart contracts (deprecated) `_
+- `Foundry x Vyper – Foundry template to compile Vyper contracts `_
diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst
index d2c5d48d96..3861bf4380 100644
--- a/docs/structure-of-a-contract.rst
+++ b/docs/structure-of-a-contract.rst
@@ -17,7 +17,7 @@ Vyper supports several source code directives to control compiler modes and help
Version Pragma
--------------
-The version pragma ensures that a contract is only compiled by the intended compiler version, or range of versions. Version strings use `NPM `_ style syntax. Starting from v0.4.0 and up, version strings will use `PEP440 version specifiers _`.
+The version pragma ensures that a contract is only compiled by the intended compiler version, or range of versions. Version strings use `NPM `_ style syntax. Starting from v0.4.0 and up, version strings will use `PEP440 version specifiers `_.
As of 0.3.10, the recommended way to specify the version pragma is as follows:
@@ -25,6 +25,10 @@ As of 0.3.10, the recommended way to specify the version pragma is as follows:
#pragma version ^0.3.0
+.. note::
+
+ Both pragma directive versions ``#pragma`` and ``# pragma`` are supported.
+
The following declaration is equivalent, and, prior to 0.3.10, was the only supported method to specify the compiler version:
.. code-block:: python
diff --git a/docs/testing-contracts-ethtester.rst b/docs/testing-contracts-ethtester.rst
index 992cdc312a..1b7e9e3263 100644
--- a/docs/testing-contracts-ethtester.rst
+++ b/docs/testing-contracts-ethtester.rst
@@ -55,9 +55,9 @@ To test events and failed transactions we expand our simple storage contract to
Next, we take a look at the two fixtures that will allow us to read the event logs and to check for failed transactions.
-.. literalinclude:: ../tests/base_conftest.py
+.. literalinclude:: ../tests/conftest.py
:language: python
- :pyobject: assert_tx_failed
+ :pyobject: tx_failed
The fixture to assert failed transactions defaults to check for a ``TransactionFailed`` exception, but can be used to check for different exceptions too, as shown below. Also note that the chain gets reverted to the state before the failed transaction.
diff --git a/docs/types.rst b/docs/types.rst
index d669e6946d..a8be721b1a 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -299,7 +299,7 @@ Members
Member Type Description
=============== =========== ==========================================================================
``balance`` ``uint256`` Balance of an address
-``codehash`` ``bytes32`` Keccak of code at an address, ``EMPTY_BYTES32`` if no contract is deployed
+``codehash`` ``bytes32`` Keccak of code at an address, ``0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`` if no contract is deployed (see `EIP-1052 `_)
``codesize`` ``uint256`` Size of code deployed at an address, in bytes
``is_contract`` ``bool`` Boolean indicating if a contract is deployed at an address
``code`` ``Bytes`` Contract bytecode
@@ -376,22 +376,22 @@ On the ABI level the Fixed-size bytes array is annotated as ``string``.
example_str: String[100] = "Test String"
-Enums
+Flags
-----
-**Keyword:** ``enum``
+**Keyword:** ``flag``
-Enums are custom defined types. An enum must have at least one member, and can hold up to a maximum of 256 members.
+Flags are custom defined types. A flag must have at least one member, and can hold up to a maximum of 256 members.
The members are represented by ``uint256`` values in the form of 2\ :sup:`n` where ``n`` is the index of the member in the range ``0 <= n <= 255``.
.. code-block:: python
- # Defining an enum with two members
- enum Roles:
+ # Defining a flag with two members
+ flag Roles:
ADMIN
USER
- # Declaring an enum variable
+ # Declaring a flag variable
role: Roles = Roles.ADMIN
# Returning a member
@@ -426,13 +426,13 @@ Operator Description
``~x`` Bitwise not
============= ======================
-Enum members can be combined using the above bitwise operators. While enum members have values that are power of two, enum member combinations may not.
+Flag members can be combined using the above bitwise operators. While flag members have values that are power of two, flag member combinations may not.
-The ``in`` and ``not in`` operators can be used in conjunction with enum member combinations to check for membership.
+The ``in`` and ``not in`` operators can be used in conjunction with flag member combinations to check for membership.
.. code-block:: python
- enum Roles:
+ flag Roles:
MANAGER
ADMIN
USER
@@ -447,7 +447,7 @@ The ``in`` and ``not in`` operators can be used in conjunction with enum member
def bar(a: Roles) -> bool:
return a not in (Roles.MANAGER | Roles.USER)
-Note that ``in`` is not the same as strict equality (``==``). ``in`` checks that *any* of the flags on two enum objects are simultaneously set, while ``==`` checks that two enum objects are bit-for-bit equal.
+Note that ``in`` is not the same as strict equality (``==``). ``in`` checks that *any* of the flags on two flag objects are simultaneously set, while ``==`` checks that two flag objects are bit-for-bit equal.
The following code uses bitwise operations to add and revoke permissions from a given ``Roles`` object.
@@ -488,7 +488,7 @@ Fixed-size Lists
Fixed-size lists hold a finite number of elements which belong to a specified type.
-Lists can be declared with ``_name: _ValueType[_Integer]``, except ``Bytes[N]``, ``String[N]`` and enums.
+Lists can be declared with ``_name: _ValueType[_Integer]``, except ``Bytes[N]``, ``String[N]`` and flags.
.. code-block:: python
diff --git a/examples/crowdfund.vy b/examples/crowdfund.vy
index 3891ad0b74..6d07e15bc4 100644
--- a/examples/crowdfund.vy
+++ b/examples/crowdfund.vy
@@ -1,4 +1,8 @@
-# Setup private variables (only callable from within the contract)
+###########################################################################
+## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
+###########################################################################
+
+# example of a crowd funding contract
funders: HashMap[address, uint256]
beneficiary: address
@@ -18,15 +22,15 @@ def __init__(_beneficiary: address, _goal: uint256, _timelimit: uint256):
@external
@payable
def participate():
- assert block.timestamp < self.deadline, "deadline not met (yet)"
+ assert block.timestamp < self.deadline, "deadline has expired"
self.funders[msg.sender] += msg.value
# Enough money was raised! Send funds to the beneficiary
@external
def finalize():
- assert block.timestamp >= self.deadline, "deadline has passed"
- assert self.balance >= self.goal, "the goal has not been reached"
+ assert block.timestamp >= self.deadline, "deadline has not expired yet"
+ assert self.balance >= self.goal, "goal has not been reached"
selfdestruct(self.beneficiary)
diff --git a/examples/market_maker/on_chain_market_maker.vy b/examples/market_maker/on_chain_market_maker.vy
index be9c62b945..d385d2e0c6 100644
--- a/examples/market_maker/on_chain_market_maker.vy
+++ b/examples/market_maker/on_chain_market_maker.vy
@@ -9,7 +9,7 @@ invariant: public(uint256)
token_address: ERC20
owner: public(address)
-# Sets the on chain market maker with its owner, intial token quantity,
+# Sets the on chain market maker with its owner, initial token quantity,
# and initial ether quantity
@external
@payable
diff --git a/examples/tokens/ERC1155ownable.vy b/examples/tokens/ERC1155ownable.vy
index 8094225f18..30057582e8 100644
--- a/examples/tokens/ERC1155ownable.vy
+++ b/examples/tokens/ERC1155ownable.vy
@@ -1,8 +1,13 @@
+###########################################################################
+## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
+###########################################################################
+
# @version >=0.3.4
"""
-@dev Implementation of ERC-1155 non-fungible token standard ownable, with approval, OPENSEA compatible (name, symbol)
+@dev example implementation of ERC-1155 non-fungible token standard ownable, with approval, OPENSEA compatible (name, symbol)
@author Dr. Pixel (github: @Doc-Pixel)
"""
+
############### imports ###############
from vyper.interfaces import ERC165
@@ -214,7 +219,6 @@ def mint(receiver: address, id: uint256, amount:uint256):
@param receiver the account that will receive the minted token
@param id the ID of the token
@param amount of tokens for this ID
- @param data the data associated with this mint. Usually stays empty
"""
assert not self.paused, "The contract has been paused"
assert self.owner == msg.sender, "Only the contract owner can mint"
@@ -232,7 +236,6 @@ def mintBatch(receiver: address, ids: DynArray[uint256, BATCH_SIZE], amounts: Dy
@param receiver the account that will receive the minted token
@param ids array of ids for the tokens
@param amounts amounts of tokens for each ID in the ids array
- @param data the data associated with this mint. Usually stays empty
"""
assert not self.paused, "The contract has been paused"
assert self.owner == msg.sender, "Only the contract owner can mint"
diff --git a/examples/tokens/ERC20.vy b/examples/tokens/ERC20.vy
index 4c1d334691..c3809dbb60 100644
--- a/examples/tokens/ERC20.vy
+++ b/examples/tokens/ERC20.vy
@@ -1,4 +1,8 @@
-# @dev Implementation of ERC-20 token standard.
+###########################################################################
+## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
+###########################################################################
+
+# @dev example implementation of an ERC20 token
# @author Takayuki Jimba (@yudetamago)
# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
diff --git a/examples/tokens/ERC4626.vy b/examples/tokens/ERC4626.vy
index a9cbcc86c8..0a0a698bf0 100644
--- a/examples/tokens/ERC4626.vy
+++ b/examples/tokens/ERC4626.vy
@@ -1,4 +1,11 @@
# NOTE: Copied from https://github.com/fubuloubu/ERC4626/blob/1a10b051928b11eeaad15d80397ed36603c2a49b/contracts/VyperVault.vy
+
+# example implementation of an ERC4626 vault
+
+###########################################################################
+## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
+###########################################################################
+
from vyper.interfaces import ERC20
from vyper.interfaces import ERC4626
diff --git a/examples/tokens/ERC721.vy b/examples/tokens/ERC721.vy
index 5125040399..152b94b046 100644
--- a/examples/tokens/ERC721.vy
+++ b/examples/tokens/ERC721.vy
@@ -1,4 +1,8 @@
-# @dev Implementation of ERC-721 non-fungible token standard.
+###########################################################################
+## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
+###########################################################################
+
+# @dev example implementation of ERC-721 non-fungible token standard.
# @author Ryuya Nakamura (@nrryuya)
# Modified from: https://github.com/vyperlang/vyper/blob/de74722bf2d8718cca46902be165f9fe0e3641dd/examples/tokens/ERC721.vy
diff --git a/examples/wallet/wallet.vy b/examples/wallet/wallet.vy
index 5fd5229136..e2515d9e62 100644
--- a/examples/wallet/wallet.vy
+++ b/examples/wallet/wallet.vy
@@ -1,5 +1,8 @@
-# An example of how you can do a wallet in Vyper.
-# Warning: NOT AUDITED. Do not use to store substantial quantities of funds.
+###########################################################################
+## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR!
+###########################################################################
+
+# An example of how you can implement a wallet in Vyper.
# A list of the owners addresses (there are a maximum of 5 owners)
owners: public(address[5])
diff --git a/setup.py b/setup.py
index c81b9bed4a..f5d643ad88 100644
--- a/setup.py
+++ b/setup.py
@@ -13,7 +13,6 @@
"pytest-instafail>=0.4,<1.0",
"pytest-xdist>=2.5,<3.0",
"pytest-split>=0.7.0,<1.0",
- "pytest-rerunfailures>=10.2,<11",
"eth-tester[py-evm]>=0.9.0b1,<0.10",
"py-evm>=0.7.0a1,<0.8",
"web3==6.0.0",
@@ -23,12 +22,12 @@
"eth-stdlib==0.2.6",
],
"lint": [
- "black==23.3.0",
- "flake8==3.9.2",
- "flake8-bugbear==20.1.4",
- "flake8-use-fstring==1.1",
- "isort==5.9.3",
- "mypy==0.982",
+ "black==23.12.0",
+ "flake8==6.1.0",
+ "flake8-bugbear==23.12.2",
+ "flake8-use-fstring==1.4",
+ "isort==5.13.2",
+ "mypy==1.5",
],
"docs": ["recommonmark", "sphinx>=6.0,<7.0", "sphinx_rtd_theme>=1.2,<1.3"],
"dev": ["ipython", "pre-commit", "pyinstaller", "twine"],
@@ -99,7 +98,7 @@ def _global_version(version):
"importlib-metadata",
"wheel",
],
- setup_requires=["pytest-runner", "setuptools_scm"],
+ setup_requires=["pytest-runner", "setuptools_scm>=7.1.0,<8.0.0"],
tests_require=extras_require["test"],
extras_require=extras_require,
entry_points={
diff --git a/tests/ast/nodes/test_replace_in_tree.py b/tests/ast/nodes/test_replace_in_tree.py
deleted file mode 100644
index 682e7ce7de..0000000000
--- a/tests/ast/nodes/test_replace_in_tree.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import pytest
-
-from vyper import ast as vy_ast
-from vyper.exceptions import CompilerPanic
-
-
-def test_assumptions():
- # ASTs generated separately from the same source should compare equal
- test_tree = vy_ast.parse_to_ast("foo = 42")
- expected_tree = vy_ast.parse_to_ast("foo = 42")
- assert vy_ast.compare_nodes(test_tree, expected_tree)
-
- # ASTs generated separately with different source should compare not-equal
- test_tree = vy_ast.parse_to_ast("foo = 42")
- expected_tree = vy_ast.parse_to_ast("bar = 666")
- assert not vy_ast.compare_nodes(test_tree, expected_tree)
-
-
-def test_simple_replacement():
- test_tree = vy_ast.parse_to_ast("foo = 42")
- expected_tree = vy_ast.parse_to_ast("bar = 42")
-
- old_node = test_tree.body[0].target
- new_node = vy_ast.parse_to_ast("bar").body[0].value
-
- test_tree.replace_in_tree(old_node, new_node)
-
- assert vy_ast.compare_nodes(test_tree, expected_tree)
-
-
-def test_list_replacement_similar_nodes():
- test_tree = vy_ast.parse_to_ast("foo = [1, 1, 1, 1, 1]")
- expected_tree = vy_ast.parse_to_ast("foo = [1, 1, 31337, 1, 1]")
-
- old_node = test_tree.body[0].value.elements[2]
- new_node = vy_ast.parse_to_ast("31337").body[0].value
-
- test_tree.replace_in_tree(old_node, new_node)
-
- assert vy_ast.compare_nodes(test_tree, expected_tree)
-
-
-def test_parents_children():
- test_tree = vy_ast.parse_to_ast("foo = 42")
-
- old_node = test_tree.body[0].target
- parent = old_node.get_ancestor()
-
- new_node = vy_ast.parse_to_ast("bar").body[0].value
- test_tree.replace_in_tree(old_node, new_node)
-
- assert old_node.get_ancestor() == new_node.get_ancestor()
-
- assert old_node not in parent.get_children()
- assert new_node in parent.get_children()
-
- assert old_node not in test_tree.get_descendants()
- assert new_node in test_tree.get_descendants()
-
-
-def test_cannot_replace_twice():
- test_tree = vy_ast.parse_to_ast("foo = 42")
- old_node = test_tree.body[0].target
-
- new_node = vy_ast.parse_to_ast("42").body[0].value
-
- test_tree.replace_in_tree(old_node, new_node)
-
- with pytest.raises(CompilerPanic):
- test_tree.replace_in_tree(old_node, new_node)
diff --git a/tests/ast/test_folding.py b/tests/ast/test_folding.py
deleted file mode 100644
index 22d5f58222..0000000000
--- a/tests/ast/test_folding.py
+++ /dev/null
@@ -1,315 +0,0 @@
-import pytest
-
-from vyper import ast as vy_ast
-from vyper.ast import folding
-from vyper.exceptions import OverflowException
-
-
-def test_integration():
- test_ast = vy_ast.parse_to_ast("[1+2, 6+7][8-8]")
- expected_ast = vy_ast.parse_to_ast("3")
-
- folding.fold(test_ast)
-
- assert vy_ast.compare_nodes(test_ast, expected_ast)
-
-
-def test_replace_binop_simple():
- test_ast = vy_ast.parse_to_ast("1 + 2")
- expected_ast = vy_ast.parse_to_ast("3")
-
- folding.replace_literal_ops(test_ast)
-
- assert vy_ast.compare_nodes(test_ast, expected_ast)
-
-
-def test_replace_binop_nested():
- test_ast = vy_ast.parse_to_ast("((6 + (2**4)) * 4) / 2")
- expected_ast = vy_ast.parse_to_ast("44")
-
- folding.replace_literal_ops(test_ast)
-
- assert vy_ast.compare_nodes(test_ast, expected_ast)
-
-
-def test_replace_binop_nested_intermediate_overflow():
- test_ast = vy_ast.parse_to_ast("2**255 * 2 / 10")
- with pytest.raises(OverflowException):
- folding.fold(test_ast)
-
-
-def test_replace_binop_nested_intermediate_underflow():
- test_ast = vy_ast.parse_to_ast("-2**255 * 2 - 10 + 100")
- with pytest.raises(OverflowException):
- folding.fold(test_ast)
-
-
-def test_replace_decimal_nested_intermediate_overflow():
- test_ast = vy_ast.parse_to_ast(
- "18707220957835557353007165858768422651595.9365500927 + 1e-10 - 1e-10"
- )
- with pytest.raises(OverflowException):
- folding.fold(test_ast)
-
-
-def test_replace_decimal_nested_intermediate_underflow():
- test_ast = vy_ast.parse_to_ast(
- "-18707220957835557353007165858768422651595.9365500928 - 1e-10 + 1e-10"
- )
- with pytest.raises(OverflowException):
- folding.fold(test_ast)
-
-
-def test_replace_literal_ops():
- test_ast = vy_ast.parse_to_ast("[not True, True and False, True or False]")
- expected_ast = vy_ast.parse_to_ast("[False, False, True]")
-
- folding.replace_literal_ops(test_ast)
-
- assert vy_ast.compare_nodes(test_ast, expected_ast)
-
-
-def test_replace_subscripts_simple():
- test_ast = vy_ast.parse_to_ast("[foo, bar, baz][1]")
- expected_ast = vy_ast.parse_to_ast("bar")
-
- folding.replace_subscripts(test_ast)
-
- assert vy_ast.compare_nodes(test_ast, expected_ast)
-
-
-def test_replace_subscripts_nested():
- test_ast = vy_ast.parse_to_ast("[[0, 1], [2, 3], [4, 5]][2][1]")
- expected_ast = vy_ast.parse_to_ast("5")
-
- folding.replace_subscripts(test_ast)
-
- assert vy_ast.compare_nodes(test_ast, expected_ast)
-
-
-constants_modified = [
- "bar = FOO",
- "bar: int128[FOO]",
- "[1, 2, FOO]",
- "def bar(a: int128 = FOO): pass",
- "log bar(FOO)",
- "FOO + 1",
- "a: int128[FOO / 2]",
- "a[FOO - 1] = 44",
-]
-
-
-@pytest.mark.parametrize("source", constants_modified)
-def test_replace_constant(source):
- unmodified_ast = vy_ast.parse_to_ast(source)
- folded_ast = vy_ast.parse_to_ast(source)
-
- folding.replace_constant(folded_ast, "FOO", vy_ast.Int(value=31337), True)
-
- assert not vy_ast.compare_nodes(unmodified_ast, folded_ast)
-
-
-constants_unmodified = [
- "FOO = 42",
- "self.FOO = 42",
- "bar = FOO()",
- "FOO()",
- "bar = FOO()",
- "bar = self.FOO",
- "log FOO(bar)",
- "[1, 2, FOO()]",
- "FOO[42] = 2",
-]
-
-
-@pytest.mark.parametrize("source", constants_unmodified)
-def test_replace_constant_no(source):
- unmodified_ast = vy_ast.parse_to_ast(source)
- folded_ast = vy_ast.parse_to_ast(source)
-
- folding.replace_constant(folded_ast, "FOO", vy_ast.Int(value=31337), True)
-
- assert vy_ast.compare_nodes(unmodified_ast, folded_ast)
-
-
-builtins_modified = [
- "ZERO_ADDRESS",
- "foo = ZERO_ADDRESS",
- "foo: int128[ZERO_ADDRESS] = 42",
- "foo = [ZERO_ADDRESS]",
- "def foo(bar: address = ZERO_ADDRESS): pass",
- "def foo(): bar = ZERO_ADDRESS",
- "def foo(): return ZERO_ADDRESS",
- "log foo(ZERO_ADDRESS)",
- "log foo(42, ZERO_ADDRESS)",
-]
-
-
-@pytest.mark.parametrize("source", builtins_modified)
-def test_replace_builtin_constant(source):
- unmodified_ast = vy_ast.parse_to_ast(source)
- folded_ast = vy_ast.parse_to_ast(source)
-
- folding.replace_builtin_constants(folded_ast)
-
- assert not vy_ast.compare_nodes(unmodified_ast, folded_ast)
-
-
-builtins_unmodified = [
- "ZERO_ADDRESS = 2",
- "ZERO_ADDRESS()",
- "def foo(ZERO_ADDRESS: int128 = 42): pass",
- "def foo(): ZERO_ADDRESS = 42",
- "def ZERO_ADDRESS(): pass",
- "log ZERO_ADDRESS(42)",
-]
-
-
-@pytest.mark.parametrize("source", builtins_unmodified)
-def test_replace_builtin_constant_no(source):
- unmodified_ast = vy_ast.parse_to_ast(source)
- folded_ast = vy_ast.parse_to_ast(source)
-
- folding.replace_builtin_constants(folded_ast)
-
- assert vy_ast.compare_nodes(unmodified_ast, folded_ast)
-
-
-userdefined_modified = [
- "FOO",
- "foo = FOO",
- "foo: int128[FOO] = 42",
- "foo = [FOO]",
- "foo += FOO",
- "def foo(bar: int128 = FOO): pass",
- "def foo(): bar = FOO",
- "def foo(): return FOO",
-]
-
-
-@pytest.mark.parametrize("source", userdefined_modified)
-def test_replace_userdefined_constant(source):
- source = f"FOO: constant(int128) = 42\n{source}"
-
- unmodified_ast = vy_ast.parse_to_ast(source)
- folded_ast = vy_ast.parse_to_ast(source)
-
- folding.replace_user_defined_constants(folded_ast)
-
- assert not vy_ast.compare_nodes(unmodified_ast, folded_ast)
-
-
-userdefined_unmodified = [
- "FOO: constant(int128) = 42",
- "FOO = 42",
- "FOO += 42",
- "FOO()",
- "def foo(FOO: int128 = 42): pass",
- "def foo(): FOO = 42",
- "def FOO(): pass",
-]
-
-
-@pytest.mark.parametrize("source", userdefined_unmodified)
-def test_replace_userdefined_constant_no(source):
- source = f"FOO: constant(int128) = 42\n{source}"
-
- unmodified_ast = vy_ast.parse_to_ast(source)
- folded_ast = vy_ast.parse_to_ast(source)
-
- folding.replace_user_defined_constants(folded_ast)
-
- assert vy_ast.compare_nodes(unmodified_ast, folded_ast)
-
-
-dummy_address = "0x000000000000000000000000000000000000dEaD"
-userdefined_attributes = [("b: uint256 = ADDR.balance", f"b: uint256 = {dummy_address}.balance")]
-
-
-@pytest.mark.parametrize("source", userdefined_attributes)
-def test_replace_userdefined_attribute(source):
- preamble = f"ADDR: constant(address) = {dummy_address}"
- l_source = f"{preamble}\n{source[0]}"
- r_source = f"{preamble}\n{source[1]}"
-
- l_ast = vy_ast.parse_to_ast(l_source)
- folding.replace_user_defined_constants(l_ast)
-
- r_ast = vy_ast.parse_to_ast(r_source)
-
- assert vy_ast.compare_nodes(l_ast, r_ast)
-
-
-userdefined_struct = [("b: Foo = FOO", "b: Foo = Foo({a: 123, b: 456})")]
-
-
-@pytest.mark.parametrize("source", userdefined_struct)
-def test_replace_userdefined_struct(source):
- preamble = """
-struct Foo:
- a: uint256
- b: uint256
-
-FOO: constant(Foo) = Foo({a: 123, b: 456})
- """
- l_source = f"{preamble}\n{source[0]}"
- r_source = f"{preamble}\n{source[1]}"
-
- l_ast = vy_ast.parse_to_ast(l_source)
- folding.replace_user_defined_constants(l_ast)
-
- r_ast = vy_ast.parse_to_ast(r_source)
-
- assert vy_ast.compare_nodes(l_ast, r_ast)
-
-
-userdefined_nested_struct = [
- ("b: Foo = FOO", "b: Foo = Foo({f1: Bar({b1: 123, b2: 456}), f2: 789})")
-]
-
-
-@pytest.mark.parametrize("source", userdefined_nested_struct)
-def test_replace_userdefined_nested_struct(source):
- preamble = """
-struct Bar:
- b1: uint256
- b2: uint256
-
-struct Foo:
- f1: Bar
- f2: uint256
-
-FOO: constant(Foo) = Foo({f1: Bar({b1: 123, b2: 456}), f2: 789})
- """
- l_source = f"{preamble}\n{source[0]}"
- r_source = f"{preamble}\n{source[1]}"
-
- l_ast = vy_ast.parse_to_ast(l_source)
- folding.replace_user_defined_constants(l_ast)
-
- r_ast = vy_ast.parse_to_ast(r_source)
-
- assert vy_ast.compare_nodes(l_ast, r_ast)
-
-
-builtin_folding_functions = [("ceil(4.2)", "5"), ("floor(4.2)", "4")]
-
-builtin_folding_sources = [
- "{}",
- "foo = {}",
- "foo = [{0}, {0}]",
- "def foo(): {}",
- "def foo(): return {}",
- "def foo(bar: {}): pass",
-]
-
-
-@pytest.mark.parametrize("source", builtin_folding_sources)
-@pytest.mark.parametrize("original,result", builtin_folding_functions)
-def test_replace_builtins(source, original, result):
- original_ast = vy_ast.parse_to_ast(source.format(original))
- target_ast = vy_ast.parse_to_ast(source.format(result))
-
- folding.replace_builtin_functions(original_ast)
-
- assert vy_ast.compare_nodes(original_ast, target_ast)
diff --git a/tests/base_conftest.py b/tests/base_conftest.py
deleted file mode 100644
index 81e8dedc36..0000000000
--- a/tests/base_conftest.py
+++ /dev/null
@@ -1,216 +0,0 @@
-import json
-
-import pytest
-import web3.exceptions
-from eth_tester import EthereumTester, PyEVMBackend
-from eth_tester.exceptions import TransactionFailed
-from eth_utils.toolz import compose
-from hexbytes import HexBytes
-from web3 import Web3
-from web3.contract import Contract
-from web3.providers.eth_tester import EthereumTesterProvider
-
-from vyper import compiler
-from vyper.ast.grammar import parse_vyper_source
-from vyper.compiler.settings import Settings
-
-
-class VyperMethod:
- ALLOWED_MODIFIERS = {"call", "estimateGas", "transact", "buildTransaction"}
-
- def __init__(self, function, normalizers=None):
- self._function = function
- self._function._return_data_normalizers = normalizers
-
- def __call__(self, *args, **kwargs):
- return self.__prepared_function(*args, **kwargs)
-
- def __prepared_function(self, *args, **kwargs):
- if not kwargs:
- modifier, modifier_dict = "call", {}
- fn_abi = [
- x
- for x in self._function.contract_abi
- if x.get("name") == self._function.function_identifier
- ].pop()
- # To make tests faster just supply some high gas value.
- modifier_dict.update({"gas": fn_abi.get("gas", 0) + 500000})
- elif len(kwargs) == 1:
- modifier, modifier_dict = kwargs.popitem()
- if modifier not in self.ALLOWED_MODIFIERS:
- raise TypeError(f"The only allowed keyword arguments are: {self.ALLOWED_MODIFIERS}")
- else:
- raise TypeError(f"Use up to one keyword argument, one of: {self.ALLOWED_MODIFIERS}")
- return getattr(self._function(*args), modifier)(modifier_dict)
-
-
-class VyperContract:
- """
- An alternative Contract Factory which invokes all methods as `call()`,
- unless you add a keyword argument. The keyword argument assigns the prep method.
- This call
- > contract.withdraw(amount, transact={'from': eth.accounts[1], 'gas': 100000, ...})
- is equivalent to this call in the classic contract:
- > contract.functions.withdraw(amount).transact({'from': eth.accounts[1], 'gas': 100000, ...})
- """
-
- def __init__(self, classic_contract, method_class=VyperMethod):
- classic_contract._return_data_normalizers += CONCISE_NORMALIZERS
- self._classic_contract = classic_contract
- self.address = self._classic_contract.address
- protected_fn_names = [fn for fn in dir(self) if not fn.endswith("__")]
-
- try:
- fn_names = [fn["name"] for fn in self._classic_contract.functions._functions]
- except web3.exceptions.NoABIFunctionsFound:
- fn_names = []
-
- for fn_name in fn_names:
- # Override namespace collisions
- if fn_name in protected_fn_names:
- raise AttributeError(f"{fn_name} is protected!")
- else:
- _classic_method = getattr(self._classic_contract.functions, fn_name)
- _concise_method = method_class(
- _classic_method, self._classic_contract._return_data_normalizers
- )
- setattr(self, fn_name, _concise_method)
-
- @classmethod
- def factory(cls, *args, **kwargs):
- return compose(cls, Contract.factory(*args, **kwargs))
-
-
-def _none_addr(datatype, data):
- if datatype == "address" and int(data, base=16) == 0:
- return (datatype, None)
- else:
- return (datatype, data)
-
-
-CONCISE_NORMALIZERS = (_none_addr,)
-
-
-@pytest.fixture(scope="module")
-def tester():
- # set absurdly high gas limit so that london basefee never adjusts
- # (note: 2**63 - 1 is max that evm allows)
- custom_genesis = PyEVMBackend._generate_genesis_params(overrides={"gas_limit": 10**10})
- custom_genesis["base_fee_per_gas"] = 0
- backend = PyEVMBackend(genesis_parameters=custom_genesis)
- return EthereumTester(backend=backend)
-
-
-def zero_gas_price_strategy(web3, transaction_params=None):
- return 0 # zero gas price makes testing simpler.
-
-
-@pytest.fixture(scope="module")
-def w3(tester):
- w3 = Web3(EthereumTesterProvider(tester))
- w3.eth.set_gas_price_strategy(zero_gas_price_strategy)
- return w3
-
-
-def _get_contract(w3, source_code, optimize, *args, override_opt_level=None, **kwargs):
- settings = Settings()
- settings.evm_version = kwargs.pop("evm_version", None)
- settings.optimize = override_opt_level or optimize
- out = compiler.compile_code(
- source_code,
- # test that metadata gets generated
- ["abi", "bytecode", "metadata"],
- settings=settings,
- interface_codes=kwargs.pop("interface_codes", None),
- show_gas_estimates=True, # Enable gas estimates for testing
- )
- parse_vyper_source(source_code) # Test grammar.
- json.dumps(out["metadata"]) # test metadata is json serializable
- abi = out["abi"]
- bytecode = out["bytecode"]
- value = kwargs.pop("value_in_eth", 0) * 10**18 # Handle deploying with an eth value.
- c = w3.eth.contract(abi=abi, bytecode=bytecode)
- deploy_transaction = c.constructor(*args)
- tx_info = {"from": w3.eth.accounts[0], "value": value, "gasPrice": 0}
- tx_info.update(kwargs)
- tx_hash = deploy_transaction.transact(tx_info)
- address = w3.eth.get_transaction_receipt(tx_hash)["contractAddress"]
- return w3.eth.contract(address, abi=abi, bytecode=bytecode, ContractFactoryClass=VyperContract)
-
-
-def _deploy_blueprint_for(w3, source_code, optimize, initcode_prefix=b"", **kwargs):
- settings = Settings()
- settings.evm_version = kwargs.pop("evm_version", None)
- settings.optimize = optimize
- out = compiler.compile_code(
- source_code,
- ["abi", "bytecode"],
- interface_codes=kwargs.pop("interface_codes", None),
- settings=settings,
- show_gas_estimates=True, # Enable gas estimates for testing
- )
- parse_vyper_source(source_code) # Test grammar.
- abi = out["abi"]
- bytecode = HexBytes(initcode_prefix) + HexBytes(out["bytecode"])
- bytecode_len = len(bytecode)
- bytecode_len_hex = hex(bytecode_len)[2:].rjust(4, "0")
- # prepend a quick deploy preamble
- deploy_preamble = HexBytes("61" + bytecode_len_hex + "3d81600a3d39f3")
- deploy_bytecode = HexBytes(deploy_preamble) + bytecode
-
- deployer_abi = [] # just a constructor
- c = w3.eth.contract(abi=deployer_abi, bytecode=deploy_bytecode)
- deploy_transaction = c.constructor()
- tx_info = {"from": w3.eth.accounts[0], "value": 0, "gasPrice": 0}
-
- tx_hash = deploy_transaction.transact(tx_info)
- address = w3.eth.get_transaction_receipt(tx_hash)["contractAddress"]
-
- # sanity check
- assert w3.eth.get_code(address) == bytecode, (w3.eth.get_code(address), bytecode)
-
- def factory(address):
- return w3.eth.contract(
- address, abi=abi, bytecode=bytecode, ContractFactoryClass=VyperContract
- )
-
- return w3.eth.contract(address, bytecode=deploy_bytecode), factory
-
-
-@pytest.fixture(scope="module")
-def deploy_blueprint_for(w3, optimize):
- def deploy_blueprint_for(source_code, *args, **kwargs):
- return _deploy_blueprint_for(w3, source_code, optimize, *args, **kwargs)
-
- return deploy_blueprint_for
-
-
-@pytest.fixture(scope="module")
-def get_contract(w3, optimize):
- def get_contract(source_code, *args, **kwargs):
- return _get_contract(w3, source_code, optimize, *args, **kwargs)
-
- return get_contract
-
-
-@pytest.fixture
-def get_logs(w3):
- def get_logs(tx_hash, c, event_name):
- tx_receipt = w3.eth.get_transaction_receipt(tx_hash)
- return c._classic_contract.events[event_name]().process_receipt(tx_receipt)
-
- return get_logs
-
-
-@pytest.fixture(scope="module")
-def assert_tx_failed(tester):
- def assert_tx_failed(function_to_test, exception=TransactionFailed, exc_text=None):
- snapshot_id = tester.take_snapshot()
- with pytest.raises(exception) as excinfo:
- function_to_test()
- tester.revert_to_snapshot(snapshot_id)
- if exc_text:
- # TODO test equality
- assert exc_text in str(excinfo.value), (exc_text, excinfo.value)
-
- return assert_tx_failed
diff --git a/tests/cli/vyper_compile/test_compile_files.py b/tests/cli/vyper_compile/test_compile_files.py
deleted file mode 100644
index 31cf622658..0000000000
--- a/tests/cli/vyper_compile/test_compile_files.py
+++ /dev/null
@@ -1,30 +0,0 @@
-import pytest
-
-from vyper.cli.vyper_compile import compile_files
-
-
-def test_combined_json_keys(tmp_path):
- bar_path = tmp_path.joinpath("bar.vy")
- with bar_path.open("w") as fp:
- fp.write("")
-
- combined_keys = {
- "bytecode",
- "bytecode_runtime",
- "blueprint_bytecode",
- "abi",
- "source_map",
- "layout",
- "method_identifiers",
- "userdoc",
- "devdoc",
- }
- compile_data = compile_files([bar_path], ["combined_json"], root_folder=tmp_path)
-
- assert set(compile_data.keys()) == {"bar.vy", "version"}
- assert set(compile_data["bar.vy"].keys()) == combined_keys
-
-
-def test_invalid_root_path():
- with pytest.raises(FileNotFoundError):
- compile_files([], [], root_folder="path/that/does/not/exist")
diff --git a/tests/cli/vyper_compile/test_import_paths.py b/tests/cli/vyper_compile/test_import_paths.py
deleted file mode 100644
index 81f209113f..0000000000
--- a/tests/cli/vyper_compile/test_import_paths.py
+++ /dev/null
@@ -1,260 +0,0 @@
-import pytest
-
-from vyper.cli.vyper_compile import compile_files, get_interface_file_path
-
-FOO_CODE = """
-{}
-
-struct FooStruct:
- foo_: uint256
-
-@external
-def foo() -> FooStruct:
- return FooStruct({{foo_: 13}})
-
-@external
-def bar(a: address) -> FooStruct:
- return {}(a).bar()
-"""
-
-BAR_CODE = """
-struct FooStruct:
- foo_: uint256
-@external
-def bar() -> FooStruct:
- return FooStruct({foo_: 13})
-"""
-
-
-SAME_FOLDER_IMPORT_STMT = [
- ("import Bar as Bar", "Bar"),
- ("import contracts.Bar as Bar", "Bar"),
- ("from . import Bar", "Bar"),
- ("from contracts import Bar", "Bar"),
- ("from ..contracts import Bar", "Bar"),
- ("from . import Bar as FooBar", "FooBar"),
- ("from contracts import Bar as FooBar", "FooBar"),
- ("from ..contracts import Bar as FooBar", "FooBar"),
-]
-
-
-@pytest.mark.parametrize("import_stmt,alias", SAME_FOLDER_IMPORT_STMT)
-def test_import_same_folder(import_stmt, alias, tmp_path):
- tmp_path.joinpath("contracts").mkdir()
-
- foo_path = tmp_path.joinpath("contracts/foo.vy")
- with foo_path.open("w") as fp:
- fp.write(FOO_CODE.format(import_stmt, alias))
-
- with tmp_path.joinpath("contracts/Bar.vy").open("w") as fp:
- fp.write(BAR_CODE)
-
- assert compile_files([foo_path], ["combined_json"], root_folder=tmp_path)
-
-
-SUBFOLDER_IMPORT_STMT = [
- ("import other.Bar as Bar", "Bar"),
- ("import contracts.other.Bar as Bar", "Bar"),
- ("from other import Bar", "Bar"),
- ("from contracts.other import Bar", "Bar"),
- ("from .other import Bar", "Bar"),
- ("from ..contracts.other import Bar", "Bar"),
- ("from other import Bar as FooBar", "FooBar"),
- ("from contracts.other import Bar as FooBar", "FooBar"),
- ("from .other import Bar as FooBar", "FooBar"),
- ("from ..contracts.other import Bar as FooBar", "FooBar"),
-]
-
-
-@pytest.mark.parametrize("import_stmt, alias", SUBFOLDER_IMPORT_STMT)
-def test_import_subfolder(import_stmt, alias, tmp_path):
- tmp_path.joinpath("contracts").mkdir()
-
- foo_path = tmp_path.joinpath("contracts/foo.vy")
- with foo_path.open("w") as fp:
- fp.write(FOO_CODE.format(import_stmt, alias))
-
- tmp_path.joinpath("contracts/other").mkdir()
- with tmp_path.joinpath("contracts/other/Bar.vy").open("w") as fp:
- fp.write(BAR_CODE)
-
- assert compile_files([foo_path], ["combined_json"], root_folder=tmp_path)
-
-
-OTHER_FOLDER_IMPORT_STMT = [
- ("import interfaces.Bar as Bar", "Bar"),
- ("from interfaces import Bar", "Bar"),
- ("from ..interfaces import Bar", "Bar"),
- ("from interfaces import Bar as FooBar", "FooBar"),
- ("from ..interfaces import Bar as FooBar", "FooBar"),
-]
-
-
-@pytest.mark.parametrize("import_stmt, alias", OTHER_FOLDER_IMPORT_STMT)
-def test_import_other_folder(import_stmt, alias, tmp_path):
- tmp_path.joinpath("contracts").mkdir()
-
- foo_path = tmp_path.joinpath("contracts/foo.vy")
- with foo_path.open("w") as fp:
- fp.write(FOO_CODE.format(import_stmt, alias))
-
- tmp_path.joinpath("interfaces").mkdir()
- with tmp_path.joinpath("interfaces/Bar.vy").open("w") as fp:
- fp.write(BAR_CODE)
-
- assert compile_files([foo_path], ["combined_json"], root_folder=tmp_path)
-
-
-def test_import_parent_folder(tmp_path, assert_compile_failed):
- tmp_path.joinpath("contracts").mkdir()
- tmp_path.joinpath("contracts/baz").mkdir()
-
- foo_path = tmp_path.joinpath("contracts/baz/foo.vy")
- with foo_path.open("w") as fp:
- fp.write(FOO_CODE.format("from ... import Bar", "Bar"))
-
- with tmp_path.joinpath("Bar.vy").open("w") as fp:
- fp.write(BAR_CODE)
-
- assert compile_files([foo_path], ["combined_json"], root_folder=tmp_path)
- # Cannot perform relative import outside of base folder
- with pytest.raises(FileNotFoundError):
- compile_files([foo_path], ["combined_json"], root_folder=tmp_path.joinpath("contracts"))
-
-
-META_IMPORT_STMT = [
- "import Meta as Meta",
- "import contracts.Meta as Meta",
- "from . import Meta",
- "from contracts import Meta",
-]
-
-
-@pytest.mark.parametrize("import_stmt", META_IMPORT_STMT)
-def test_import_self_interface(import_stmt, tmp_path):
- # a contract can access its derived interface by importing itself
- code = f"""
-{import_stmt}
-
-struct FooStruct:
- foo_: uint256
-
-@external
-def know_thyself(a: address) -> FooStruct:
- return Meta(a).be_known()
-
-@external
-def be_known() -> FooStruct:
- return FooStruct({{foo_: 42}})
- """
-
- tmp_path.joinpath("contracts").mkdir()
-
- meta_path = tmp_path.joinpath("contracts/Meta.vy")
- with meta_path.open("w") as fp:
- fp.write(code)
-
- assert compile_files([meta_path], ["combined_json"], root_folder=tmp_path)
-
-
-DERIVED_IMPORT_STMT_BAZ = ["import Foo as Foo", "from . import Foo"]
-
-DERIVED_IMPORT_STMT_FOO = ["import Bar as Bar", "from . import Bar"]
-
-
-@pytest.mark.parametrize("import_stmt_baz", DERIVED_IMPORT_STMT_BAZ)
-@pytest.mark.parametrize("import_stmt_foo", DERIVED_IMPORT_STMT_FOO)
-def test_derived_interface_imports(import_stmt_baz, import_stmt_foo, tmp_path):
- # contracts-as-interfaces should be able to contain import statements
- baz_code = f"""
-{import_stmt_baz}
-
-struct FooStruct:
- foo_: uint256
-
-@external
-def foo(a: address) -> FooStruct:
- return Foo(a).foo()
-
-@external
-def bar(_foo: address, _bar: address) -> FooStruct:
- return Foo(_foo).bar(_bar)
- """
-
- with tmp_path.joinpath("Foo.vy").open("w") as fp:
- fp.write(FOO_CODE.format(import_stmt_foo, "Bar"))
-
- with tmp_path.joinpath("Bar.vy").open("w") as fp:
- fp.write(BAR_CODE)
-
- baz_path = tmp_path.joinpath("Baz.vy")
- with baz_path.open("w") as fp:
- fp.write(baz_code)
-
- assert compile_files([baz_path], ["combined_json"], root_folder=tmp_path)
-
-
-def test_local_namespace(tmp_path):
- # interface code namespaces should be isolated
- # all of these contract should be able to compile together
- codes = [
- "import foo as FooBar",
- "import bar as FooBar",
- "import foo as BarFoo",
- "import bar as BarFoo",
- ]
- struct_def = """
-struct FooStruct:
- foo_: uint256
-
- """
-
- compile_paths = []
- for i, code in enumerate(codes):
- code += struct_def
- path = tmp_path.joinpath(f"code{i}.vy")
- with path.open("w") as fp:
- fp.write(code)
- compile_paths.append(path)
-
- for file_name in ("foo.vy", "bar.vy"):
- with tmp_path.joinpath(file_name).open("w") as fp:
- fp.write(BAR_CODE)
-
- assert compile_files(compile_paths, ["combined_json"], root_folder=tmp_path)
-
-
-def test_get_interface_file_path(tmp_path):
- for file_name in ("foo.vy", "foo.json", "bar.vy", "baz.json", "potato"):
- with tmp_path.joinpath(file_name).open("w") as fp:
- fp.write("")
-
- tmp_path.joinpath("interfaces").mkdir()
- for file_name in ("interfaces/foo.json", "interfaces/bar"):
- with tmp_path.joinpath(file_name).open("w") as fp:
- fp.write("")
-
- base_paths = [tmp_path, tmp_path.joinpath("interfaces")]
- assert get_interface_file_path(base_paths, "foo") == tmp_path.joinpath("foo.vy")
- assert get_interface_file_path(base_paths, "bar") == tmp_path.joinpath("bar.vy")
- assert get_interface_file_path(base_paths, "baz") == tmp_path.joinpath("baz.json")
-
- base_paths = [tmp_path.joinpath("interfaces"), tmp_path]
- assert get_interface_file_path(base_paths, "foo") == tmp_path.joinpath("interfaces/foo.json")
- assert get_interface_file_path(base_paths, "bar") == tmp_path.joinpath("bar.vy")
- assert get_interface_file_path(base_paths, "baz") == tmp_path.joinpath("baz.json")
-
- with pytest.raises(Exception):
- get_interface_file_path(base_paths, "potato")
-
-
-def test_compile_outside_root_path(tmp_path):
- foo_path = tmp_path.joinpath("foo.vy")
- with foo_path.open("w") as fp:
- fp.write(FOO_CODE.format("import bar as Bar", "Bar"))
-
- bar_path = tmp_path.joinpath("bar.vy")
- with bar_path.open("w") as fp:
- fp.write(BAR_CODE)
-
- assert compile_files([foo_path, bar_path], ["combined_json"], root_folder=".")
diff --git a/tests/cli/vyper_json/test_compile_from_input_dict.py b/tests/cli/vyper_json/test_compile_from_input_dict.py
deleted file mode 100644
index a6d0a23100..0000000000
--- a/tests/cli/vyper_json/test_compile_from_input_dict.py
+++ /dev/null
@@ -1,132 +0,0 @@
-#!/usr/bin/env python3
-
-from copy import deepcopy
-
-import pytest
-
-import vyper
-from vyper.cli.vyper_json import (
- TRANSLATE_MAP,
- compile_from_input_dict,
- exc_handler_raises,
- exc_handler_to_dict,
-)
-from vyper.exceptions import InvalidType, JSONError, SyntaxException
-
-FOO_CODE = """
-import contracts.bar as Bar
-
-@external
-def foo(a: address) -> bool:
- return Bar(a).bar(1)
-
-@external
-def baz() -> uint256:
- return self.balance
-"""
-
-BAR_CODE = """
-@external
-def bar(a: uint256) -> bool:
- return True
-"""
-
-BAD_SYNTAX_CODE = """
-def bar()>:
-"""
-
-BAD_COMPILER_CODE = """
-@external
-def oopsie(a: uint256) -> bool:
- return 42
-"""
-
-BAR_ABI = [
- {
- "name": "bar",
- "outputs": [{"type": "bool", "name": "out"}],
- "inputs": [{"type": "uint256", "name": "a"}],
- "stateMutability": "nonpayable",
- "type": "function",
- "gas": 313,
- }
-]
-
-INPUT_JSON = {
- "language": "Vyper",
- "sources": {
- "contracts/foo.vy": {"content": FOO_CODE},
- "contracts/bar.vy": {"content": BAR_CODE},
- },
- "interfaces": {"contracts/bar.json": {"abi": BAR_ABI}},
- "settings": {"outputSelection": {"*": ["*"]}},
-}
-
-
-def test_root_folder_not_exists():
- with pytest.raises(FileNotFoundError):
- compile_from_input_dict({}, root_folder="/path/that/does/not/exist")
-
-
-def test_wrong_language():
- with pytest.raises(JSONError):
- compile_from_input_dict({"language": "Solidity"})
-
-
-def test_exc_handler_raises_syntax():
- input_json = deepcopy(INPUT_JSON)
- input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE}
- with pytest.raises(SyntaxException):
- compile_from_input_dict(input_json, exc_handler_raises)
-
-
-def test_exc_handler_to_dict_syntax():
- input_json = deepcopy(INPUT_JSON)
- input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE}
- result, _ = compile_from_input_dict(input_json, exc_handler_to_dict)
- assert "errors" in result
- assert len(result["errors"]) == 1
- error = result["errors"][0]
- assert error["component"] == "parser"
- assert error["type"] == "SyntaxException"
-
-
-def test_exc_handler_raises_compiler():
- input_json = deepcopy(INPUT_JSON)
- input_json["sources"]["badcode.vy"] = {"content": BAD_COMPILER_CODE}
- with pytest.raises(InvalidType):
- compile_from_input_dict(input_json, exc_handler_raises)
-
-
-def test_exc_handler_to_dict_compiler():
- input_json = deepcopy(INPUT_JSON)
- input_json["sources"]["badcode.vy"] = {"content": BAD_COMPILER_CODE}
- result, _ = compile_from_input_dict(input_json, exc_handler_to_dict)
- assert sorted(result.keys()) == ["compiler", "errors"]
- assert result["compiler"] == f"vyper-{vyper.__version__}"
- assert len(result["errors"]) == 1
- error = result["errors"][0]
- assert error["component"] == "compiler"
- assert error["type"] == "InvalidType"
-
-
-def test_source_ids_increment():
- input_json = deepcopy(INPUT_JSON)
- input_json["settings"]["outputSelection"] = {"*": ["evm.deployedBytecode.sourceMap"]}
- result, _ = compile_from_input_dict(input_json)
- assert result["contracts/bar.vy"]["source_map"]["pc_pos_map_compressed"].startswith("-1:-1:0")
- assert result["contracts/foo.vy"]["source_map"]["pc_pos_map_compressed"].startswith("-1:-1:1")
-
-
-def test_outputs():
- result, _ = compile_from_input_dict(INPUT_JSON)
- assert sorted(result.keys()) == ["contracts/bar.vy", "contracts/foo.vy"]
- assert sorted(result["contracts/bar.vy"].keys()) == sorted(set(TRANSLATE_MAP.values()))
-
-
-def test_relative_import_paths():
- input_json = deepcopy(INPUT_JSON)
- input_json["sources"]["contracts/potato/baz/baz.vy"] = {"content": """from ... import foo"""}
- input_json["sources"]["contracts/potato/baz/potato.vy"] = {"content": """from . import baz"""}
- input_json["sources"]["contracts/potato/footato.vy"] = {"content": """from baz import baz"""}
- compile_from_input_dict(input_json)
diff --git a/tests/cli/vyper_json/test_compile_json.py b/tests/cli/vyper_json/test_compile_json.py
deleted file mode 100644
index f03006c4ad..0000000000
--- a/tests/cli/vyper_json/test_compile_json.py
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/usr/bin/env python3
-
-import json
-from copy import deepcopy
-
-import pytest
-
-from vyper.cli.vyper_json import compile_from_input_dict, compile_json
-from vyper.exceptions import JSONError
-
-FOO_CODE = """
-import contracts.bar as Bar
-
-@external
-def foo(a: address) -> bool:
- return Bar(a).bar(1)
-"""
-
-BAR_CODE = """
-@external
-def bar(a: uint256) -> bool:
- return True
-"""
-
-BAR_ABI = [
- {
- "name": "bar",
- "outputs": [{"type": "bool", "name": "out"}],
- "inputs": [{"type": "uint256", "name": "a"}],
- "stateMutability": "nonpayable",
- "type": "function",
- "gas": 313,
- }
-]
-
-INPUT_JSON = {
- "language": "Vyper",
- "sources": {
- "contracts/foo.vy": {"content": FOO_CODE},
- "contracts/bar.vy": {"content": BAR_CODE},
- },
- "interfaces": {"contracts/bar.json": {"abi": BAR_ABI}},
- "settings": {"outputSelection": {"*": ["*"]}},
-}
-
-
-def test_input_formats():
- assert compile_json(INPUT_JSON) == compile_json(json.dumps(INPUT_JSON))
-
-
-def test_bad_json():
- with pytest.raises(JSONError):
- compile_json("this probably isn't valid JSON, is it")
-
-
-def test_keyerror_becomes_jsonerror():
- input_json = deepcopy(INPUT_JSON)
- del input_json["sources"]
- with pytest.raises(KeyError):
- compile_from_input_dict(input_json)
- with pytest.raises(JSONError):
- compile_json(input_json)
diff --git a/tests/cli/vyper_json/test_get_contracts.py b/tests/cli/vyper_json/test_get_contracts.py
deleted file mode 100644
index 86a5052f72..0000000000
--- a/tests/cli/vyper_json/test_get_contracts.py
+++ /dev/null
@@ -1,71 +0,0 @@
-#!/usr/bin/env python3
-
-import pytest
-
-from vyper.cli.vyper_json import get_input_dict_contracts
-from vyper.exceptions import JSONError
-from vyper.utils import keccak256
-
-FOO_CODE = """
-import contracts.bar as Bar
-
-@external
-def foo(a: address) -> bool:
- return Bar(a).bar(1)
-"""
-
-BAR_CODE = """
-@external
-def bar(a: uint256) -> bool:
- return True
-"""
-
-
-def test_no_sources():
- with pytest.raises(KeyError):
- get_input_dict_contracts({})
-
-
-def test_contracts_urls():
- with pytest.raises(JSONError):
- get_input_dict_contracts({"sources": {"foo.vy": {"urls": ["https://foo.code.com/"]}}})
-
-
-def test_contracts_no_content_key():
- with pytest.raises(JSONError):
- get_input_dict_contracts({"sources": {"foo.vy": FOO_CODE}})
-
-
-def test_contracts_keccak():
- hash_ = keccak256(FOO_CODE.encode()).hex()
-
- input_json = {"sources": {"foo.vy": {"content": FOO_CODE, "keccak256": hash_}}}
- get_input_dict_contracts(input_json)
-
- input_json["sources"]["foo.vy"]["keccak256"] = "0x" + hash_
- get_input_dict_contracts(input_json)
-
- input_json["sources"]["foo.vy"]["keccak256"] = "0x1234567890"
- with pytest.raises(JSONError):
- get_input_dict_contracts(input_json)
-
-
-def test_contracts_bad_path():
- input_json = {"sources": {"../foo.vy": {"content": FOO_CODE}}}
- with pytest.raises(JSONError):
- get_input_dict_contracts(input_json)
-
-
-def test_contract_collision():
- # ./foo.vy and foo.vy will resolve to the same path
- input_json = {"sources": {"./foo.vy": {"content": FOO_CODE}, "foo.vy": {"content": FOO_CODE}}}
- with pytest.raises(JSONError):
- get_input_dict_contracts(input_json)
-
-
-def test_contracts_return_value():
- input_json = {
- "sources": {"foo.vy": {"content": FOO_CODE}, "contracts/bar.vy": {"content": BAR_CODE}}
- }
- result = get_input_dict_contracts(input_json)
- assert result == {"foo.vy": FOO_CODE, "contracts/bar.vy": BAR_CODE}
diff --git a/tests/cli/vyper_json/test_interfaces.py b/tests/cli/vyper_json/test_interfaces.py
deleted file mode 100644
index 7804ae1c3d..0000000000
--- a/tests/cli/vyper_json/test_interfaces.py
+++ /dev/null
@@ -1,126 +0,0 @@
-#!/usr/bin/env python3
-
-import pytest
-
-from vyper.cli.vyper_json import get_input_dict_interfaces, get_interface_codes
-from vyper.exceptions import JSONError
-
-FOO_CODE = """
-import contracts.bar as Bar
-
-@external
-def foo(a: address) -> bool:
- return Bar(a).bar(1)
-"""
-
-BAR_CODE = """
-@external
-def bar(a: uint256) -> bool:
- return True
-"""
-
-BAR_ABI = [
- {
- "name": "bar",
- "outputs": [{"type": "bool", "name": "out"}],
- "inputs": [{"type": "uint256", "name": "a"}],
- "stateMutability": "nonpayable",
- "type": "function",
- "gas": 313,
- }
-]
-
-
-# get_input_dict_interfaces tests
-
-
-def test_no_interfaces():
- result = get_input_dict_interfaces({})
- assert isinstance(result, dict)
- assert not result
-
-
-def test_interface_collision():
- input_json = {"interfaces": {"bar.json": {"abi": BAR_ABI}, "bar.vy": {"content": BAR_CODE}}}
- with pytest.raises(JSONError):
- get_input_dict_interfaces(input_json)
-
-
-def test_interfaces_wrong_suffix():
- input_json = {"interfaces": {"foo.abi": {"content": FOO_CODE}}}
- with pytest.raises(JSONError):
- get_input_dict_interfaces(input_json)
-
- input_json = {"interfaces": {"interface.folder/foo": {"content": FOO_CODE}}}
- with pytest.raises(JSONError):
- get_input_dict_interfaces(input_json)
-
-
-def test_json_no_abi():
- input_json = {"interfaces": {"bar.json": {"content": BAR_ABI}}}
- with pytest.raises(JSONError):
- get_input_dict_interfaces(input_json)
-
-
-def test_vy_no_content():
- input_json = {"interfaces": {"bar.vy": {"abi": BAR_CODE}}}
- with pytest.raises(JSONError):
- get_input_dict_interfaces(input_json)
-
-
-def test_interfaces_output():
- input_json = {
- "interfaces": {
- "bar.json": {"abi": BAR_ABI},
- "interface.folder/bar2.vy": {"content": BAR_CODE},
- }
- }
- result = get_input_dict_interfaces(input_json)
- assert isinstance(result, dict)
- assert result == {
- "bar": {"type": "json", "code": BAR_ABI},
- "interface.folder/bar2": {"type": "vyper", "code": BAR_CODE},
- }
-
-
-def test_manifest_output():
- input_json = {"interfaces": {"bar.json": {"contractTypes": {"Bar": {"abi": BAR_ABI}}}}}
- result = get_input_dict_interfaces(input_json)
- assert isinstance(result, dict)
- assert result == {"Bar": {"type": "json", "code": BAR_ABI}}
-
-
-# get_interface_codes tests
-
-
-def test_interface_codes_from_contracts():
- # interface should be generated from contract
- assert get_interface_codes(
- None, "foo.vy", {"foo.vy": FOO_CODE, "contracts/bar.vy": BAR_CODE}, {}
- )
- assert get_interface_codes(
- None, "foo/foo.vy", {"foo/foo.vy": FOO_CODE, "contracts/bar.vy": BAR_CODE}, {}
- )
-
-
-def test_interface_codes_from_interfaces():
- # existing interface should be given preference over contract-as-interface
- contracts = {"foo.vy": FOO_CODE, "contacts/bar.vy": BAR_CODE}
- result = get_interface_codes(None, "foo.vy", contracts, {"contracts/bar": "bar"})
- assert result["Bar"] == "bar"
-
-
-def test_root_path(tmp_path):
- tmp_path.joinpath("contracts").mkdir()
- with tmp_path.joinpath("contracts/bar.vy").open("w") as fp:
- fp.write("bar")
-
- with pytest.raises(FileNotFoundError):
- get_interface_codes(None, "foo.vy", {"foo.vy": FOO_CODE}, {})
-
- # interface from file system should take lowest priority
- result = get_interface_codes(tmp_path, "foo.vy", {"foo.vy": FOO_CODE}, {})
- assert result["Bar"] == {"code": "bar", "type": "vyper"}
- contracts = {"foo.vy": FOO_CODE, "contracts/bar.vy": BAR_CODE}
- result = get_interface_codes(None, "foo.vy", contracts, {})
- assert result["Bar"] == {"code": BAR_CODE, "type": "vyper"}
diff --git a/tests/cli/vyper_json/test_output_dict.py b/tests/cli/vyper_json/test_output_dict.py
deleted file mode 100644
index e2a3466ccf..0000000000
--- a/tests/cli/vyper_json/test_output_dict.py
+++ /dev/null
@@ -1,38 +0,0 @@
-#!/usr/bin/env python3
-
-import vyper
-from vyper.cli.vyper_json import format_to_output_dict
-from vyper.compiler import OUTPUT_FORMATS, compile_codes
-
-FOO_CODE = """
-@external
-def foo() -> bool:
- return True
-"""
-
-
-def test_keys():
- compiler_data = compile_codes({"foo.vy": FOO_CODE}, output_formats=list(OUTPUT_FORMATS.keys()))
- output_json = format_to_output_dict(compiler_data)
- assert sorted(output_json.keys()) == ["compiler", "contracts", "sources"]
- assert output_json["compiler"] == f"vyper-{vyper.__version__}"
- data = compiler_data["foo.vy"]
- assert output_json["sources"]["foo.vy"] == {"id": 0, "ast": data["ast_dict"]["ast"]}
- assert output_json["contracts"]["foo.vy"]["foo"] == {
- "abi": data["abi"],
- "devdoc": data["devdoc"],
- "interface": data["interface"],
- "ir": data["ir_dict"],
- "userdoc": data["userdoc"],
- "metadata": data["metadata"],
- "evm": {
- "bytecode": {"object": data["bytecode"], "opcodes": data["opcodes"]},
- "deployedBytecode": {
- "object": data["bytecode_runtime"],
- "opcodes": data["opcodes_runtime"],
- "sourceMap": data["source_map"]["pc_pos_map_compressed"],
- "sourceMapFull": data["source_map_full"],
- },
- "methodIdentifiers": data["method_identifiers"],
- },
- }
diff --git a/tests/cli/vyper_json/test_output_selection.py b/tests/cli/vyper_json/test_output_selection.py
deleted file mode 100644
index c72f06f5a7..0000000000
--- a/tests/cli/vyper_json/test_output_selection.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python3
-
-import pytest
-
-from vyper.cli.vyper_json import TRANSLATE_MAP, get_input_dict_output_formats
-from vyper.exceptions import JSONError
-
-
-def test_no_outputs():
- with pytest.raises(KeyError):
- get_input_dict_output_formats({}, {})
-
-
-def test_invalid_output():
- input_json = {"settings": {"outputSelection": {"foo.vy": ["abi", "foobar"]}}}
- sources = {"foo.vy": ""}
- with pytest.raises(JSONError):
- get_input_dict_output_formats(input_json, sources)
-
-
-def test_unknown_contract():
- input_json = {"settings": {"outputSelection": {"bar.vy": ["abi"]}}}
- sources = {"foo.vy": ""}
- with pytest.raises(JSONError):
- get_input_dict_output_formats(input_json, sources)
-
-
-@pytest.mark.parametrize("output", TRANSLATE_MAP.items())
-def test_translate_map(output):
- input_json = {"settings": {"outputSelection": {"foo.vy": [output[0]]}}}
- sources = {"foo.vy": ""}
- assert get_input_dict_output_formats(input_json, sources) == {"foo.vy": [output[1]]}
-
-
-def test_star():
- input_json = {"settings": {"outputSelection": {"*": ["*"]}}}
- sources = {"foo.vy": "", "bar.vy": ""}
- expected = sorted(set(TRANSLATE_MAP.values()))
- result = get_input_dict_output_formats(input_json, sources)
- assert result == {"foo.vy": expected, "bar.vy": expected}
-
-
-def test_evm():
- input_json = {"settings": {"outputSelection": {"foo.vy": ["abi", "evm"]}}}
- sources = {"foo.vy": ""}
- expected = ["abi"] + sorted(v for k, v in TRANSLATE_MAP.items() if k.startswith("evm"))
- result = get_input_dict_output_formats(input_json, sources)
- assert result == {"foo.vy": expected}
-
-
-def test_solc_style():
- input_json = {"settings": {"outputSelection": {"foo.vy": {"": ["abi"], "foo.vy": ["ir"]}}}}
- sources = {"foo.vy": ""}
- assert get_input_dict_output_formats(input_json, sources) == {"foo.vy": ["abi", "ir_dict"]}
diff --git a/tests/conftest.py b/tests/conftest.py
index d519ca3100..51b4b4459a 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,28 +1,40 @@
+import json
import logging
+from contextlib import contextmanager
from functools import wraps
+import hypothesis
import pytest
+import web3.exceptions
from eth_tester import EthereumTester, PyEVMBackend
+from eth_tester.exceptions import TransactionFailed
from eth_utils import setup_DEBUG2_logging
+from eth_utils.toolz import compose
from hexbytes import HexBytes
from web3 import Web3
+from web3.contract import Contract
from web3.providers.eth_tester import EthereumTesterProvider
from vyper import compiler
+from vyper.ast.grammar import parse_vyper_source
from vyper.codegen.ir_node import IRnode
-from vyper.compiler.settings import OptimizationLevel, _set_debug_mode
+from vyper.compiler.input_bundle import FilesystemInputBundle, InputBundle
+from vyper.compiler.settings import OptimizationLevel, Settings, _set_debug_mode
from vyper.ir import compile_ir, optimizer
-from .base_conftest import VyperContract, _get_contract, zero_gas_price_strategy
-
-# Import the base_conftest fixtures
-pytest_plugins = ["tests.base_conftest", "tests.fixtures.memorymock"]
+# Import the base fixtures
+pytest_plugins = ["tests.fixtures.memorymock"]
############
# PATCHING #
############
+# disable hypothesis deadline globally
+hypothesis.settings.register_profile("ci", deadline=None)
+hypothesis.settings.load_profile("ci")
+
+
def set_evm_verbose_logging():
logger = logging.getLogger("eth.vm.computation.Computation")
setup_DEBUG2_logging()
@@ -64,6 +76,42 @@ def keccak():
return Web3.keccak
+@pytest.fixture
+def make_file(tmp_path):
+ # writes file_contents to file_name, creating it in the
+ # tmp_path directory. returns final path.
+ def fn(file_name, file_contents):
+ path = tmp_path / file_name
+ path.parent.mkdir(parents=True, exist_ok=True)
+ with path.open("w") as f:
+ f.write(file_contents)
+
+ return path
+
+ return fn
+
+
+# this can either be used for its side effects (to prepare a call
+# to get_contract), or the result can be provided directly to
+# compile_code / CompilerData.
+@pytest.fixture
+def make_input_bundle(tmp_path, make_file):
+ def fn(sources_dict):
+ for file_name, file_contents in sources_dict.items():
+ make_file(file_name, file_contents)
+ return FilesystemInputBundle([tmp_path])
+
+ return fn
+
+
+# for tests which just need an input bundle, doesn't matter what it is
+@pytest.fixture
+def dummy_input_bundle():
+ return InputBundle([])
+
+
+# TODO: remove me, this is just string.encode("utf-8").ljust()
+# only used in test_logging.py.
@pytest.fixture
def bytes_helper():
def bytes_helper(str, length):
@@ -72,45 +120,35 @@ def bytes_helper(str, length):
return bytes_helper
-@pytest.fixture
-def get_contract_from_ir(w3, optimize):
- def ir_compiler(ir, *args, **kwargs):
- ir = IRnode.from_list(ir)
- if optimize != OptimizationLevel.NONE:
- ir = optimizer.optimize(ir)
- bytecode, _ = compile_ir.assembly_to_evm(
- compile_ir.compile_to_assembly(ir, optimize=optimize)
- )
- abi = kwargs.get("abi") or []
- c = w3.eth.contract(abi=abi, bytecode=bytecode)
- deploy_transaction = c.constructor()
- tx_hash = deploy_transaction.transact()
- address = w3.eth.get_transaction_receipt(tx_hash)["contractAddress"]
- contract = w3.eth.contract(
- address, abi=abi, bytecode=bytecode, ContractFactoryClass=VyperContract
- )
- return contract
+def _none_addr(datatype, data):
+ if datatype == "address" and int(data, base=16) == 0:
+ return (datatype, None)
+ else:
+ return (datatype, data)
- return ir_compiler
+
+CONCISE_NORMALIZERS = (_none_addr,)
@pytest.fixture(scope="module")
-def get_contract_module(optimize):
- """
- This fixture is used for Hypothesis tests to ensure that
- the same contract is called over multiple runs of the test.
- """
- custom_genesis = PyEVMBackend._generate_genesis_params(overrides={"gas_limit": 4500000})
+def tester():
+ # set absurdly high gas limit so that london basefee never adjusts
+ # (note: 2**63 - 1 is max that evm allows)
+ custom_genesis = PyEVMBackend._generate_genesis_params(overrides={"gas_limit": 10**10})
custom_genesis["base_fee_per_gas"] = 0
backend = PyEVMBackend(genesis_parameters=custom_genesis)
- tester = EthereumTester(backend=backend)
- w3 = Web3(EthereumTesterProvider(tester))
- w3.eth.set_gas_price_strategy(zero_gas_price_strategy)
+ return EthereumTester(backend=backend)
- def get_contract_module(source_code, *args, **kwargs):
- return _get_contract(w3, source_code, optimize, *args, **kwargs)
- return get_contract_module
+def zero_gas_price_strategy(web3, transaction_params=None):
+ return 0 # zero gas price makes testing simpler.
+
+
+@pytest.fixture(scope="module")
+def w3(tester):
+ w3 = Web3(EthereumTesterProvider(tester))
+ w3.eth.set_gas_price_strategy(zero_gas_price_strategy)
+ return w3
def get_compiler_gas_estimate(code, func):
@@ -152,6 +190,132 @@ def set_decorator_to_contract_function(w3, tester, contract, source_code, func):
setattr(contract, func, func_with_decorator)
+class VyperMethod:
+ ALLOWED_MODIFIERS = {"call", "estimateGas", "transact", "buildTransaction"}
+
+ def __init__(self, function, normalizers=None):
+ self._function = function
+ self._function._return_data_normalizers = normalizers
+
+ def __call__(self, *args, **kwargs):
+ return self.__prepared_function(*args, **kwargs)
+
+ def __prepared_function(self, *args, **kwargs):
+ if not kwargs:
+ modifier, modifier_dict = "call", {}
+ fn_abi = [
+ x
+ for x in self._function.contract_abi
+ if x.get("name") == self._function.function_identifier
+ ].pop()
+ # To make tests faster just supply some high gas value.
+ modifier_dict.update({"gas": fn_abi.get("gas", 0) + 500000})
+ elif len(kwargs) == 1:
+ modifier, modifier_dict = kwargs.popitem()
+ if modifier not in self.ALLOWED_MODIFIERS:
+ raise TypeError(f"The only allowed keyword arguments are: {self.ALLOWED_MODIFIERS}")
+ else:
+ raise TypeError(f"Use up to one keyword argument, one of: {self.ALLOWED_MODIFIERS}")
+ return getattr(self._function(*args), modifier)(modifier_dict)
+
+
+class VyperContract:
+ """
+ An alternative Contract Factory which invokes all methods as `call()`,
+ unless you add a keyword argument. The keyword argument assigns the prep method.
+ This call
+ > contract.withdraw(amount, transact={'from': eth.accounts[1], 'gas': 100000, ...})
+ is equivalent to this call in the classic contract:
+ > contract.functions.withdraw(amount).transact({'from': eth.accounts[1], 'gas': 100000, ...})
+ """
+
+ def __init__(self, classic_contract, method_class=VyperMethod):
+ classic_contract._return_data_normalizers += CONCISE_NORMALIZERS
+ self._classic_contract = classic_contract
+ self.address = self._classic_contract.address
+ protected_fn_names = [fn for fn in dir(self) if not fn.endswith("__")]
+
+ try:
+ fn_names = [fn["name"] for fn in self._classic_contract.functions._functions]
+ except web3.exceptions.NoABIFunctionsFound:
+ fn_names = []
+
+ for fn_name in fn_names:
+ # Override namespace collisions
+ if fn_name in protected_fn_names:
+ raise AttributeError(f"{fn_name} is protected!")
+ else:
+ _classic_method = getattr(self._classic_contract.functions, fn_name)
+ _concise_method = method_class(
+ _classic_method, self._classic_contract._return_data_normalizers
+ )
+ setattr(self, fn_name, _concise_method)
+
+ @classmethod
+ def factory(cls, *args, **kwargs):
+ return compose(cls, Contract.factory(*args, **kwargs))
+
+
+@pytest.fixture
+def get_contract_from_ir(w3, optimize):
+ def ir_compiler(ir, *args, **kwargs):
+ ir = IRnode.from_list(ir)
+ if optimize != OptimizationLevel.NONE:
+ ir = optimizer.optimize(ir)
+
+ bytecode, _ = compile_ir.assembly_to_evm(
+ compile_ir.compile_to_assembly(ir, optimize=optimize)
+ )
+
+ abi = kwargs.get("abi") or []
+ c = w3.eth.contract(abi=abi, bytecode=bytecode)
+ deploy_transaction = c.constructor()
+ tx_hash = deploy_transaction.transact()
+ address = w3.eth.get_transaction_receipt(tx_hash)["contractAddress"]
+ contract = w3.eth.contract(
+ address, abi=abi, bytecode=bytecode, ContractFactoryClass=VyperContract
+ )
+ return contract
+
+ return ir_compiler
+
+
+def _get_contract(
+ w3, source_code, optimize, *args, override_opt_level=None, input_bundle=None, **kwargs
+):
+ settings = Settings()
+ settings.evm_version = kwargs.pop("evm_version", None)
+ settings.optimize = override_opt_level or optimize
+ out = compiler.compile_code(
+ source_code,
+ # test that all output formats can get generated
+ output_formats=list(compiler.OUTPUT_FORMATS.keys()),
+ settings=settings,
+ input_bundle=input_bundle,
+ show_gas_estimates=True, # Enable gas estimates for testing
+ )
+ parse_vyper_source(source_code) # Test grammar.
+ json.dumps(out["metadata"]) # test metadata is json serializable
+ abi = out["abi"]
+ bytecode = out["bytecode"]
+ value = kwargs.pop("value_in_eth", 0) * 10**18 # Handle deploying with an eth value.
+ c = w3.eth.contract(abi=abi, bytecode=bytecode)
+ deploy_transaction = c.constructor(*args)
+ tx_info = {"from": w3.eth.accounts[0], "value": value, "gasPrice": 0}
+ tx_info.update(kwargs)
+ tx_hash = deploy_transaction.transact(tx_info)
+ address = w3.eth.get_transaction_receipt(tx_hash)["contractAddress"]
+ return w3.eth.contract(address, abi=abi, bytecode=bytecode, ContractFactoryClass=VyperContract)
+
+
+@pytest.fixture(scope="module")
+def get_contract(w3, optimize):
+ def fn(source_code, *args, **kwargs):
+ return _get_contract(w3, source_code, optimize, *args, **kwargs)
+
+ return fn
+
+
@pytest.fixture
def get_contract_with_gas_estimation(tester, w3, optimize):
def get_contract_with_gas_estimation(source_code, *args, **kwargs):
@@ -172,6 +336,73 @@ def get_contract_with_gas_estimation_for_constants(source_code, *args, **kwargs)
return get_contract_with_gas_estimation_for_constants
+@pytest.fixture(scope="module")
+def get_contract_module(optimize):
+ """
+ This fixture is used for Hypothesis tests to ensure that
+ the same contract is called over multiple runs of the test.
+ """
+ custom_genesis = PyEVMBackend._generate_genesis_params(overrides={"gas_limit": 4500000})
+ custom_genesis["base_fee_per_gas"] = 0
+ backend = PyEVMBackend(genesis_parameters=custom_genesis)
+ tester = EthereumTester(backend=backend)
+ w3 = Web3(EthereumTesterProvider(tester))
+ w3.eth.set_gas_price_strategy(zero_gas_price_strategy)
+
+ def get_contract_module(source_code, *args, **kwargs):
+ return _get_contract(w3, source_code, optimize, *args, **kwargs)
+
+ return get_contract_module
+
+
+def _deploy_blueprint_for(w3, source_code, optimize, initcode_prefix=b"", **kwargs):
+ settings = Settings()
+ settings.evm_version = kwargs.pop("evm_version", None)
+ settings.optimize = optimize
+ out = compiler.compile_code(
+ source_code,
+ output_formats=list(compiler.OUTPUT_FORMATS.keys()),
+ settings=settings,
+ show_gas_estimates=True, # Enable gas estimates for testing
+ )
+ parse_vyper_source(source_code) # Test grammar.
+ abi = out["abi"]
+ bytecode = HexBytes(initcode_prefix) + HexBytes(out["bytecode"])
+ bytecode_len = len(bytecode)
+ bytecode_len_hex = hex(bytecode_len)[2:].rjust(4, "0")
+ # prepend a quick deploy preamble
+ deploy_preamble = HexBytes("61" + bytecode_len_hex + "3d81600a3d39f3")
+ deploy_bytecode = HexBytes(deploy_preamble) + bytecode
+
+ deployer_abi = [] # just a constructor
+ c = w3.eth.contract(abi=deployer_abi, bytecode=deploy_bytecode)
+ deploy_transaction = c.constructor()
+ tx_info = {"from": w3.eth.accounts[0], "value": 0, "gasPrice": 0}
+
+ tx_hash = deploy_transaction.transact(tx_info)
+ address = w3.eth.get_transaction_receipt(tx_hash)["contractAddress"]
+
+ # sanity check
+ assert w3.eth.get_code(address) == bytecode, (w3.eth.get_code(address), bytecode)
+
+ def factory(address):
+ return w3.eth.contract(
+ address, abi=abi, bytecode=bytecode, ContractFactoryClass=VyperContract
+ )
+
+ return w3.eth.contract(address, bytecode=deploy_bytecode), factory
+
+
+@pytest.fixture(scope="module")
+def deploy_blueprint_for(w3, optimize):
+ def deploy_blueprint_for(source_code, *args, **kwargs):
+ return _deploy_blueprint_for(w3, source_code, optimize, *args, **kwargs)
+
+ return deploy_blueprint_for
+
+
+# TODO: this should not be a fixture.
+# remove me and replace all uses with `with pytest.raises`.
@pytest.fixture
def assert_compile_failed():
def assert_compile_failed(function_to_test, exception=Exception):
@@ -181,22 +412,6 @@ def assert_compile_failed(function_to_test, exception=Exception):
return assert_compile_failed
-@pytest.fixture
-def search_for_sublist():
- def search_for_sublist(ir, sublist):
- _list = ir.to_list() if hasattr(ir, "to_list") else ir
- if _list == sublist:
- return True
- if isinstance(_list, list):
- for i in _list:
- ret = search_for_sublist(i, sublist)
- if ret is True:
- return ret
- return False
-
- return search_for_sublist
-
-
@pytest.fixture
def create2_address_of(keccak):
def _f(_addr, _salt, _initcode):
@@ -242,3 +457,27 @@ def assert_side_effects_invoked(side_effects_contract, side_effects_trigger, n=1
assert end_value == start_value + n
return assert_side_effects_invoked
+
+
+@pytest.fixture
+def get_logs(w3):
+ def get_logs(tx_hash, c, event_name):
+ tx_receipt = w3.eth.get_transaction_receipt(tx_hash)
+ return c._classic_contract.events[event_name]().process_receipt(tx_receipt)
+
+ return get_logs
+
+
+@pytest.fixture(scope="module")
+def tx_failed(tester):
+ @contextmanager
+ def fn(exception=TransactionFailed, exc_text=None):
+ snapshot_id = tester.take_snapshot()
+ with pytest.raises(exception) as excinfo:
+ yield excinfo
+ tester.revert_to_snapshot(snapshot_id)
+ if exc_text:
+ # TODO test equality
+ assert exc_text in str(excinfo.value), (exc_text, excinfo.value)
+
+ return fn
diff --git a/tests/compiler/ir/__init__.py b/tests/functional/__init__.py
similarity index 100%
rename from tests/compiler/ir/__init__.py
rename to tests/functional/__init__.py
diff --git a/tests/parser/exceptions/__init__.py b/tests/functional/builtins/codegen/__init__.py
similarity index 100%
rename from tests/parser/exceptions/__init__.py
rename to tests/functional/builtins/codegen/__init__.py
diff --git a/tests/parser/functions/test_abi.py b/tests/functional/builtins/codegen/test_abi.py
similarity index 100%
rename from tests/parser/functions/test_abi.py
rename to tests/functional/builtins/codegen/test_abi.py
diff --git a/tests/parser/functions/test_abi_decode.py b/tests/functional/builtins/codegen/test_abi_decode.py
similarity index 88%
rename from tests/parser/functions/test_abi_decode.py
rename to tests/functional/builtins/codegen/test_abi_decode.py
index 2f9b93057d..69bfef63ea 100644
--- a/tests/parser/functions/test_abi_decode.py
+++ b/tests/functional/builtins/codegen/test_abi_decode.py
@@ -25,7 +25,7 @@ def test_abi_decode_complex(get_contract):
@external
def abi_decode(x: Bytes[160]) -> (address, int128, bool, decimal, bytes32):
- a: address = ZERO_ADDRESS
+ a: address = empty(address)
b: int128 = 0
c: bool = False
d: decimal = 0.0
@@ -39,7 +39,7 @@ def abi_decode_struct(x: Bytes[544]) -> Human:
name: "",
pet: Animal({
name: "",
- address_: ZERO_ADDRESS,
+ address_: empty(address),
id_: 0,
is_furry: False,
price: 0.0,
@@ -331,7 +331,7 @@ def abi_decode(x: Bytes[32]) -> uint256:
b"\x01" * 96, # Length of byte array is beyond size bound of output type
],
)
-def test_clamper(get_contract, assert_tx_failed, input_):
+def test_clamper(get_contract, tx_failed, input_):
contract = """
@external
def abi_decode(x: Bytes[96]) -> (uint256, uint256):
@@ -341,7 +341,38 @@ def abi_decode(x: Bytes[96]) -> (uint256, uint256):
return a, b
"""
c = get_contract(contract)
- assert_tx_failed(lambda: c.abi_decode(input_))
+ with tx_failed():
+ c.abi_decode(input_)
+
+
+def test_clamper_nested_uint8(get_contract, tx_failed):
+ # check that _abi_decode clamps on word-types even when it is in a nested expression
+ # decode -> validate uint8 -> revert if input >= 256 -> cast back to uint256
+ contract = """
+@external
+def abi_decode(x: uint256) -> uint256:
+ a: uint256 = convert(_abi_decode(slice(msg.data, 4, 32), (uint8)), uint256)
+ return a
+ """
+ c = get_contract(contract)
+ assert c.abi_decode(255) == 255
+ with tx_failed():
+ c.abi_decode(256)
+
+
+def test_clamper_nested_bytes(get_contract, tx_failed):
+ # check that _abi_decode clamps dynamic even when it is in a nested expression
+ # decode -> validate Bytes[20] -> revert if len(input) > 20 -> convert back to -> add 1
+ contract = """
+@external
+def abi_decode(x: Bytes[96]) -> Bytes[21]:
+ a: Bytes[21] = concat(b"a", _abi_decode(x, Bytes[20]))
+ return a
+ """
+ c = get_contract(contract)
+ assert c.abi_decode(abi.encode("(bytes)", (b"bc",))) == b"abc"
+ with tx_failed():
+ c.abi_decode(abi.encode("(bytes)", (b"a" * 22,)))
@pytest.mark.parametrize(
@@ -353,7 +384,7 @@ def abi_decode(x: Bytes[96]) -> (uint256, uint256):
("Bytes[5]", b"\x01" * 192),
],
)
-def test_clamper_dynamic(get_contract, assert_tx_failed, output_typ, input_):
+def test_clamper_dynamic(get_contract, tx_failed, output_typ, input_):
contract = f"""
@external
def abi_decode(x: Bytes[192]) -> {output_typ}:
@@ -362,7 +393,8 @@ def abi_decode(x: Bytes[192]) -> {output_typ}:
return a
"""
c = get_contract(contract)
- assert_tx_failed(lambda: c.abi_decode(input_))
+ with tx_failed():
+ c.abi_decode(input_)
@pytest.mark.parametrize(
@@ -394,7 +426,7 @@ def abi_decode(x: Bytes[160]) -> uint256:
("Bytes[5]", "address", b"\x01" * 128),
],
)
-def test_clamper_dynamic_tuple(get_contract, assert_tx_failed, output_typ1, output_typ2, input_):
+def test_clamper_dynamic_tuple(get_contract, tx_failed, output_typ1, output_typ2, input_):
contract = f"""
@external
def abi_decode(x: Bytes[224]) -> ({output_typ1}, {output_typ2}):
@@ -404,7 +436,8 @@ def abi_decode(x: Bytes[224]) -> ({output_typ1}, {output_typ2}):
return a, b
"""
c = get_contract(contract)
- assert_tx_failed(lambda: c.abi_decode(input_))
+ with tx_failed():
+ c.abi_decode(input_)
FAIL_LIST = [
diff --git a/tests/parser/functions/test_abi_encode.py b/tests/functional/builtins/codegen/test_abi_encode.py
similarity index 100%
rename from tests/parser/functions/test_abi_encode.py
rename to tests/functional/builtins/codegen/test_abi_encode.py
diff --git a/tests/parser/functions/test_addmod.py b/tests/functional/builtins/codegen/test_addmod.py
similarity index 93%
rename from tests/parser/functions/test_addmod.py
rename to tests/functional/builtins/codegen/test_addmod.py
index b3135660bb..00745c0cdb 100644
--- a/tests/parser/functions/test_addmod.py
+++ b/tests/functional/builtins/codegen/test_addmod.py
@@ -1,4 +1,4 @@
-def test_uint256_addmod(assert_tx_failed, get_contract_with_gas_estimation):
+def test_uint256_addmod(tx_failed, get_contract_with_gas_estimation):
uint256_code = """
@external
def _uint256_addmod(x: uint256, y: uint256, z: uint256) -> uint256:
@@ -11,7 +11,8 @@ def _uint256_addmod(x: uint256, y: uint256, z: uint256) -> uint256:
assert c._uint256_addmod(32, 2, 32) == 2
assert c._uint256_addmod((2**256) - 1, 0, 2) == 1
assert c._uint256_addmod(2**255, 2**255, 6) == 4
- assert_tx_failed(lambda: c._uint256_addmod(1, 2, 0))
+ with tx_failed():
+ c._uint256_addmod(1, 2, 0)
def test_uint256_addmod_ext_call(
diff --git a/tests/parser/types/value/test_as_wei_value.py b/tests/functional/builtins/codegen/test_as_wei_value.py
similarity index 60%
rename from tests/parser/types/value/test_as_wei_value.py
rename to tests/functional/builtins/codegen/test_as_wei_value.py
index 249ac4b2ff..522684fa05 100644
--- a/tests/parser/types/value/test_as_wei_value.py
+++ b/tests/functional/builtins/codegen/test_as_wei_value.py
@@ -23,7 +23,7 @@
@pytest.mark.parametrize("denom,multiplier", wei_denoms.items())
-def test_wei_uint256(get_contract, assert_tx_failed, denom, multiplier):
+def test_wei_uint256(get_contract, tx_failed, denom, multiplier):
code = f"""
@external
def foo(a: uint256) -> uint256:
@@ -36,11 +36,12 @@ def foo(a: uint256) -> uint256:
assert c.foo(value) == value * (10**multiplier)
value = (2**256 - 1) // (10 ** (multiplier - 1))
- assert_tx_failed(lambda: c.foo(value))
+ with tx_failed():
+ c.foo(value)
@pytest.mark.parametrize("denom,multiplier", wei_denoms.items())
-def test_wei_int128(get_contract, assert_tx_failed, denom, multiplier):
+def test_wei_int128(get_contract, tx_failed, denom, multiplier):
code = f"""
@external
def foo(a: int128) -> uint256:
@@ -54,7 +55,7 @@ def foo(a: int128) -> uint256:
@pytest.mark.parametrize("denom,multiplier", wei_denoms.items())
-def test_wei_decimal(get_contract, assert_tx_failed, denom, multiplier):
+def test_wei_decimal(get_contract, tx_failed, denom, multiplier):
code = f"""
@external
def foo(a: decimal) -> uint256:
@@ -69,7 +70,7 @@ def foo(a: decimal) -> uint256:
@pytest.mark.parametrize("value", (-1, -(2**127)))
@pytest.mark.parametrize("data_type", ["decimal", "int128"])
-def test_negative_value_reverts(get_contract, assert_tx_failed, value, data_type):
+def test_negative_value_reverts(get_contract, tx_failed, value, data_type):
code = f"""
@external
def foo(a: {data_type}) -> uint256:
@@ -77,12 +78,13 @@ def foo(a: {data_type}) -> uint256:
"""
c = get_contract(code)
- assert_tx_failed(lambda: c.foo(value))
+ with tx_failed():
+ c.foo(value)
@pytest.mark.parametrize("denom,multiplier", wei_denoms.items())
@pytest.mark.parametrize("data_type", ["decimal", "int128", "uint256"])
-def test_zero_value(get_contract, assert_tx_failed, denom, multiplier, data_type):
+def test_zero_value(get_contract, tx_failed, denom, multiplier, data_type):
code = f"""
@external
def foo(a: {data_type}) -> uint256:
@@ -91,3 +93,36 @@ def foo(a: {data_type}) -> uint256:
c = get_contract(code)
assert c.foo(0) == 0
+
+
+def test_ext_call(w3, side_effects_contract, assert_side_effects_invoked, get_contract):
+ code = """
+@external
+def foo(a: Foo) -> uint256:
+ return as_wei_value(a.foo(7), "ether")
+
+interface Foo:
+ def foo(x: uint8) -> uint8: nonpayable
+ """
+
+ c1 = side_effects_contract("uint8")
+ c2 = get_contract(code)
+
+ assert c2.foo(c1.address) == w3.to_wei(7, "ether")
+ assert_side_effects_invoked(c1, lambda: c2.foo(c1.address, transact={}))
+
+
+def test_internal_call(w3, get_contract_with_gas_estimation):
+ code = """
+@external
+def foo() -> uint256:
+ return as_wei_value(self.bar(), "ether")
+
+@internal
+def bar() -> uint8:
+ return 7
+ """
+
+ c = get_contract_with_gas_estimation(code)
+
+ assert c.foo() == w3.to_wei(7, "ether")
diff --git a/tests/parser/functions/test_bitwise.py b/tests/functional/builtins/codegen/test_bitwise.py
similarity index 98%
rename from tests/parser/functions/test_bitwise.py
rename to tests/functional/builtins/codegen/test_bitwise.py
index 3ba74034ac..1d62a5be79 100644
--- a/tests/parser/functions/test_bitwise.py
+++ b/tests/functional/builtins/codegen/test_bitwise.py
@@ -32,7 +32,7 @@ def _shr(x: uint256, y: uint256) -> uint256:
def test_bitwise_opcodes():
- opcodes = compile_code(code, ["opcodes"])["opcodes"]
+ opcodes = compile_code(code, output_formats=["opcodes"])["opcodes"]
assert "SHL" in opcodes
assert "SHR" in opcodes
diff --git a/tests/parser/functions/test_ceil.py b/tests/functional/builtins/codegen/test_ceil.py
similarity index 100%
rename from tests/parser/functions/test_ceil.py
rename to tests/functional/builtins/codegen/test_ceil.py
diff --git a/tests/parser/functions/test_concat.py b/tests/functional/builtins/codegen/test_concat.py
similarity index 100%
rename from tests/parser/functions/test_concat.py
rename to tests/functional/builtins/codegen/test_concat.py
diff --git a/tests/parser/functions/test_convert.py b/tests/functional/builtins/codegen/test_convert.py
similarity index 94%
rename from tests/parser/functions/test_convert.py
rename to tests/functional/builtins/codegen/test_convert.py
index eb8449447c..559e1448ef 100644
--- a/tests/parser/functions/test_convert.py
+++ b/tests/functional/builtins/codegen/test_convert.py
@@ -486,10 +486,10 @@ def test_memory_variable_convert(x: {i_typ}) -> {o_typ}:
@pytest.mark.parametrize("typ", ["uint8", "int128", "int256", "uint256"])
@pytest.mark.parametrize("val", [1, 2, 2**128, 2**256 - 1, 2**256 - 2])
-def test_enum_conversion(get_contract_with_gas_estimation, assert_compile_failed, val, typ):
+def test_flag_conversion(get_contract_with_gas_estimation, assert_compile_failed, val, typ):
roles = "\n ".join([f"ROLE_{i}" for i in range(256)])
contract = f"""
-enum Roles:
+flag Roles:
{roles}
@external
@@ -510,11 +510,11 @@ def bar(a: uint256) -> Roles:
@pytest.mark.parametrize("typ", ["uint8", "int128", "int256", "uint256"])
@pytest.mark.parametrize("val", [1, 2, 3, 4, 2**128, 2**256 - 1, 2**256 - 2])
-def test_enum_conversion_2(
- get_contract_with_gas_estimation, assert_compile_failed, assert_tx_failed, val, typ
+def test_flag_conversion_2(
+ get_contract_with_gas_estimation, assert_compile_failed, tx_failed, val, typ
):
contract = f"""
-enum Status:
+flag Status:
STARTED
PAUSED
STOPPED
@@ -529,29 +529,12 @@ def foo(a: {typ}) -> Status:
if lo <= val <= hi:
assert c.foo(val) == val
else:
- assert_tx_failed(lambda: c.foo(val))
+ with tx_failed():
+ c.foo(val)
else:
assert_compile_failed(lambda: get_contract_with_gas_estimation(contract), TypeMismatch)
-# TODO CMC 2022-04-06 I think this test is somewhat unnecessary.
-@pytest.mark.parametrize(
- "builtin_constant,out_type,out_value",
- [("ZERO_ADDRESS", "bool", False), ("msg.sender", "bool", True)],
-)
-def test_convert_builtin_constant(
- get_contract_with_gas_estimation, builtin_constant, out_type, out_value
-):
- contract = f"""
-@external
-def convert_builtin_constant() -> {out_type}:
- return convert({builtin_constant}, {out_type})
- """
-
- c = get_contract_with_gas_estimation(contract)
- assert c.convert_builtin_constant() == out_value
-
-
# uint256 conversion is currently valid due to type inference on literals
# not quite working yet
same_type_conversion_blocked = sorted(TEST_TYPES - {UINT256_T})
@@ -626,7 +609,7 @@ def foo() -> {t_bytes}:
@pytest.mark.parametrize("i_typ,o_typ,val", generate_reverting_cases())
@pytest.mark.fuzzing
def test_conversion_failures(
- get_contract_with_gas_estimation, assert_compile_failed, assert_tx_failed, i_typ, o_typ, val
+ get_contract_with_gas_estimation, assert_compile_failed, tx_failed, i_typ, o_typ, val
):
"""
Test multiple contracts and check for a specific exception.
@@ -668,7 +651,8 @@ def foo():
"""
c2 = get_contract_with_gas_estimation(contract_2)
- assert_tx_failed(lambda: c2.foo())
+ with tx_failed():
+ c2.foo()
contract_3 = f"""
@external
@@ -677,4 +661,5 @@ def foo(bar: {i_typ}) -> {o_typ}:
"""
c3 = get_contract_with_gas_estimation(contract_3)
- assert_tx_failed(lambda: c3.foo(val))
+ with tx_failed():
+ c3.foo(val)
diff --git a/tests/parser/functions/test_create_functions.py b/tests/functional/builtins/codegen/test_create_functions.py
similarity index 64%
rename from tests/parser/functions/test_create_functions.py
rename to tests/functional/builtins/codegen/test_create_functions.py
index 876d50b27d..afa729ac8a 100644
--- a/tests/parser/functions/test_create_functions.py
+++ b/tests/functional/builtins/codegen/test_create_functions.py
@@ -77,7 +77,7 @@ def test2() -> Bytes[100]:
assert c.test2() == b"hello world!"
-def test_minimal_proxy_exception(w3, get_contract, assert_tx_failed):
+def test_minimal_proxy_exception(w3, get_contract, tx_failed):
code = """
interface SubContract:
@@ -111,7 +111,8 @@ def test2(a: uint256) -> Bytes[100]:
c.test(transact={})
assert c.test2(1) == b"hello world!"
- assert_tx_failed(lambda: c.test2(0))
+ with tx_failed():
+ c.test2(0)
GAS_SENT = 30000
tx_hash = c.test2(0, transact={"gas": GAS_SENT})
@@ -122,9 +123,7 @@ def test2(a: uint256) -> Bytes[100]:
assert receipt["gasUsed"] < GAS_SENT
-def test_create_minimal_proxy_to_create2(
- get_contract, create2_address_of, keccak, assert_tx_failed
-):
+def test_create_minimal_proxy_to_create2(get_contract, create2_address_of, keccak, tx_failed):
code = """
main: address
@@ -143,20 +142,15 @@ def test(_salt: bytes32) -> address:
c.test(salt, transact={})
# revert on collision
- assert_tx_failed(lambda: c.test(salt, transact={}))
+ with tx_failed():
+ c.test(salt, transact={})
# test blueprints with various prefixes - 0xfe would block calls to the blueprint
# contract, and 0xfe7100 is ERC5202 magic
@pytest.mark.parametrize("blueprint_prefix", [b"", b"\xfe", b"\xfe\71\x00"])
def test_create_from_blueprint(
- get_contract,
- deploy_blueprint_for,
- w3,
- keccak,
- create2_address_of,
- assert_tx_failed,
- blueprint_prefix,
+ get_contract, deploy_blueprint_for, w3, keccak, create2_address_of, tx_failed, blueprint_prefix
):
code = """
@external
@@ -193,7 +187,8 @@ def test2(target: address, salt: bytes32):
# extcodesize check
zero_address = "0x" + "00" * 20
- assert_tx_failed(lambda: d.test(zero_address))
+ with tx_failed():
+ d.test(zero_address)
# now same thing but with create2
salt = keccak(b"vyper")
@@ -209,11 +204,12 @@ def test2(target: address, salt: bytes32):
assert HexBytes(test.address) == create2_address_of(d.address, salt, initcode)
# can't collide addresses
- assert_tx_failed(lambda: d.test2(f.address, salt))
+ with tx_failed():
+ d.test2(f.address, salt)
def test_create_from_blueprint_bad_code_offset(
- get_contract, get_contract_from_ir, deploy_blueprint_for, w3, assert_tx_failed
+ get_contract, get_contract_from_ir, deploy_blueprint_for, w3, tx_failed
):
deployer_code = """
BLUEPRINT: immutable(address)
@@ -254,15 +250,17 @@ def test(code_ofst: uint256) -> address:
d.test(initcode_len - 1)
# code_offset=len(blueprint) NOT fine! would EXTCODECOPY empty initcode
- assert_tx_failed(lambda: d.test(initcode_len))
+ with tx_failed():
+ d.test(initcode_len)
# code_offset=EIP_170_LIMIT definitely not fine!
- assert_tx_failed(lambda: d.test(EIP_170_LIMIT))
+ with tx_failed():
+ d.test(EIP_170_LIMIT)
# test create_from_blueprint with args
def test_create_from_blueprint_args(
- get_contract, deploy_blueprint_for, w3, keccak, create2_address_of, assert_tx_failed
+ get_contract, deploy_blueprint_for, w3, keccak, create2_address_of, tx_failed
):
code = """
struct Bar:
@@ -332,7 +330,8 @@ def should_fail(target: address, arg1: String[129], arg2: Bar):
assert test.bar() == BAR
# extcodesize check
- assert_tx_failed(lambda: d.test("0x" + "00" * 20, FOO, BAR))
+ with tx_failed():
+ d.test("0x" + "00" * 20, FOO, BAR)
# now same thing but with create2
salt = keccak(b"vyper")
@@ -359,9 +358,11 @@ def should_fail(target: address, arg1: String[129], arg2: Bar):
assert test.bar() == BAR
# can't collide addresses
- assert_tx_failed(lambda: d.test2(f.address, FOO, BAR, salt))
+ with tx_failed():
+ d.test2(f.address, FOO, BAR, salt)
# ditto - with raw_args
- assert_tx_failed(lambda: d.test4(f.address, encoded_args, salt))
+ with tx_failed():
+ d.test4(f.address, encoded_args, salt)
# but creating a contract with different args is ok
FOO = "bar"
@@ -375,10 +376,11 @@ def should_fail(target: address, arg1: String[129], arg2: Bar):
BAR = ("",)
sig = keccak("should_fail(address,string,(string))".encode()).hex()[:10]
encoded = abi.encode("(address,string,(string))", (f.address, FOO, BAR)).hex()
- assert_tx_failed(lambda: w3.eth.send_transaction({"to": d.address, "data": f"{sig}{encoded}"}))
+ with tx_failed():
+ w3.eth.send_transaction({"to": d.address, "data": f"{sig}{encoded}"})
-def test_create_copy_of(get_contract, w3, keccak, create2_address_of, assert_tx_failed):
+def test_create_copy_of(get_contract, w3, keccak, create2_address_of, tx_failed):
code = """
created_address: public(address)
@internal
@@ -412,7 +414,8 @@ def test2(target: address, salt: bytes32) -> address:
assert w3.eth.get_code(test1) == bytecode
# extcodesize check
- assert_tx_failed(lambda: c.test("0x" + "00" * 20))
+ with tx_failed():
+ c.test("0x" + "00" * 20)
# test1 = c.test(b"\x01")
# assert w3.eth.get_code(test1) == b"\x01"
@@ -425,9 +428,220 @@ def test2(target: address, salt: bytes32) -> address:
assert HexBytes(test2) == create2_address_of(c.address, salt, vyper_initcode(bytecode))
# can't create2 where contract already exists
- assert_tx_failed(lambda: c.test2(c.address, salt, transact={}))
+ with tx_failed():
+ c.test2(c.address, salt, transact={})
# test single byte contract
# test2 = c.test2(b"\x01", salt)
# assert HexBytes(test2) == create2_address_of(c.address, salt, vyper_initcode(b"\x01"))
- # assert_tx_failed(lambda: c.test2(bytecode, salt))
+ # with tx_failed():
+ # c.test2(bytecode, salt)
+
+
+# XXX: these various tests to check the msize allocator for
+# create_copy_of and create_from_blueprint depend on calling convention
+# and variables writing to memory. think of ways to make more robust to
+# changes in calling convention and memory layout
+@pytest.mark.parametrize("blueprint_prefix", [b"", b"\xfe", b"\xfe\71\x00"])
+def test_create_from_blueprint_complex_value(
+ get_contract, deploy_blueprint_for, w3, blueprint_prefix
+):
+ # check msize allocator does not get trampled by value= kwarg
+ code = """
+var: uint256
+
+@external
+@payable
+def __init__(x: uint256):
+ self.var = x
+
+@external
+def foo()-> uint256:
+ return self.var
+ """
+
+ prefix_len = len(blueprint_prefix)
+
+ some_constant = b"\00" * 31 + b"\x0c"
+
+ deployer_code = f"""
+created_address: public(address)
+x: constant(Bytes[32]) = {some_constant}
+
+@internal
+def foo() -> uint256:
+ g:uint256 = 42
+ return 3
+
+@external
+@payable
+def test(target: address):
+ self.created_address = create_from_blueprint(
+ target,
+ x,
+ code_offset={prefix_len},
+ value=self.foo(),
+ raw_args=True
+ )
+ """
+
+ foo_contract = get_contract(code, 12)
+ expected_runtime_code = w3.eth.get_code(foo_contract.address)
+
+ f, FooContract = deploy_blueprint_for(code, initcode_prefix=blueprint_prefix)
+
+ d = get_contract(deployer_code)
+
+ d.test(f.address, transact={"value": 3})
+
+ test = FooContract(d.created_address())
+ assert w3.eth.get_code(test.address) == expected_runtime_code
+ assert test.foo() == 12
+
+
+@pytest.mark.parametrize("blueprint_prefix", [b"", b"\xfe", b"\xfe\71\x00"])
+def test_create_from_blueprint_complex_salt_raw_args(
+ get_contract, deploy_blueprint_for, w3, blueprint_prefix
+):
+ # test msize allocator does not get trampled by salt= kwarg
+ code = """
+var: uint256
+
+@external
+@payable
+def __init__(x: uint256):
+ self.var = x
+
+@external
+def foo()-> uint256:
+ return self.var
+ """
+
+ some_constant = b"\00" * 31 + b"\x0c"
+ prefix_len = len(blueprint_prefix)
+
+ deployer_code = f"""
+created_address: public(address)
+
+x: constant(Bytes[32]) = {some_constant}
+salt: constant(bytes32) = keccak256("kebab")
+
+@internal
+def foo() -> bytes32:
+ g:uint256 = 42
+ return salt
+
+@external
+@payable
+def test(target: address):
+ self.created_address = create_from_blueprint(
+ target,
+ x,
+ code_offset={prefix_len},
+ salt=self.foo(),
+ raw_args= True
+ )
+ """
+
+ foo_contract = get_contract(code, 12)
+ expected_runtime_code = w3.eth.get_code(foo_contract.address)
+
+ f, FooContract = deploy_blueprint_for(code, initcode_prefix=blueprint_prefix)
+
+ d = get_contract(deployer_code)
+
+ d.test(f.address, transact={})
+
+ test = FooContract(d.created_address())
+ assert w3.eth.get_code(test.address) == expected_runtime_code
+ assert test.foo() == 12
+
+
+@pytest.mark.parametrize("blueprint_prefix", [b"", b"\xfe", b"\xfe\71\x00"])
+def test_create_from_blueprint_complex_salt_no_constructor_args(
+ get_contract, deploy_blueprint_for, w3, blueprint_prefix
+):
+ # test msize allocator does not get trampled by salt= kwarg
+ code = """
+var: uint256
+
+@external
+@payable
+def __init__():
+ self.var = 12
+
+@external
+def foo()-> uint256:
+ return self.var
+ """
+
+ prefix_len = len(blueprint_prefix)
+ deployer_code = f"""
+created_address: public(address)
+
+salt: constant(bytes32) = keccak256("kebab")
+
+@external
+@payable
+def test(target: address):
+ self.created_address = create_from_blueprint(
+ target,
+ code_offset={prefix_len},
+ salt=keccak256(_abi_encode(target))
+ )
+ """
+
+ foo_contract = get_contract(code)
+ expected_runtime_code = w3.eth.get_code(foo_contract.address)
+
+ f, FooContract = deploy_blueprint_for(code, initcode_prefix=blueprint_prefix)
+
+ d = get_contract(deployer_code)
+
+ d.test(f.address, transact={})
+
+ test = FooContract(d.created_address())
+ assert w3.eth.get_code(test.address) == expected_runtime_code
+ assert test.foo() == 12
+
+
+def test_create_copy_of_complex_kwargs(get_contract, w3):
+ # test msize allocator does not get trampled by salt= kwarg
+ complex_salt = """
+created_address: public(address)
+
+@external
+def test(target: address) -> address:
+ self.created_address = create_copy_of(
+ target,
+ salt=keccak256(_abi_encode(target))
+ )
+ return self.created_address
+
+ """
+
+ c = get_contract(complex_salt)
+ bytecode = w3.eth.get_code(c.address)
+ c.test(c.address, transact={})
+ test1 = c.created_address()
+ assert w3.eth.get_code(test1) == bytecode
+
+ # test msize allocator does not get trampled by value= kwarg
+ complex_value = """
+created_address: public(address)
+
+@external
+@payable
+def test(target: address) -> address:
+ value: uint256 = 2
+ self.created_address = create_copy_of(target, value = [2,2,2][value])
+ return self.created_address
+
+ """
+
+ c = get_contract(complex_value)
+ bytecode = w3.eth.get_code(c.address)
+
+ c.test(c.address, transact={"value": 2})
+ test1 = c.created_address()
+ assert w3.eth.get_code(test1) == bytecode
diff --git a/tests/parser/functions/test_ec.py b/tests/functional/builtins/codegen/test_ec.py
similarity index 100%
rename from tests/parser/functions/test_ec.py
rename to tests/functional/builtins/codegen/test_ec.py
diff --git a/tests/parser/functions/test_ecrecover.py b/tests/functional/builtins/codegen/test_ecrecover.py
similarity index 100%
rename from tests/parser/functions/test_ecrecover.py
rename to tests/functional/builtins/codegen/test_ecrecover.py
diff --git a/tests/parser/functions/test_empty.py b/tests/functional/builtins/codegen/test_empty.py
similarity index 97%
rename from tests/parser/functions/test_empty.py
rename to tests/functional/builtins/codegen/test_empty.py
index c10d03550a..c3627785dc 100644
--- a/tests/parser/functions/test_empty.py
+++ b/tests/functional/builtins/codegen/test_empty.py
@@ -87,8 +87,8 @@ def foo():
self.foobar = empty(address)
bar = empty(address)
- assert self.foobar == ZERO_ADDRESS
- assert bar == ZERO_ADDRESS
+ assert self.foobar == empty(address)
+ assert bar == empty(address)
""",
"""
@external
@@ -214,12 +214,12 @@ def foo():
self.foobar = empty(address[3])
bar = empty(address[3])
- assert self.foobar[0] == ZERO_ADDRESS
- assert self.foobar[1] == ZERO_ADDRESS
- assert self.foobar[2] == ZERO_ADDRESS
- assert bar[0] == ZERO_ADDRESS
- assert bar[1] == ZERO_ADDRESS
- assert bar[2] == ZERO_ADDRESS
+ assert self.foobar[0] == empty(address)
+ assert self.foobar[1] == empty(address)
+ assert self.foobar[2] == empty(address)
+ assert bar[0] == empty(address)
+ assert bar[1] == empty(address)
+ assert bar[2] == empty(address)
""",
],
)
@@ -376,14 +376,14 @@ def foo():
assert self.foobar.c == False
assert self.foobar.d == 0.0
assert self.foobar.e == 0x0000000000000000000000000000000000000000000000000000000000000000
- assert self.foobar.f == ZERO_ADDRESS
+ assert self.foobar.f == empty(address)
assert bar.a == 0
assert bar.b == 0
assert bar.c == False
assert bar.d == 0.0
assert bar.e == 0x0000000000000000000000000000000000000000000000000000000000000000
- assert bar.f == ZERO_ADDRESS
+ assert bar.f == empty(address)
"""
c = get_contract_with_gas_estimation(code)
diff --git a/tests/parser/functions/test_extract32.py b/tests/functional/builtins/codegen/test_extract32.py
similarity index 82%
rename from tests/parser/functions/test_extract32.py
rename to tests/functional/builtins/codegen/test_extract32.py
index c1a333ae32..a95b57b5ab 100644
--- a/tests/parser/functions/test_extract32.py
+++ b/tests/functional/builtins/codegen/test_extract32.py
@@ -1,4 +1,4 @@
-def test_extract32_extraction(assert_tx_failed, get_contract_with_gas_estimation):
+def test_extract32_extraction(tx_failed, get_contract_with_gas_estimation):
extract32_code = """
y: Bytes[100]
@external
@@ -34,18 +34,19 @@ def extrakt32_storage(index: uint256, inp: Bytes[100]) -> bytes32:
)
for S, i in test_cases:
- expected_result = S[i : i + 32] if 0 <= i <= len(S) - 32 else None
- if expected_result is None:
- assert_tx_failed(lambda: c.extrakt32(S, i))
- else:
+ if 0 <= i <= len(S) - 32:
+ expected_result = S[i : i + 32]
assert c.extrakt32(S, i) == expected_result
assert c.extrakt32_mem(S, i) == expected_result
assert c.extrakt32_storage(i, S) == expected_result
+ else:
+ with tx_failed():
+ c.extrakt32(S, i)
print("Passed bytes32 extraction test")
-def test_extract32_code(assert_tx_failed, get_contract_with_gas_estimation):
+def test_extract32_code(tx_failed, get_contract_with_gas_estimation):
extract32_code = """
@external
def foo(inp: Bytes[32]) -> int128:
@@ -72,7 +73,8 @@ def foq(inp: Bytes[32]) -> address:
assert c.foo(b"\x00" * 30 + b"\x01\x01") == 257
assert c.bar(b"\x00" * 30 + b"\x01\x01") == 257
- assert_tx_failed(lambda: c.foo(b"\x80" + b"\x00" * 30))
+ with tx_failed():
+ c.foo(b"\x80" + b"\x00" * 30)
assert c.bar(b"\x80" + b"\x00" * 31) == 2**255
@@ -80,6 +82,7 @@ def foq(inp: Bytes[32]) -> address:
assert c.fop(b"crow" * 8) == b"crow" * 8
assert c.foq(b"\x00" * 12 + b"3" * 20) == "0x" + "3" * 40
- assert_tx_failed(lambda: c.foq(b"crow" * 8))
+ with tx_failed():
+ c.foq(b"crow" * 8)
print("Passed extract32 test")
diff --git a/tests/parser/functions/test_floor.py b/tests/functional/builtins/codegen/test_floor.py
similarity index 100%
rename from tests/parser/functions/test_floor.py
rename to tests/functional/builtins/codegen/test_floor.py
diff --git a/tests/parser/functions/test_is_contract.py b/tests/functional/builtins/codegen/test_is_contract.py
similarity index 100%
rename from tests/parser/functions/test_is_contract.py
rename to tests/functional/builtins/codegen/test_is_contract.py
diff --git a/tests/parser/functions/test_keccak256.py b/tests/functional/builtins/codegen/test_keccak256.py
similarity index 71%
rename from tests/parser/functions/test_keccak256.py
rename to tests/functional/builtins/codegen/test_keccak256.py
index 90fa8b9e09..3b0b9f2018 100644
--- a/tests/parser/functions/test_keccak256.py
+++ b/tests/functional/builtins/codegen/test_keccak256.py
@@ -1,3 +1,6 @@
+from vyper.utils import hex_to_int
+
+
def test_hash_code(get_contract_with_gas_estimation, keccak):
hash_code = """
@external
@@ -80,3 +83,31 @@ def try32(inp: bytes32) -> bool:
assert c.tryy(b"\x35" * 33) is True
print("Passed KECCAK256 hash test")
+
+
+def test_hash_constant_bytes32(get_contract_with_gas_estimation, keccak):
+ hex_val = "0x1234567890123456789012345678901234567890123456789012345678901234"
+ code = f"""
+FOO: constant(bytes32) = {hex_val}
+BAR: constant(bytes32) = keccak256(FOO)
+@external
+def foo() -> bytes32:
+ x: bytes32 = BAR
+ return x
+ """
+ c = get_contract_with_gas_estimation(code)
+ assert "0x" + c.foo().hex() == keccak(hex_to_int(hex_val).to_bytes(32, "big")).hex()
+
+
+def test_hash_constant_string(get_contract_with_gas_estimation, keccak):
+ str_val = "0x1234567890123456789012345678901234567890123456789012345678901234"
+ code = f"""
+FOO: constant(String[66]) = "{str_val}"
+BAR: constant(bytes32) = keccak256(FOO)
+@external
+def foo() -> bytes32:
+ x: bytes32 = BAR
+ return x
+ """
+ c = get_contract_with_gas_estimation(code)
+ assert "0x" + c.foo().hex() == keccak(str_val.encode()).hex()
diff --git a/tests/parser/functions/test_length.py b/tests/functional/builtins/codegen/test_length.py
similarity index 100%
rename from tests/parser/functions/test_length.py
rename to tests/functional/builtins/codegen/test_length.py
diff --git a/tests/parser/functions/test_method_id.py b/tests/functional/builtins/codegen/test_method_id.py
similarity index 100%
rename from tests/parser/functions/test_method_id.py
rename to tests/functional/builtins/codegen/test_method_id.py
diff --git a/tests/parser/functions/test_minmax.py b/tests/functional/builtins/codegen/test_minmax.py
similarity index 98%
rename from tests/parser/functions/test_minmax.py
rename to tests/functional/builtins/codegen/test_minmax.py
index da939d605a..f86504522f 100644
--- a/tests/parser/functions/test_minmax.py
+++ b/tests/functional/builtins/codegen/test_minmax.py
@@ -198,7 +198,7 @@ def foo() -> uint256:
def test_minmax_var_uint256_negative_int128(
- get_contract_with_gas_estimation, assert_tx_failed, assert_compile_failed
+ get_contract_with_gas_estimation, tx_failed, assert_compile_failed
):
from vyper.exceptions import TypeMismatch
diff --git a/tests/parser/functions/test_minmax_value.py b/tests/functional/builtins/codegen/test_minmax_value.py
similarity index 100%
rename from tests/parser/functions/test_minmax_value.py
rename to tests/functional/builtins/codegen/test_minmax_value.py
diff --git a/tests/parser/functions/test_mulmod.py b/tests/functional/builtins/codegen/test_mulmod.py
similarity index 94%
rename from tests/parser/functions/test_mulmod.py
rename to tests/functional/builtins/codegen/test_mulmod.py
index 96477897b9..ba82ebd5b8 100644
--- a/tests/parser/functions/test_mulmod.py
+++ b/tests/functional/builtins/codegen/test_mulmod.py
@@ -1,4 +1,4 @@
-def test_uint256_mulmod(assert_tx_failed, get_contract_with_gas_estimation):
+def test_uint256_mulmod(tx_failed, get_contract_with_gas_estimation):
uint256_code = """
@external
def _uint256_mulmod(x: uint256, y: uint256, z: uint256) -> uint256:
@@ -11,7 +11,8 @@ def _uint256_mulmod(x: uint256, y: uint256, z: uint256) -> uint256:
assert c._uint256_mulmod(200, 3, 601) == 600
assert c._uint256_mulmod(2**255, 1, 3) == 2
assert c._uint256_mulmod(2**255, 2, 6) == 4
- assert_tx_failed(lambda: c._uint256_mulmod(2, 2, 0))
+ with tx_failed():
+ c._uint256_mulmod(2, 2, 0)
def test_uint256_mulmod_complex(get_contract_with_gas_estimation):
diff --git a/tests/parser/functions/test_raw_call.py b/tests/functional/builtins/codegen/test_raw_call.py
similarity index 70%
rename from tests/parser/functions/test_raw_call.py
rename to tests/functional/builtins/codegen/test_raw_call.py
index 9c6fba79e7..4d37176cf8 100644
--- a/tests/parser/functions/test_raw_call.py
+++ b/tests/functional/builtins/codegen/test_raw_call.py
@@ -91,7 +91,7 @@ def create_and_return_proxy(inp: address) -> address:
# print(f'Gas consumed: {(chain.head_state.receipts[-1].gas_used - chain.head_state.receipts[-2].gas_used - chain.last_tx.intrinsic_gas_used)}') # noqa: E501
-def test_multiple_levels2(assert_tx_failed, get_contract_with_gas_estimation):
+def test_multiple_levels2(tx_failed, get_contract_with_gas_estimation):
inner_code = """
@external
def returnten() -> int128:
@@ -114,7 +114,8 @@ def create_and_return_proxy(inp: address) -> address:
c2 = get_contract_with_gas_estimation(outer_code)
- assert_tx_failed(lambda: c2.create_and_call_returnten(c.address))
+ with tx_failed():
+ c2.create_and_call_returnten(c.address)
print("Passed minimal proxy exception test")
@@ -171,7 +172,7 @@ def set(i: int128, owner: address):
assert outer_contract.owners(1) == a1
-def test_gas(get_contract, assert_tx_failed):
+def test_gas(get_contract, tx_failed):
inner_code = """
bar: bytes32
@@ -202,7 +203,8 @@ def foo_call(_addr: address):
# manually specifying an insufficient amount should fail
outer_contract = get_contract(outer_code.format(", gas=15000"))
- assert_tx_failed(lambda: outer_contract.foo_call(inner_contract.address))
+ with tx_failed():
+ outer_contract.foo_call(inner_contract.address)
def test_static_call(get_contract):
@@ -274,8 +276,8 @@ def test_raw_call(_target: address):
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"])
+ output1 = compile_code(code1, output_formats=["bytecode", "bytecode_runtime"])
+ output2 = compile_code(code2, output_formats=["bytecode", "bytecode_runtime"])
assert output1 == output2
@@ -296,8 +298,8 @@ 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"])
+ output1 = compile_code(code1, output_formats=["bytecode", "bytecode_runtime"])
+ output2 = compile_code(code2, output_formats=["bytecode", "bytecode_runtime"])
assert output1 == output2
@@ -323,7 +325,7 @@ def foo(_addr: address) -> bool:
assert caller.foo(target.address) is True
-def test_static_call_fails_nonpayable(get_contract, assert_tx_failed):
+def test_static_call_fails_nonpayable(get_contract, tx_failed):
target_source = """
baz: int128
@@ -349,10 +351,11 @@ def foo(_addr: address) -> int128:
target = get_contract(target_source)
caller = get_contract(caller_source)
- assert_tx_failed(lambda: caller.foo(target.address))
+ with tx_failed():
+ caller.foo(target.address)
-def test_checkable_raw_call(get_contract, assert_tx_failed):
+def test_checkable_raw_call(get_contract, tx_failed):
target_source = """
baz: int128
@external
@@ -426,6 +429,164 @@ def baz(_addr: address, should_raise: bool) -> uint256:
assert caller.baz(target.address, False) == 3
+# XXX: these test_raw_call_clean_mem* tests depend on variables and
+# calling convention writing to memory. think of ways to make more
+# robust to changes to calling convention and memory layout.
+
+
+def test_raw_call_msg_data_clean_mem(get_contract):
+ # test msize uses clean memory and does not get overwritten by
+ # any raw_call() arguments
+ code = """
+identity: constant(address) = 0x0000000000000000000000000000000000000004
+
+@external
+def foo():
+ pass
+
+@internal
+@view
+def get_address()->address:
+ a:uint256 = 121 # 0x79
+ return identity
+@external
+def bar(f: uint256, u: uint256) -> Bytes[100]:
+ # embed an internal call in the calculation of address
+ a: Bytes[100] = raw_call(self.get_address(), msg.data, max_outsize=100)
+ return a
+ """
+
+ c = get_contract(code)
+ assert (
+ c.bar(1, 2).hex() == "ae42e951"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000002"
+ )
+
+
+def test_raw_call_clean_mem2(get_contract):
+ # test msize uses clean memory and does not get overwritten by
+ # any raw_call() arguments, another way
+ code = """
+buf: Bytes[100]
+
+@external
+def bar(f: uint256, g: uint256, h: uint256) -> Bytes[100]:
+ # embed a memory modifying expression in the calculation of address
+ self.buf = raw_call(
+ [0x0000000000000000000000000000000000000004,][f-1],
+ msg.data,
+ max_outsize=100
+ )
+ return self.buf
+ """
+ c = get_contract(code)
+
+ assert (
+ c.bar(1, 2, 3).hex() == "9309b76e"
+ "0000000000000000000000000000000000000000000000000000000000000001"
+ "0000000000000000000000000000000000000000000000000000000000000002"
+ "0000000000000000000000000000000000000000000000000000000000000003"
+ )
+
+
+def test_raw_call_clean_mem3(get_contract):
+ # test msize uses clean memory and does not get overwritten by
+ # any raw_call() arguments, and also test order of evaluation for
+ # scope_multi
+ code = """
+buf: Bytes[100]
+canary: String[32]
+
+@internal
+def bar() -> address:
+ self.canary = "bar"
+ return 0x0000000000000000000000000000000000000004
+
+@internal
+def goo() -> uint256:
+ self.canary = "goo"
+ return 0
+
+@external
+def foo() -> String[32]:
+ self.buf = raw_call(self.bar(), msg.data, value = self.goo(), max_outsize=100)
+ return self.canary
+ """
+ c = get_contract(code)
+ assert c.foo() == "goo"
+
+
+def test_raw_call_clean_mem_kwargs_value(get_contract):
+ # test msize uses clean memory and does not get overwritten by
+ # any raw_call() kwargs
+ code = """
+buf: Bytes[100]
+
+# add a dummy function to trigger memory expansion in the selector table routine
+@external
+def foo():
+ pass
+
+@internal
+def _value() -> uint256:
+ x: uint256 = 1
+ return x
+
+@external
+def bar(f: uint256) -> Bytes[100]:
+ # embed a memory modifying expression in the calculation of address
+ self.buf = raw_call(
+ 0x0000000000000000000000000000000000000004,
+ msg.data,
+ max_outsize=100,
+ value=self._value()
+ )
+ return self.buf
+ """
+ c = get_contract(code, value=1)
+
+ assert (
+ c.bar(13).hex() == "0423a132"
+ "000000000000000000000000000000000000000000000000000000000000000d"
+ )
+
+
+def test_raw_call_clean_mem_kwargs_gas(get_contract):
+ # test msize uses clean memory and does not get overwritten by
+ # any raw_call() kwargs
+ code = """
+buf: Bytes[100]
+
+# add a dummy function to trigger memory expansion in the selector table routine
+@external
+def foo():
+ pass
+
+@internal
+def _gas() -> uint256:
+ x: uint256 = msg.gas
+ return x
+
+@external
+def bar(f: uint256) -> Bytes[100]:
+ # embed a memory modifying expression in the calculation of address
+ self.buf = raw_call(
+ 0x0000000000000000000000000000000000000004,
+ msg.data,
+ max_outsize=100,
+ gas=self._gas()
+ )
+ return self.buf
+ """
+ c = get_contract(code, value=1)
+
+ assert (
+ c.bar(15).hex() == "0423a132"
+ "000000000000000000000000000000000000000000000000000000000000000f"
+ )
+
+
uncompilable_code = [
(
"""
diff --git a/tests/parser/functions/test_send.py b/tests/functional/builtins/codegen/test_send.py
similarity index 94%
rename from tests/parser/functions/test_send.py
rename to tests/functional/builtins/codegen/test_send.py
index 199f708cb4..36f8979556 100644
--- a/tests/parser/functions/test_send.py
+++ b/tests/functional/builtins/codegen/test_send.py
@@ -1,4 +1,4 @@
-def test_send(assert_tx_failed, get_contract):
+def test_send(tx_failed, get_contract):
send_test = """
@external
def foo():
@@ -9,9 +9,11 @@ def fop():
send(msg.sender, 10)
"""
c = get_contract(send_test, value=10)
- assert_tx_failed(lambda: c.foo(transact={}))
+ with tx_failed():
+ c.foo(transact={})
c.fop(transact={})
- assert_tx_failed(lambda: c.fop(transact={}))
+ with tx_failed():
+ c.fop(transact={})
def test_default_gas(get_contract, w3):
diff --git a/tests/parser/functions/test_sha256.py b/tests/functional/builtins/codegen/test_sha256.py
similarity index 66%
rename from tests/parser/functions/test_sha256.py
rename to tests/functional/builtins/codegen/test_sha256.py
index 468e684645..8e1b89bd31 100644
--- a/tests/parser/functions/test_sha256.py
+++ b/tests/functional/builtins/codegen/test_sha256.py
@@ -2,6 +2,8 @@
import pytest
+from vyper.utils import hex_to_int
+
pytestmark = pytest.mark.usefixtures("memory_mocker")
@@ -77,3 +79,31 @@ def bar() -> bytes32:
c.set(test_val, transact={})
assert c.a() == test_val
assert c.bar() == hashlib.sha256(test_val).digest()
+
+
+def test_sha256_constant_bytes32(get_contract_with_gas_estimation):
+ hex_val = "0x1234567890123456789012345678901234567890123456789012345678901234"
+ code = f"""
+FOO: constant(bytes32) = {hex_val}
+BAR: constant(bytes32) = sha256(FOO)
+@external
+def foo() -> bytes32:
+ x: bytes32 = BAR
+ return x
+ """
+ c = get_contract_with_gas_estimation(code)
+ assert c.foo() == hashlib.sha256(hex_to_int(hex_val).to_bytes(32, "big")).digest()
+
+
+def test_sha256_constant_string(get_contract_with_gas_estimation):
+ str_val = "0x1234567890123456789012345678901234567890123456789012345678901234"
+ code = f"""
+FOO: constant(String[66]) = "{str_val}"
+BAR: constant(bytes32) = sha256(FOO)
+@external
+def foo() -> bytes32:
+ x: bytes32 = BAR
+ return x
+ """
+ c = get_contract_with_gas_estimation(code)
+ assert c.foo() == hashlib.sha256(str_val.encode()).digest()
diff --git a/tests/parser/functions/test_slice.py b/tests/functional/builtins/codegen/test_slice.py
similarity index 81%
rename from tests/parser/functions/test_slice.py
rename to tests/functional/builtins/codegen/test_slice.py
index 6229b47921..a15a3eeb35 100644
--- a/tests/parser/functions/test_slice.py
+++ b/tests/functional/builtins/codegen/test_slice.py
@@ -32,26 +32,26 @@ def slice_tower_test(inp1: Bytes[50]) -> Bytes[50]:
_bytes_1024 = st.binary(min_size=0, max_size=1024)
-@pytest.mark.parametrize("literal_start", (True, False))
-@pytest.mark.parametrize("literal_length", (True, False))
+@pytest.mark.parametrize("use_literal_start", (True, False))
+@pytest.mark.parametrize("use_literal_length", (True, False))
@pytest.mark.parametrize("opt_level", list(OptimizationLevel))
@given(start=_draw_1024, length=_draw_1024, length_bound=_draw_1024_1, bytesdata=_bytes_1024)
-@settings(max_examples=100, deadline=None)
+@settings(max_examples=100)
@pytest.mark.fuzzing
def test_slice_immutable(
get_contract,
assert_compile_failed,
- assert_tx_failed,
+ tx_failed,
opt_level,
bytesdata,
start,
- literal_start,
+ use_literal_start,
length,
- literal_length,
+ use_literal_length,
length_bound,
):
- _start = start if literal_start else "start"
- _length = length if literal_length else "length"
+ _start = start if use_literal_start else "start"
+ _length = length if use_literal_length else "length"
code = f"""
IMMUTABLE_BYTES: immutable(Bytes[{length_bound}])
@@ -71,47 +71,58 @@ def _get_contract():
return get_contract(code, bytesdata, start, length, override_opt_level=opt_level)
if (
- (start + length > length_bound and literal_start and literal_length)
- or (literal_length and length > length_bound)
- or (literal_start and start > length_bound)
- or (literal_length and length < 1)
+ (start + length > length_bound and use_literal_start and use_literal_length)
+ or (use_literal_length and length > length_bound)
+ or (use_literal_start and start > length_bound)
+ or (use_literal_length and length == 0)
):
assert_compile_failed(lambda: _get_contract(), ArgumentException)
elif start + length > len(bytesdata) or (len(bytesdata) > length_bound):
# deploy fail
- assert_tx_failed(lambda: _get_contract())
+ with tx_failed():
+ _get_contract()
else:
c = _get_contract()
assert c.do_splice() == bytesdata[start : start + length]
@pytest.mark.parametrize("location", ("storage", "calldata", "memory", "literal", "code"))
-@pytest.mark.parametrize("literal_start", (True, False))
-@pytest.mark.parametrize("literal_length", (True, False))
+@pytest.mark.parametrize("use_literal_start", (True, False))
+@pytest.mark.parametrize("use_literal_length", (True, False))
@pytest.mark.parametrize("opt_level", list(OptimizationLevel))
@given(start=_draw_1024, length=_draw_1024, length_bound=_draw_1024_1, bytesdata=_bytes_1024)
-@settings(max_examples=100, deadline=None)
+@settings(max_examples=100)
@pytest.mark.fuzzing
-def test_slice_bytes(
+def test_slice_bytes_fuzz(
get_contract,
assert_compile_failed,
- assert_tx_failed,
+ tx_failed,
opt_level,
location,
bytesdata,
start,
- literal_start,
+ use_literal_start,
length,
- literal_length,
+ use_literal_length,
length_bound,
):
+ preamble = ""
if location == "memory":
spliced_code = f"foo: Bytes[{length_bound}] = inp"
foo = "foo"
elif location == "storage":
+ preamble = f"""
+foo: Bytes[{length_bound}]
+ """
spliced_code = "self.foo = inp"
foo = "self.foo"
elif location == "code":
+ preamble = f"""
+IMMUTABLE_BYTES: immutable(Bytes[{length_bound}])
+@external
+def __init__(foo: Bytes[{length_bound}]):
+ IMMUTABLE_BYTES = foo
+ """
spliced_code = ""
foo = "IMMUTABLE_BYTES"
elif location == "literal":
@@ -123,15 +134,11 @@ def test_slice_bytes(
else:
raise Exception("unreachable")
- _start = start if literal_start else "start"
- _length = length if literal_length else "length"
+ _start = start if use_literal_start else "start"
+ _length = length if use_literal_length else "length"
code = f"""
-foo: Bytes[{length_bound}]
-IMMUTABLE_BYTES: immutable(Bytes[{length_bound}])
-@external
-def __init__(foo: Bytes[{length_bound}]):
- IMMUTABLE_BYTES = foo
+{preamble}
@external
def do_slice(inp: Bytes[{length_bound}], start: uint256, length: uint256) -> Bytes[{length_bound}]:
@@ -142,24 +149,42 @@ def do_slice(inp: Bytes[{length_bound}], start: uint256, length: uint256) -> Byt
def _get_contract():
return get_contract(code, bytesdata, override_opt_level=opt_level)
- data_length = len(bytesdata) if location == "literal" else length_bound
- if (
- (start + length > data_length and literal_start and literal_length)
- or (literal_length and length > data_length)
- or (location == "literal" and len(bytesdata) > length_bound)
- or (literal_start and start > data_length)
- or (literal_length and length < 1)
- ):
+ # length bound is the container size; input_bound is the bound on the input
+ # (which can be different, if the input is a literal)
+ input_bound = length_bound
+ slice_output_too_large = False
+
+ if location == "literal":
+ input_bound = len(bytesdata)
+
+ # ex.:
+ # @external
+ # def do_slice(inp: Bytes[1], start: uint256, length: uint256) -> Bytes[1]:
+ # return slice(b'\x00\x00', 0, length)
+ output_length = length if use_literal_length else input_bound
+ slice_output_too_large = output_length > length_bound
+
+ end = start + length
+
+ compile_time_oob = (
+ (use_literal_length and (length > input_bound or length == 0))
+ or (use_literal_start and start > input_bound)
+ or (use_literal_start and use_literal_length and start + length > input_bound)
+ )
+
+ if compile_time_oob or slice_output_too_large:
assert_compile_failed(lambda: _get_contract(), (ArgumentException, TypeMismatch))
- elif len(bytesdata) > data_length:
+ elif location == "code" and len(bytesdata) > length_bound:
# deploy fail
- assert_tx_failed(lambda: _get_contract())
- elif start + length > len(bytesdata):
+ with tx_failed():
+ _get_contract()
+ elif end > len(bytesdata) or len(bytesdata) > length_bound:
c = _get_contract()
- assert_tx_failed(lambda: c.do_slice(bytesdata, start, length))
+ with tx_failed():
+ c.do_slice(bytesdata, start, length)
else:
c = _get_contract()
- assert c.do_slice(bytesdata, start, length) == bytesdata[start : start + length], code
+ assert c.do_slice(bytesdata, start, length) == bytesdata[start:end], code
def test_slice_private(get_contract):
diff --git a/tests/parser/functions/test_mkstr.py b/tests/functional/builtins/codegen/test_uint2str.py
similarity index 66%
rename from tests/parser/functions/test_mkstr.py
rename to tests/functional/builtins/codegen/test_uint2str.py
index 9d2b7fe3f5..d9edea154b 100644
--- a/tests/parser/functions/test_mkstr.py
+++ b/tests/functional/builtins/codegen/test_uint2str.py
@@ -2,6 +2,9 @@
import pytest
+from vyper.compiler import compile_code
+from vyper.exceptions import InvalidType, OverflowException
+
VALID_BITS = list(range(8, 257, 8))
@@ -37,3 +40,25 @@ def foo(x: uint{bits}) -> uint256:
"""
c = get_contract(code)
assert c.foo(2**bits - 1) == 0, bits
+
+
+def test_bignum_throws():
+ code = """
+@external
+def test():
+ a: String[78] = uint2str(2**256)
+ pass
+ """
+ with pytest.raises(OverflowException):
+ compile_code(code)
+
+
+def test_int_fails():
+ code = """
+@external
+def test():
+ a: String[78] = uint2str(-1)
+ pass
+ """
+ with pytest.raises(InvalidType):
+ compile_code(code)
diff --git a/tests/parser/functions/test_unary.py b/tests/functional/builtins/codegen/test_unary.py
similarity index 88%
rename from tests/parser/functions/test_unary.py
rename to tests/functional/builtins/codegen/test_unary.py
index da3823edfe..2be5c0d33f 100644
--- a/tests/parser/functions/test_unary.py
+++ b/tests/functional/builtins/codegen/test_unary.py
@@ -13,14 +13,15 @@ def negate(a: uint256) -> uint256:
assert_compile_failed(lambda: get_contract(code), exception=InvalidOperation)
-def test_unary_sub_int128_fail(get_contract, assert_tx_failed):
+def test_unary_sub_int128_fail(get_contract, tx_failed):
code = """@external
def negate(a: int128) -> int128:
return -(a)
"""
c = get_contract(code)
# This test should revert on overflow condition
- assert_tx_failed(lambda: c.negate(-(2**127)))
+ with tx_failed():
+ c.negate(-(2**127))
@pytest.mark.parametrize("val", [-(2**127) + 1, 0, 2**127 - 1])
@@ -68,16 +69,11 @@ def bar() -> decimal:
def test_negation_int128(get_contract):
code = """
-a: constant(int128) = -2**127
-
-@external
-def foo() -> int128:
- return -2**127
+a: constant(int128) = min_value(int128)
@external
def bar() -> int128:
return -(a+1)
"""
c = get_contract(code)
- assert c.foo() == -(2**127)
assert c.bar() == 2**127 - 1
diff --git a/tests/parser/functions/test_unsafe_math.py b/tests/functional/builtins/codegen/test_unsafe_math.py
similarity index 100%
rename from tests/parser/functions/test_unsafe_math.py
rename to tests/functional/builtins/codegen/test_unsafe_math.py
diff --git a/tests/builtins/folding/test_abs.py b/tests/functional/builtins/folding/test_abs.py
similarity index 68%
rename from tests/builtins/folding/test_abs.py
rename to tests/functional/builtins/folding/test_abs.py
index 58f861ed0c..68131678fa 100644
--- a/tests/builtins/folding/test_abs.py
+++ b/tests/functional/builtins/folding/test_abs.py
@@ -4,11 +4,11 @@
from vyper import ast as vy_ast
from vyper.builtins import functions as vy_fn
-from vyper.exceptions import OverflowException
+from vyper.exceptions import InvalidType
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(a=st.integers(min_value=-(2**255) + 1, max_value=2**255 - 1))
@example(a=0)
def test_abs(get_contract, a):
@@ -21,13 +21,13 @@ def foo(a: int256) -> int256:
vyper_ast = vy_ast.parse_to_ast(f"abs({a})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE["abs"].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE["abs"]._try_fold(old_node)
assert contract.foo(a) == new_node.value == abs(a)
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(a=st.integers(min_value=2**255, max_value=2**256 - 1))
def test_abs_upper_bound_folding(get_contract, a):
source = f"""
@@ -35,11 +35,11 @@ def test_abs_upper_bound_folding(get_contract, a):
def foo(a: int256) -> int256:
return abs({a})
"""
- with pytest.raises(OverflowException):
+ with pytest.raises(InvalidType):
get_contract(source)
-def test_abs_lower_bound(get_contract, assert_tx_failed):
+def test_abs_lower_bound(get_contract, tx_failed):
source = """
@external
def foo(a: int256) -> int256:
@@ -47,14 +47,15 @@ def foo(a: int256) -> int256:
"""
contract = get_contract(source)
- assert_tx_failed(lambda: contract.foo(-(2**255)))
+ with tx_failed():
+ contract.foo(-(2**255))
-def test_abs_lower_bound_folded(get_contract, assert_tx_failed):
+def test_abs_lower_bound_folded(get_contract, tx_failed):
source = """
@external
def foo() -> int256:
- return abs(-2**255)
+ return abs(min_value(int256))
"""
- with pytest.raises(OverflowException):
+ with pytest.raises(InvalidType):
get_contract(source)
diff --git a/tests/builtins/folding/test_addmod_mulmod.py b/tests/functional/builtins/folding/test_addmod_mulmod.py
similarity index 87%
rename from tests/builtins/folding/test_addmod_mulmod.py
rename to tests/functional/builtins/folding/test_addmod_mulmod.py
index 0514dea18a..1d789f1655 100644
--- a/tests/builtins/folding/test_addmod_mulmod.py
+++ b/tests/functional/builtins/folding/test_addmod_mulmod.py
@@ -9,7 +9,7 @@
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(a=st_uint256, b=st_uint256, c=st_uint256)
@pytest.mark.parametrize("fn_name", ["uint256_addmod", "uint256_mulmod"])
def test_modmath(get_contract, a, b, c, fn_name):
@@ -24,6 +24,6 @@ def foo(a: uint256, b: uint256, c: uint256) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({a}, {b}, {c})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE[fn_name]._try_fold(old_node)
assert contract.foo(a, b, c) == new_node.value
diff --git a/tests/builtins/folding/test_bitwise.py b/tests/functional/builtins/folding/test_bitwise.py
similarity index 89%
rename from tests/builtins/folding/test_bitwise.py
rename to tests/functional/builtins/folding/test_bitwise.py
index d28e482589..53a6d333a0 100644
--- a/tests/builtins/folding/test_bitwise.py
+++ b/tests/functional/builtins/folding/test_bitwise.py
@@ -13,8 +13,11 @@
st_sint256 = st.integers(min_value=-(2**255), max_value=2**255 - 1)
+# TODO: move this file to tests/unit/ast/nodes/test_fold_bitwise.py
+
+
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@pytest.mark.parametrize("op", ["&", "|", "^"])
@given(a=st_uint256, b=st_uint256)
def test_bitwise_ops(get_contract, a, b, op):
@@ -28,13 +31,13 @@ def foo(a: uint256, b: uint256) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"{a} {op} {b}")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
assert contract.foo(a, b) == new_node.value
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@pytest.mark.parametrize("op", ["<<", ">>"])
@given(a=st_uint256, b=st.integers(min_value=0, max_value=256))
def test_bitwise_shift_unsigned(get_contract, a, b, op):
@@ -49,7 +52,7 @@ def foo(a: uint256, b: uint256) -> uint256:
old_node = vyper_ast.body[0].value
try:
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
# force bounds check, no-op because validate_numeric_bounds
# already does this, but leave in for hygiene (in case
# more types are added).
@@ -64,7 +67,7 @@ def foo(a: uint256, b: uint256) -> uint256:
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@pytest.mark.parametrize("op", ["<<", ">>"])
@given(a=st_sint256, b=st.integers(min_value=0, max_value=256))
def test_bitwise_shift_signed(get_contract, a, b, op):
@@ -79,7 +82,7 @@ def foo(a: int256, b: uint256) -> int256:
old_node = vyper_ast.body[0].value
try:
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
validate_expected_type(new_node, INT256_T) # force bounds check
# compile time behavior does not match runtime behavior.
# compile-time will throw on OOB, runtime will wrap.
@@ -92,7 +95,7 @@ def foo(a: int256, b: uint256) -> int256:
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(value=st_uint256)
def test_bitwise_not(get_contract, value):
source = """
@@ -104,6 +107,6 @@ def foo(a: uint256) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"~{value}")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
assert contract.foo(value) == new_node.value
diff --git a/tests/builtins/folding/test_epsilon.py b/tests/functional/builtins/folding/test_epsilon.py
similarity index 87%
rename from tests/builtins/folding/test_epsilon.py
rename to tests/functional/builtins/folding/test_epsilon.py
index 794648cfce..4f5e9434ec 100644
--- a/tests/builtins/folding/test_epsilon.py
+++ b/tests/functional/builtins/folding/test_epsilon.py
@@ -15,6 +15,6 @@ def foo() -> {typ_name}:
vyper_ast = vy_ast.parse_to_ast(f"epsilon({typ_name})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE["epsilon"].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE["epsilon"]._try_fold(old_node)
assert contract.foo() == new_node.value
diff --git a/tests/builtins/folding/test_floor_ceil.py b/tests/functional/builtins/folding/test_floor_ceil.py
similarity index 89%
rename from tests/builtins/folding/test_floor_ceil.py
rename to tests/functional/builtins/folding/test_floor_ceil.py
index 763f8fec63..04921e504e 100644
--- a/tests/builtins/folding/test_floor_ceil.py
+++ b/tests/functional/builtins/folding/test_floor_ceil.py
@@ -13,7 +13,7 @@
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(value=st_decimals)
@example(value=Decimal("0.9999999999"))
@example(value=Decimal("0.0000000001"))
@@ -30,6 +30,6 @@ def foo(a: decimal) -> int256:
vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({value})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE[fn_name]._try_fold(old_node)
assert contract.foo(value) == new_node.value
diff --git a/tests/builtins/folding/test_fold_as_wei_value.py b/tests/functional/builtins/folding/test_fold_as_wei_value.py
similarity index 87%
rename from tests/builtins/folding/test_fold_as_wei_value.py
rename to tests/functional/builtins/folding/test_fold_as_wei_value.py
index 11d23bd3bf..4287615bab 100644
--- a/tests/builtins/folding/test_fold_as_wei_value.py
+++ b/tests/functional/builtins/folding/test_fold_as_wei_value.py
@@ -19,7 +19,7 @@
@pytest.mark.fuzzing
-@settings(max_examples=10, deadline=1000)
+@settings(max_examples=10)
@given(value=st_decimals)
@pytest.mark.parametrize("denom", denoms)
def test_decimal(get_contract, value, denom):
@@ -32,13 +32,13 @@ def foo(a: decimal) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"as_wei_value({value:.10f}, '{denom}')")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.AsWeiValue().evaluate(old_node)
+ new_node = vy_fn.AsWeiValue()._try_fold(old_node)
assert contract.foo(value) == new_node.value
@pytest.mark.fuzzing
-@settings(max_examples=10, deadline=1000)
+@settings(max_examples=10)
@given(value=st.integers(min_value=0, max_value=2**128))
@pytest.mark.parametrize("denom", denoms)
def test_integer(get_contract, value, denom):
@@ -51,6 +51,6 @@ def foo(a: uint256) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"as_wei_value({value}, '{denom}')")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.AsWeiValue().evaluate(old_node)
+ new_node = vy_fn.AsWeiValue()._try_fold(old_node)
assert contract.foo(value) == new_node.value
diff --git a/tests/builtins/folding/test_keccak_sha.py b/tests/functional/builtins/folding/test_keccak_sha.py
similarity index 84%
rename from tests/builtins/folding/test_keccak_sha.py
rename to tests/functional/builtins/folding/test_keccak_sha.py
index 8e283566de..8da420538f 100644
--- a/tests/builtins/folding/test_keccak_sha.py
+++ b/tests/functional/builtins/folding/test_keccak_sha.py
@@ -10,7 +10,7 @@
@pytest.mark.fuzzing
@given(value=st.text(alphabet=alphabet, min_size=0, max_size=100))
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@pytest.mark.parametrize("fn_name", ["keccak256", "sha256"])
def test_string(get_contract, value, fn_name):
source = f"""
@@ -22,14 +22,14 @@ def foo(a: String[100]) -> bytes32:
vyper_ast = vy_ast.parse_to_ast(f"{fn_name}('''{value}''')")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE[fn_name]._try_fold(old_node)
assert f"0x{contract.foo(value).hex()}" == new_node.value
@pytest.mark.fuzzing
@given(value=st.binary(min_size=0, max_size=100))
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@pytest.mark.parametrize("fn_name", ["keccak256", "sha256"])
def test_bytes(get_contract, value, fn_name):
source = f"""
@@ -41,14 +41,14 @@ def foo(a: Bytes[100]) -> bytes32:
vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({value})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE[fn_name]._try_fold(old_node)
assert f"0x{contract.foo(value).hex()}" == new_node.value
@pytest.mark.fuzzing
@given(value=st.binary(min_size=1, max_size=100))
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@pytest.mark.parametrize("fn_name", ["keccak256", "sha256"])
def test_hex(get_contract, value, fn_name):
source = f"""
@@ -62,6 +62,6 @@ def foo(a: Bytes[100]) -> bytes32:
vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({value})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE[fn_name]._try_fold(old_node)
assert f"0x{contract.foo(value).hex()}" == new_node.value
diff --git a/tests/builtins/folding/test_len.py b/tests/functional/builtins/folding/test_len.py
similarity index 90%
rename from tests/builtins/folding/test_len.py
rename to tests/functional/builtins/folding/test_len.py
index edf33120dd..967f906555 100644
--- a/tests/builtins/folding/test_len.py
+++ b/tests/functional/builtins/folding/test_len.py
@@ -17,7 +17,7 @@ def foo(a: String[1024]) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"len('{value}')")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.Len().evaluate(old_node)
+ new_node = vy_fn.Len()._try_fold(old_node)
assert contract.foo(value) == new_node.value
@@ -35,7 +35,7 @@ def foo(a: Bytes[1024]) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"len(b'{value}')")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.Len().evaluate(old_node)
+ new_node = vy_fn.Len()._try_fold(old_node)
assert contract.foo(value.encode()) == new_node.value
@@ -53,6 +53,6 @@ def foo(a: Bytes[1024]) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"len({value})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.Len().evaluate(old_node)
+ new_node = vy_fn.Len()._try_fold(old_node)
assert contract.foo(value) == new_node.value
diff --git a/tests/builtins/folding/test_min_max.py b/tests/functional/builtins/folding/test_min_max.py
similarity index 85%
rename from tests/builtins/folding/test_min_max.py
rename to tests/functional/builtins/folding/test_min_max.py
index e2d33237ca..36a611fa1b 100644
--- a/tests/builtins/folding/test_min_max.py
+++ b/tests/functional/builtins/folding/test_min_max.py
@@ -18,7 +18,7 @@
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st_decimals, right=st_decimals)
@pytest.mark.parametrize("fn_name", ["min", "max"])
def test_decimal(get_contract, left, right, fn_name):
@@ -31,13 +31,13 @@ def foo(a: decimal, b: decimal) -> decimal:
vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({left}, {right})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE[fn_name]._try_fold(old_node)
assert contract.foo(left, right) == new_node.value
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st_int128, right=st_int128)
@pytest.mark.parametrize("fn_name", ["min", "max"])
def test_int128(get_contract, left, right, fn_name):
@@ -50,13 +50,13 @@ def foo(a: int128, b: int128) -> int128:
vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({left}, {right})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE[fn_name]._try_fold(old_node)
assert contract.foo(left, right) == new_node.value
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st_uint256, right=st_uint256)
@pytest.mark.parametrize("fn_name", ["min", "max"])
def test_min_uint256(get_contract, left, right, fn_name):
@@ -69,6 +69,6 @@ def foo(a: uint256, b: uint256) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({left}, {right})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node)
+ new_node = vy_fn.DISPATCH_TABLE[fn_name]._try_fold(old_node)
assert contract.foo(left, right) == new_node.value
diff --git a/tests/builtins/folding/test_powmod.py b/tests/functional/builtins/folding/test_powmod.py
similarity index 86%
rename from tests/builtins/folding/test_powmod.py
rename to tests/functional/builtins/folding/test_powmod.py
index fdc0e300ab..a3c2567f58 100644
--- a/tests/builtins/folding/test_powmod.py
+++ b/tests/functional/builtins/folding/test_powmod.py
@@ -9,7 +9,7 @@
@pytest.mark.fuzzing
-@settings(max_examples=100, deadline=1000)
+@settings(max_examples=100)
@given(a=st_uint256, b=st_uint256)
def test_powmod_uint256(get_contract, a, b):
source = """
@@ -21,6 +21,6 @@ def foo(a: uint256, b: uint256) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"pow_mod256({a}, {b})")
old_node = vyper_ast.body[0].value
- new_node = vy_fn.PowMod256().evaluate(old_node)
+ new_node = vy_fn.PowMod256()._try_fold(old_node)
assert contract.foo(a, b) == new_node.value
diff --git a/tests/parser/functions/__init__.py b/tests/functional/codegen/__init__.py
similarity index 100%
rename from tests/parser/functions/__init__.py
rename to tests/functional/codegen/__init__.py
diff --git a/tests/parser/functions/test_default_function.py b/tests/functional/codegen/calling_convention/test_default_function.py
similarity index 89%
rename from tests/parser/functions/test_default_function.py
rename to tests/functional/codegen/calling_convention/test_default_function.py
index 4aa0b04a77..cf55607877 100644
--- a/tests/parser/functions/test_default_function.py
+++ b/tests/functional/codegen/calling_convention/test_default_function.py
@@ -1,4 +1,4 @@
-def test_throw_on_sending(w3, assert_tx_failed, get_contract_with_gas_estimation):
+def test_throw_on_sending(w3, tx_failed, get_contract_with_gas_estimation):
code = """
x: public(int128)
@@ -10,9 +10,8 @@ def __init__():
assert c.x() == 123
assert w3.eth.get_balance(c.address) == 0
- assert_tx_failed(
- lambda: w3.eth.send_transaction({"to": c.address, "value": w3.to_wei(0.1, "ether")})
- )
+ with tx_failed():
+ w3.eth.send_transaction({"to": c.address, "value": w3.to_wei(0.1, "ether")})
assert w3.eth.get_balance(c.address) == 0
@@ -41,7 +40,7 @@ def test_basic_default_default_param_function(w3, get_logs, get_contract_with_ga
@external
@payable
def fooBar(a: int128 = 12345) -> int128:
- log Sent(ZERO_ADDRESS)
+ log Sent(empty(address))
return a
@external
@@ -56,7 +55,7 @@ def __default__():
assert w3.eth.get_balance(c.address) == w3.to_wei(0.1, "ether")
-def test_basic_default_not_payable(w3, assert_tx_failed, get_contract_with_gas_estimation):
+def test_basic_default_not_payable(w3, tx_failed, get_contract_with_gas_estimation):
code = """
event Sent:
sender: indexed(address)
@@ -67,7 +66,8 @@ def __default__():
"""
c = get_contract_with_gas_estimation(code)
- assert_tx_failed(lambda: w3.eth.send_transaction({"to": c.address, "value": 10**17}))
+ with tx_failed():
+ w3.eth.send_transaction({"to": c.address, "value": 10**17})
def test_multi_arg_default(assert_compile_failed, get_contract_with_gas_estimation):
@@ -100,7 +100,7 @@ def __default__():
assert_compile_failed(lambda: get_contract_with_gas_estimation(code))
-def test_zero_method_id(w3, get_logs, get_contract, assert_tx_failed):
+def test_zero_method_id(w3, get_logs, get_contract, tx_failed):
# test a method with 0x00000000 selector,
# expects at least 36 bytes of calldata.
code = """
@@ -143,10 +143,11 @@ def _call_with_bytes(hexstr):
for i in range(4, 36):
# match the full 4 selector bytes, but revert due to malformed (short) calldata
- assert_tx_failed(lambda: _call_with_bytes("0x" + "00" * i))
+ with tx_failed():
+ _call_with_bytes(f"0x{'00' * i}")
-def test_another_zero_method_id(w3, get_logs, get_contract, assert_tx_failed):
+def test_another_zero_method_id(w3, get_logs, get_contract, tx_failed):
# test another zero method id but which only expects 4 bytes of calldata
code = """
event Sent:
diff --git a/tests/parser/functions/test_default_parameters.py b/tests/functional/codegen/calling_convention/test_default_parameters.py
similarity index 91%
rename from tests/parser/functions/test_default_parameters.py
rename to tests/functional/codegen/calling_convention/test_default_parameters.py
index a90f5e6624..462748a9c7 100644
--- a/tests/parser/functions/test_default_parameters.py
+++ b/tests/functional/codegen/calling_convention/test_default_parameters.py
@@ -150,7 +150,7 @@ def foo(a: int128[3] = [1, 2, 3]) -> int128[3]:
assert c.foo() == [1, 2, 3]
-def test_default_param_clamp(get_contract, monkeypatch, assert_tx_failed):
+def test_default_param_clamp(get_contract, monkeypatch, tx_failed):
code = """
@external
def bar(a: int128, b: int128 = -1) -> (int128, int128): # noqa: E501
@@ -168,7 +168,8 @@ def validate_value(cls, value):
monkeypatch.setattr("eth_abi.encoding.NumberEncoder.validate_value", validate_value)
assert c.bar(200, 2**127 - 1) == [200, 2**127 - 1]
- assert_tx_failed(lambda: c.bar(200, 2**127))
+ with tx_failed():
+ c.bar(200, 2**127)
def test_default_param_private(get_contract):
@@ -304,6 +305,48 @@ def foo(a: address = empty(address)):
def foo(a: int112 = min_value(int112)):
self.A = a
""",
+ """
+struct X:
+ x: int128
+ y: address
+BAR: constant(X) = X({x: 1, y: 0x0000000000000000000000000000000000012345})
+@external
+def out_literals(a: int128 = BAR.x + 1) -> X:
+ return BAR
+ """,
+ """
+struct X:
+ x: int128
+ y: address
+struct Y:
+ x: X
+ y: uint256
+BAR: constant(X) = X({x: 1, y: 0x0000000000000000000000000000000000012345})
+FOO: constant(Y) = Y({x: BAR, y: 256})
+@external
+def out_literals(a: int128 = FOO.x.x + 1) -> Y:
+ return FOO
+ """,
+ """
+struct Bar:
+ a: bool
+
+BAR: constant(Bar) = Bar({a: True})
+
+@external
+def foo(x: bool = True and not BAR.a):
+ pass
+ """,
+ """
+struct Bar:
+ a: uint256
+
+BAR: constant(Bar) = Bar({ a: 123 })
+
+@external
+def foo(x: bool = BAR.a + 1 > 456):
+ pass
+ """,
]
diff --git a/tests/parser/features/external_contracts/test_erc20_abi.py b/tests/functional/codegen/calling_convention/test_erc20_abi.py
similarity index 88%
rename from tests/parser/features/external_contracts/test_erc20_abi.py
rename to tests/functional/codegen/calling_convention/test_erc20_abi.py
index 4a09ce68fa..b9dc5c663f 100644
--- a/tests/parser/features/external_contracts/test_erc20_abi.py
+++ b/tests/functional/codegen/calling_convention/test_erc20_abi.py
@@ -81,7 +81,7 @@ def test_initial_state(w3, erc20_caller):
assert erc20_caller.decimals() == TOKEN_DECIMALS
-def test_call_transfer(w3, erc20, erc20_caller, assert_tx_failed):
+def test_call_transfer(w3, erc20, erc20_caller, tx_failed):
# Basic transfer.
erc20.transfer(erc20_caller.address, 10, transact={})
assert erc20.balanceOf(erc20_caller.address) == 10
@@ -90,13 +90,12 @@ def test_call_transfer(w3, erc20, erc20_caller, assert_tx_failed):
assert erc20.balanceOf(w3.eth.accounts[1]) == 10
# more than allowed
- assert_tx_failed(lambda: erc20_caller.transfer(w3.eth.accounts[1], TOKEN_TOTAL_SUPPLY))
+ with tx_failed():
+ erc20_caller.transfer(w3.eth.accounts[1], TOKEN_TOTAL_SUPPLY)
# Negative transfer value.
- assert_tx_failed(
- function_to_test=lambda: erc20_caller.transfer(w3.eth.accounts[1], -1),
- exception=ValidationError,
- )
+ with tx_failed(ValidationError):
+ erc20_caller.transfer(w3.eth.accounts[1], -1)
def test_caller_approve_allowance(w3, erc20, erc20_caller):
@@ -105,11 +104,10 @@ def test_caller_approve_allowance(w3, erc20, erc20_caller):
assert erc20_caller.allowance(w3.eth.accounts[0], erc20_caller.address) == 10
-def test_caller_tranfer_from(w3, erc20, erc20_caller, assert_tx_failed):
+def test_caller_tranfer_from(w3, erc20, erc20_caller, tx_failed):
# Cannot transfer tokens that are unavailable
- assert_tx_failed(
- lambda: erc20_caller.transferFrom(w3.eth.accounts[0], erc20_caller.address, 10)
- )
+ with tx_failed():
+ erc20_caller.transferFrom(w3.eth.accounts[0], erc20_caller.address, 10)
assert erc20.balanceOf(erc20_caller.address) == 0
assert erc20.approve(erc20_caller.address, 10, transact={})
erc20_caller.transferFrom(w3.eth.accounts[0], erc20_caller.address, 5, transact={})
diff --git a/tests/parser/features/external_contracts/test_external_contract_calls.py b/tests/functional/codegen/calling_convention/test_external_contract_calls.py
similarity index 92%
rename from tests/parser/features/external_contracts/test_external_contract_calls.py
rename to tests/functional/codegen/calling_convention/test_external_contract_calls.py
index b3cc6f5576..0af4f9f937 100644
--- a/tests/parser/features/external_contracts/test_external_contract_calls.py
+++ b/tests/functional/codegen/calling_convention/test_external_contract_calls.py
@@ -3,6 +3,7 @@
import pytest
from eth.codecs import abi
+from vyper import compile_code
from vyper.exceptions import (
ArgumentException,
InvalidType,
@@ -94,7 +95,7 @@ def get_array(arg1: address) -> Bytes[3]:
assert c2.get_array(c.address) == b"dog"
-def test_bytes_too_long(get_contract, assert_tx_failed):
+def test_bytes_too_long(get_contract, tx_failed):
contract_1 = """
@external
def array() -> Bytes[4]:
@@ -113,13 +114,14 @@ def get_array(arg1: address) -> Bytes[3]:
"""
c2 = get_contract(contract_2)
- assert_tx_failed(lambda: c2.get_array(c.address))
+ with tx_failed():
+ c2.get_array(c.address)
@pytest.mark.parametrize(
"revert_string", ["Mayday, mayday!", "A very long revert string" + "." * 512]
)
-def test_revert_propagation(get_contract, assert_tx_failed, revert_string):
+def test_revert_propagation(get_contract, tx_failed, revert_string):
raiser = f"""
@external
def run():
@@ -135,7 +137,8 @@ def run(raiser: address):
"""
c1 = get_contract(raiser)
c2 = get_contract(caller)
- assert_tx_failed(lambda: c2.run(c1.address), exc_text=revert_string)
+ with tx_failed(exc_text=revert_string):
+ c2.run(c1.address)
@pytest.mark.parametrize("a,b", [(3, 3), (4, 3), (3, 4), (32, 32), (33, 33), (64, 64)])
@@ -169,7 +172,7 @@ def get_array(arg1: address) -> (Bytes[{a}], int128, Bytes[{b}]):
@pytest.mark.parametrize("a,b", [(18, 7), (18, 18), (19, 6), (64, 6), (7, 19)])
@pytest.mark.parametrize("c,d", [(19, 7), (64, 64)])
-def test_tuple_with_bytes_too_long(get_contract, assert_tx_failed, a, c, b, d):
+def test_tuple_with_bytes_too_long(get_contract, tx_failed, a, c, b, d):
contract_1 = f"""
@external
def array() -> (Bytes[{c}], int128, Bytes[{d}]):
@@ -193,10 +196,11 @@ def get_array(arg1: address) -> (Bytes[{a}], int128, Bytes[{b}]):
c2 = get_contract(contract_2)
assert c.array() == [b"nineteen characters", 255, b"seven!!"]
- assert_tx_failed(lambda: c2.get_array(c.address))
+ with tx_failed():
+ c2.get_array(c.address)
-def test_tuple_with_bytes_too_long_two(get_contract, assert_tx_failed):
+def test_tuple_with_bytes_too_long_two(get_contract, tx_failed):
contract_1 = """
@external
def array() -> (Bytes[30], int128, Bytes[30]):
@@ -220,7 +224,8 @@ def get_array(arg1: address) -> (Bytes[30], int128, Bytes[3]):
c2 = get_contract(contract_2)
assert c.array() == [b"nineteen characters", 255, b"seven!!"]
- assert_tx_failed(lambda: c2.get_array(c.address))
+ with tx_failed():
+ c2.get_array(c.address)
@pytest.mark.parametrize("length", [8, 256])
@@ -246,7 +251,7 @@ def bar(arg1: address) -> uint8:
assert c2.bar(c.address) == 255
-def test_uint8_too_long(get_contract, assert_tx_failed):
+def test_uint8_too_long(get_contract, tx_failed):
contract_1 = """
@external
def foo() -> uint256:
@@ -265,7 +270,8 @@ def bar(arg1: address) -> uint8:
"""
c2 = get_contract(contract_2)
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("a,b", [(8, 8), (8, 256), (256, 8), (256, 256)])
@@ -298,7 +304,7 @@ def bar(arg1: address) -> (uint{a}, Bytes[3], uint{b}):
@pytest.mark.parametrize("a,b", [(8, 256), (256, 8), (256, 256)])
-def test_tuple_with_uint8_too_long(get_contract, assert_tx_failed, a, b):
+def test_tuple_with_uint8_too_long(get_contract, tx_failed, a, b):
contract_1 = f"""
@external
def foo() -> (uint{a}, Bytes[3], uint{b}):
@@ -322,11 +328,12 @@ def bar(arg1: address) -> (uint8, Bytes[3], uint8):
c2 = get_contract(contract_2)
assert c.foo() == [int(f"{(2**a)-1}"), b"dog", int(f"{(2**b)-1}")]
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("a,b", [(8, 256), (256, 8)])
-def test_tuple_with_uint8_too_long_two(get_contract, assert_tx_failed, a, b):
+def test_tuple_with_uint8_too_long_two(get_contract, tx_failed, a, b):
contract_1 = f"""
@external
def foo() -> (uint{b}, Bytes[3], uint{a}):
@@ -350,7 +357,8 @@ def bar(arg1: address) -> (uint{a}, Bytes[3], uint{b}):
c2 = get_contract(contract_2)
assert c.foo() == [int(f"{(2**b)-1}"), b"dog", int(f"{(2**a)-1}")]
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("length", [128, 256])
@@ -376,11 +384,11 @@ def bar(arg1: address) -> int128:
assert c2.bar(c.address) == 1
-def test_int128_too_long(get_contract, assert_tx_failed):
+def test_int128_too_long(get_contract, tx_failed):
contract_1 = """
@external
def foo() -> int256:
- return (2**255)-1
+ return max_value(int256)
"""
c = get_contract(contract_1)
@@ -395,7 +403,8 @@ def bar(arg1: address) -> int128:
"""
c2 = get_contract(contract_2)
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("a,b", [(128, 128), (128, 256), (256, 128), (256, 256)])
@@ -428,7 +437,7 @@ def bar(arg1: address) -> (int{a}, Bytes[3], int{b}):
@pytest.mark.parametrize("a,b", [(128, 256), (256, 128), (256, 256)])
-def test_tuple_with_int128_too_long(get_contract, assert_tx_failed, a, b):
+def test_tuple_with_int128_too_long(get_contract, tx_failed, a, b):
contract_1 = f"""
@external
def foo() -> (int{a}, Bytes[3], int{b}):
@@ -452,11 +461,12 @@ def bar(arg1: address) -> (int128, Bytes[3], int128):
c2 = get_contract(contract_2)
assert c.foo() == [int(f"{(2**(a-1))-1}"), b"dog", int(f"{(2**(b-1))-1}")]
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("a,b", [(128, 256), (256, 128)])
-def test_tuple_with_int128_too_long_two(get_contract, assert_tx_failed, a, b):
+def test_tuple_with_int128_too_long_two(get_contract, tx_failed, a, b):
contract_1 = f"""
@external
def foo() -> (int{b}, Bytes[3], int{a}):
@@ -480,7 +490,8 @@ def bar(arg1: address) -> (int{a}, Bytes[3], int{b}):
c2 = get_contract(contract_2)
assert c.foo() == [int(f"{(2**(b-1))-1}"), b"dog", int(f"{(2**(a-1))-1}")]
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("type", ["uint8", "uint256", "int128", "int256"])
@@ -506,7 +517,7 @@ def bar(arg1: address) -> decimal:
assert c2.bar(c.address) == Decimal("1e-10")
-def test_decimal_too_long(get_contract, assert_tx_failed):
+def test_decimal_too_long(get_contract, tx_failed):
contract_1 = """
@external
def foo() -> uint256:
@@ -525,7 +536,8 @@ def bar(arg1: address) -> decimal:
"""
c2 = get_contract(contract_2)
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("a", ["uint8", "uint256", "int128", "int256"])
@@ -559,7 +571,7 @@ def bar(arg1: address) -> (decimal, Bytes[3], decimal):
@pytest.mark.parametrize("a,b", [(8, 256), (256, 8), (256, 256)])
-def test_tuple_with_decimal_too_long(get_contract, assert_tx_failed, a, b):
+def test_tuple_with_decimal_too_long(get_contract, tx_failed, a, b):
contract_1 = f"""
@external
def foo() -> (uint{a}, Bytes[3], uint{b}):
@@ -583,7 +595,8 @@ def bar(arg1: address) -> (decimal, Bytes[3], decimal):
c2 = get_contract(contract_2)
assert c.foo() == [2 ** (a - 1), b"dog", 2 ** (b - 1)]
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("type", ["uint8", "uint256", "int128", "int256"])
@@ -609,7 +622,7 @@ def bar(arg1: address) -> bool:
assert c2.bar(c.address) is True
-def test_bool_too_long(get_contract, assert_tx_failed):
+def test_bool_too_long(get_contract, tx_failed):
contract_1 = """
@external
def foo() -> uint256:
@@ -628,7 +641,8 @@ def bar(arg1: address) -> bool:
"""
c2 = get_contract(contract_2)
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("a", ["uint8", "uint256", "int128", "int256"])
@@ -662,7 +676,7 @@ def bar(arg1: address) -> (bool, Bytes[3], bool):
@pytest.mark.parametrize("a", ["uint8", "uint256", "int128", "int256"])
@pytest.mark.parametrize("b", ["uint8", "uint256", "int128", "int256"])
-def test_tuple_with_bool_too_long(get_contract, assert_tx_failed, a, b):
+def test_tuple_with_bool_too_long(get_contract, tx_failed, a, b):
contract_1 = f"""
@external
def foo() -> ({a}, Bytes[3], {b}):
@@ -686,7 +700,8 @@ def bar(arg1: address) -> (bool, Bytes[3], bool):
c2 = get_contract(contract_2)
assert c.foo() == [1, b"dog", 2]
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("type", ["uint8", "int128", "uint256", "int256"])
@@ -736,7 +751,7 @@ def bar(arg1: address) -> address:
@pytest.mark.parametrize("type", ["uint256", "int256"])
-def test_address_too_long(get_contract, assert_tx_failed, type):
+def test_address_too_long(get_contract, tx_failed, type):
contract_1 = f"""
@external
def foo() -> {type}:
@@ -755,7 +770,8 @@ def bar(arg1: address) -> address:
"""
c2 = get_contract(contract_2)
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
@pytest.mark.parametrize("a", ["uint8", "int128", "uint256", "int256"])
@@ -775,9 +791,9 @@ def foo() -> (address, Bytes[3], address): view
@external
def bar(arg1: address) -> (address, Bytes[3], address):
- a: address = ZERO_ADDRESS
+ a: address = empty(address)
b: Bytes[3] = b""
- c: address = ZERO_ADDRESS
+ c: address = empty(address)
a, b, c = Foo(arg1).foo()
return a, b, c
"""
@@ -808,9 +824,9 @@ def foo() -> (address, Bytes[3], address): view
@external
def bar(arg1: address) -> (address, Bytes[3], address):
- a: address = ZERO_ADDRESS
+ a: address = empty(address)
b: Bytes[3] = b""
- c: address = ZERO_ADDRESS
+ c: address = empty(address)
a, b, c = Foo(arg1).foo()
return a, b, c
"""
@@ -826,7 +842,7 @@ def bar(arg1: address) -> (address, Bytes[3], address):
@pytest.mark.parametrize("a", ["uint256", "int256"])
@pytest.mark.parametrize("b", ["uint256", "int256"])
-def test_tuple_with_address_too_long(get_contract, assert_tx_failed, a, b):
+def test_tuple_with_address_too_long(get_contract, tx_failed, a, b):
contract_1 = f"""
@external
def foo() -> ({a}, Bytes[3], {b}):
@@ -841,16 +857,17 @@ def foo() -> (address, Bytes[3], address): view
@external
def bar(arg1: address) -> (address, Bytes[3], address):
- a: address = ZERO_ADDRESS
+ a: address = empty(address)
b: Bytes[3] = b""
- c: address = ZERO_ADDRESS
+ c: address = empty(address)
a, b, c = Foo(arg1).foo()
return a, b, c
"""
c2 = get_contract(contract_2)
assert c.foo() == [(2**160) - 1, b"dog", 2**160]
- assert_tx_failed(lambda: c2.bar(c.address))
+ with tx_failed():
+ c2.bar(c.address)
def test_external_contract_call_state_change(get_contract):
@@ -1095,7 +1112,7 @@ def _expr(x: address) -> int128:
assert c2._expr(c2.address) == 1
-def test_invalid_nonexistent_contract_call(w3, assert_tx_failed, get_contract):
+def test_invalid_nonexistent_contract_call(w3, tx_failed, get_contract):
contract_1 = """
@external
def bar() -> int128:
@@ -1115,11 +1132,13 @@ def foo(x: address) -> int128:
c2 = get_contract(contract_2)
assert c2.foo(c1.address) == 1
- assert_tx_failed(lambda: c2.foo(w3.eth.accounts[0]))
- assert_tx_failed(lambda: c2.foo(w3.eth.accounts[3]))
+ with tx_failed():
+ c2.foo(w3.eth.accounts[0])
+ with tx_failed():
+ c2.foo(w3.eth.accounts[3])
-def test_invalid_contract_reference_declaration(assert_tx_failed, get_contract):
+def test_invalid_contract_reference_declaration(tx_failed, get_contract):
contract = """
interface Bar:
get_magic_number: 1
@@ -1130,19 +1149,21 @@ def test_invalid_contract_reference_declaration(assert_tx_failed, get_contract):
def __init__():
pass
"""
- assert_tx_failed(lambda: get_contract(contract), exception=StructureException)
+ with tx_failed(exception=StructureException):
+ get_contract(contract)
-def test_invalid_contract_reference_call(assert_tx_failed, get_contract):
+def test_invalid_contract_reference_call(tx_failed, get_contract):
contract = """
@external
def bar(arg1: address, arg2: int128) -> int128:
return Foo(arg1).foo(arg2)
"""
- assert_tx_failed(lambda: get_contract(contract), exception=UndeclaredDefinition)
+ with pytest.raises(UndeclaredDefinition):
+ compile_code(contract)
-def test_invalid_contract_reference_return_type(assert_tx_failed, get_contract):
+def test_invalid_contract_reference_return_type(tx_failed, get_contract):
contract = """
interface Foo:
def foo(arg2: int128) -> invalid: view
@@ -1151,7 +1172,8 @@ def foo(arg2: int128) -> invalid: view
def bar(arg1: address, arg2: int128) -> int128:
return Foo(arg1).foo(arg2)
"""
- assert_tx_failed(lambda: get_contract(contract), exception=UnknownType)
+ with pytest.raises(UnknownType):
+ compile_code(contract)
def test_external_contract_call_declaration_expr(get_contract):
@@ -1378,7 +1400,7 @@ def get_lucky(amount_to_send: uint256) -> int128:
assert w3.eth.get_balance(c2.address) == 250
-def test_external_call_with_gas(assert_tx_failed, get_contract_with_gas_estimation):
+def test_external_call_with_gas(tx_failed, get_contract_with_gas_estimation):
contract_1 = """
@external
def get_lucky() -> int128:
@@ -1406,7 +1428,8 @@ def get_lucky(gas_amount: uint256) -> int128:
c2.set_contract(c1.address, transact={})
assert c2.get_lucky(1000) == 656598
- assert_tx_failed(lambda: c2.get_lucky(50)) # too little gas.
+ with tx_failed():
+ c2.get_lucky(50) # too little gas.
def test_skip_contract_check(get_contract_with_gas_estimation):
@@ -1538,7 +1561,7 @@ def out_literals() -> (int128, address, Bytes[10]) : view
@external
def test(addr: address) -> (int128, address, Bytes[10]):
a: int128 = 0
- b: address = ZERO_ADDRESS
+ b: address = empty(address)
c: Bytes[10] = b""
(a, b, c) = Test(addr).out_literals()
return a, b,c
@@ -2240,7 +2263,7 @@ def get_array(arg1: address) -> int128[3]:
assert c2.get_array(c.address) == [0, 0, 0]
-def test_returndatasize_too_short(get_contract, assert_tx_failed):
+def test_returndatasize_too_short(get_contract, tx_failed):
contract_1 = """
@external
def bar(a: int128) -> int128:
@@ -2256,10 +2279,11 @@ def foo(_addr: address):
"""
c1 = get_contract(contract_1)
c2 = get_contract(contract_2)
- assert_tx_failed(lambda: c2.foo(c1.address))
+ with tx_failed():
+ c2.foo(c1.address)
-def test_returndatasize_empty(get_contract, assert_tx_failed):
+def test_returndatasize_empty(get_contract, tx_failed):
contract_1 = """
@external
def bar(a: int128):
@@ -2275,7 +2299,8 @@ def foo(_addr: address) -> int128:
"""
c1 = get_contract(contract_1)
c2 = get_contract(contract_2)
- assert_tx_failed(lambda: c2.foo(c1.address))
+ with tx_failed():
+ c2.foo(c1.address)
def test_returndatasize_too_long(get_contract):
@@ -2299,7 +2324,7 @@ def foo(_addr: address) -> int128:
assert c2.foo(c1.address) == 456
-def test_no_returndata(get_contract, assert_tx_failed):
+def test_no_returndata(get_contract, tx_failed):
contract_1 = """
@external
def bar(a: int128) -> int128:
@@ -2321,10 +2346,11 @@ def foo(_addr: address, _addr2: address) -> int128:
c2 = get_contract(contract_2)
assert c2.foo(c1.address, c1.address) == 123
- assert_tx_failed(lambda: c2.foo(c1.address, "0x1234567890123456789012345678901234567890"))
+ with tx_failed():
+ c2.foo(c1.address, "0x1234567890123456789012345678901234567890")
-def test_default_override(get_contract, assert_tx_failed):
+def test_default_override(get_contract, tx_failed):
bad_erc20_code = """
@external
def transfer(receiver: address, amount: uint256):
@@ -2358,17 +2384,20 @@ def transferBorked(erc20: ERC20, receiver: address, amount: uint256):
c = get_contract(code)
# demonstrate transfer failing
- assert_tx_failed(lambda: c.transferBorked(bad_erc20.address, c.address, 0))
+ with tx_failed():
+ c.transferBorked(bad_erc20.address, c.address, 0)
# would fail without default_return_value
assert c.safeTransfer(bad_erc20.address, c.address, 0) == 7
# check that `default_return_value` does not stomp valid returndata.
negative_contract = get_contract(negative_transfer_code)
- assert_tx_failed(lambda: c.safeTransfer(negative_contract.address, c.address, 0))
+ with tx_failed():
+ c.safeTransfer(negative_contract.address, c.address, 0)
# default_return_value should fail on EOAs (addresses with no code)
random_address = "0x0000000000000000000000000000000000001234"
- assert_tx_failed(lambda: c.safeTransfer(random_address, c.address, 1))
+ with tx_failed():
+ c.safeTransfer(random_address, c.address, 1)
# in this case, the extcodesize check runs after the token contract
# selfdestructs. however, extcodesize still returns nonzero until
@@ -2378,7 +2407,7 @@ def transferBorked(erc20: ERC20, receiver: address, amount: uint256):
assert c.safeTransfer(self_destructing_contract.address, c.address, 0) == 7
-def test_default_override2(get_contract, assert_tx_failed):
+def test_default_override2(get_contract, tx_failed):
bad_code_1 = """
@external
def return_64_bytes() -> bool:
@@ -2407,7 +2436,8 @@ def bar(foo: Foo):
c = get_contract(code)
# fails due to returndatasize being nonzero but also lt 64
- assert_tx_failed(lambda: c.bar(bad_1.address))
+ with tx_failed():
+ c.bar(bad_1.address)
c.bar(bad_2.address)
@@ -2456,7 +2486,7 @@ def do_stuff(f: Foo) -> uint256:
@pytest.mark.parametrize("typ,val", [("address", TEST_ADDR)])
-def test_calldata_clamp(w3, get_contract, assert_tx_failed, keccak, typ, val):
+def test_calldata_clamp(w3, get_contract, tx_failed, keccak, typ, val):
code = f"""
@external
def foo(a: {typ}):
@@ -2469,7 +2499,8 @@ def foo(a: {typ}):
# Static size is short by 1 byte
malformed = data[:-2]
- assert_tx_failed(lambda: w3.eth.send_transaction({"to": c1.address, "data": malformed}))
+ with tx_failed():
+ w3.eth.send_transaction({"to": c1.address, "data": malformed})
# Static size is exact
w3.eth.send_transaction({"to": c1.address, "data": data})
@@ -2479,7 +2510,7 @@ def foo(a: {typ}):
@pytest.mark.parametrize("typ,val", [("address", ([TEST_ADDR] * 3, "vyper"))])
-def test_dynamic_calldata_clamp(w3, get_contract, assert_tx_failed, keccak, typ, val):
+def test_dynamic_calldata_clamp(w3, get_contract, tx_failed, keccak, typ, val):
code = f"""
@external
def foo(a: DynArray[{typ}, 3], b: String[5]):
@@ -2493,7 +2524,8 @@ def foo(a: DynArray[{typ}, 3], b: String[5]):
# Dynamic size is short by 1 byte
malformed = data[:264]
- assert_tx_failed(lambda: w3.eth.send_transaction({"to": c1.address, "data": malformed}))
+ with tx_failed():
+ w3.eth.send_transaction({"to": c1.address, "data": malformed})
# Dynamic size is at least minimum (132 bytes * 2 + 2 (for 0x) = 266)
valid = data[:266]
diff --git a/tests/parser/features/external_contracts/test_modifiable_external_contract_calls.py b/tests/functional/codegen/calling_convention/test_modifiable_external_contract_calls.py
similarity index 91%
rename from tests/parser/features/external_contracts/test_modifiable_external_contract_calls.py
rename to tests/functional/codegen/calling_convention/test_modifiable_external_contract_calls.py
index 4c321442f4..e6b2402016 100644
--- a/tests/parser/features/external_contracts/test_modifiable_external_contract_calls.py
+++ b/tests/functional/codegen/calling_convention/test_modifiable_external_contract_calls.py
@@ -1,7 +1,7 @@
from vyper.exceptions import StructureException, SyntaxException, UnknownType
-def test_external_contract_call_declaration_expr(get_contract, assert_tx_failed):
+def test_external_contract_call_declaration_expr(get_contract, tx_failed):
contract_1 = """
lucky: public(int128)
@@ -39,11 +39,12 @@ def static_set_lucky(_lucky: int128):
c2.modifiable_set_lucky(7, transact={})
assert c1.lucky() == 7
# Fails attempting a state change after a call to a static address
- assert_tx_failed(lambda: c2.static_set_lucky(5, transact={}))
+ with tx_failed():
+ c2.static_set_lucky(5, transact={})
assert c1.lucky() == 7
-def test_external_contract_call_declaration_stmt(get_contract, assert_tx_failed):
+def test_external_contract_call_declaration_stmt(get_contract, tx_failed):
contract_1 = """
lucky: public(int128)
@@ -83,11 +84,12 @@ def static_set_lucky(_lucky: int128):
c2.modifiable_set_lucky(7, transact={})
assert c1.lucky() == 7
# Fails attempting a state change after a call to a static address
- assert_tx_failed(lambda: c2.static_set_lucky(5, transact={}))
+ with tx_failed():
+ c2.static_set_lucky(5, transact={})
assert c1.lucky() == 7
-def test_multiple_contract_state_changes(get_contract, assert_tx_failed):
+def test_multiple_contract_state_changes(get_contract, tx_failed):
contract_1 = """
lucky: public(int128)
@@ -161,9 +163,12 @@ def static_modifiable_set_lucky(_lucky: int128):
assert c1.lucky() == 0
c3.modifiable_modifiable_set_lucky(7, transact={})
assert c1.lucky() == 7
- assert_tx_failed(lambda: c3.modifiable_static_set_lucky(6, transact={}))
- assert_tx_failed(lambda: c3.static_modifiable_set_lucky(6, transact={}))
- assert_tx_failed(lambda: c3.static_static_set_lucky(6, transact={}))
+ with tx_failed():
+ c3.modifiable_static_set_lucky(6, transact={})
+ with tx_failed():
+ c3.static_modifiable_set_lucky(6, transact={})
+ with tx_failed():
+ c3.static_static_set_lucky(6, transact={})
assert c1.lucky() == 7
diff --git a/tests/parser/functions/test_return.py b/tests/functional/codegen/calling_convention/test_return.py
similarity index 100%
rename from tests/parser/functions/test_return.py
rename to tests/functional/codegen/calling_convention/test_return.py
diff --git a/tests/parser/functions/test_return_struct.py b/tests/functional/codegen/calling_convention/test_return_struct.py
similarity index 98%
rename from tests/parser/functions/test_return_struct.py
rename to tests/functional/codegen/calling_convention/test_return_struct.py
index 425caedb75..cdd8342d8a 100644
--- a/tests/parser/functions/test_return_struct.py
+++ b/tests/functional/codegen/calling_convention/test_return_struct.py
@@ -17,7 +17,7 @@ def test() -> Voter:
return a
"""
- out = compile_code(code, ["abi"])
+ out = compile_code(code, output_formats=["abi"])
abi = out["abi"][0]
assert abi["name"] == "test"
@@ -38,7 +38,7 @@ def test() -> Voter:
return a
"""
- out = compile_code(code, ["abi"])
+ out = compile_code(code, output_formats=["abi"])
abi = out["abi"][0]
assert abi["name"] == "test"
diff --git a/tests/parser/functions/test_return_tuple.py b/tests/functional/codegen/calling_convention/test_return_tuple.py
similarity index 94%
rename from tests/parser/functions/test_return_tuple.py
rename to tests/functional/codegen/calling_convention/test_return_tuple.py
index 87b7cdcde3..266555ead6 100644
--- a/tests/parser/functions/test_return_tuple.py
+++ b/tests/functional/codegen/calling_convention/test_return_tuple.py
@@ -1,5 +1,6 @@
import pytest
+from vyper import compile_code
from vyper.exceptions import TypeMismatch
pytestmark = pytest.mark.usefixtures("memory_mocker")
@@ -99,7 +100,7 @@ def out_literals() -> (int128, address, Bytes[10]):
@external
def test() -> (int128, address, Bytes[10]):
a: int128 = 0
- b: address = ZERO_ADDRESS
+ b: address = empty(address)
c: Bytes[10] = b""
(a, b, c) = self._out_literals()
return a, b, c
@@ -138,7 +139,7 @@ def test2() -> (int128, address):
@external
def test3() -> (address, int128):
- x: address = ZERO_ADDRESS
+ x: address = empty(address)
self.a, self.c, x, self.d = self._out_literals()
return x, self.a
"""
@@ -152,11 +153,11 @@ def test3() -> (address, int128):
assert c.test3() == [c.out_literals()[2], 1]
-def test_tuple_return_typecheck(assert_tx_failed, get_contract_with_gas_estimation):
+def test_tuple_return_typecheck(tx_failed, get_contract_with_gas_estimation):
code = """
@external
def getTimeAndBalance() -> (bool, address):
return block.timestamp, self.balance
"""
-
- assert_tx_failed(lambda: get_contract_with_gas_estimation(code), TypeMismatch)
+ with pytest.raises(TypeMismatch):
+ compile_code(code)
diff --git a/tests/parser/features/external_contracts/test_self_call_struct.py b/tests/functional/codegen/calling_convention/test_self_call_struct.py
similarity index 100%
rename from tests/parser/features/external_contracts/test_self_call_struct.py
rename to tests/functional/codegen/calling_convention/test_self_call_struct.py
diff --git a/tests/functional/codegen/test_struct_return.py b/tests/functional/codegen/calling_convention/test_struct_return.py
similarity index 100%
rename from tests/functional/codegen/test_struct_return.py
rename to tests/functional/codegen/calling_convention/test_struct_return.py
diff --git a/tests/functional/codegen/test_tuple_return.py b/tests/functional/codegen/calling_convention/test_tuple_return.py
similarity index 100%
rename from tests/functional/codegen/test_tuple_return.py
rename to tests/functional/codegen/calling_convention/test_tuple_return.py
diff --git a/tests/parser/functions/test_block_number.py b/tests/functional/codegen/environment_variables/test_block_number.py
similarity index 100%
rename from tests/parser/functions/test_block_number.py
rename to tests/functional/codegen/environment_variables/test_block_number.py
diff --git a/tests/parser/functions/test_block.py b/tests/functional/codegen/environment_variables/test_blockhash.py
similarity index 77%
rename from tests/parser/functions/test_block.py
rename to tests/functional/codegen/environment_variables/test_blockhash.py
index b92c17a561..68db053b12 100644
--- a/tests/parser/functions/test_block.py
+++ b/tests/functional/codegen/environment_variables/test_blockhash.py
@@ -23,7 +23,7 @@ def foo() -> bytes32:
assert_compile_failed(lambda: get_contract_with_gas_estimation(code))
-def test_too_old_blockhash(assert_tx_failed, get_contract_with_gas_estimation, w3):
+def test_too_old_blockhash(tx_failed, get_contract_with_gas_estimation, w3):
w3.testing.mine(257)
code = """
@external
@@ -31,14 +31,16 @@ def get_50_blockhash() -> bytes32:
return blockhash(block.number - 257)
"""
c = get_contract_with_gas_estimation(code)
- assert_tx_failed(lambda: c.get_50_blockhash())
+ with tx_failed():
+ c.get_50_blockhash()
-def test_non_existing_blockhash(assert_tx_failed, get_contract_with_gas_estimation):
+def test_non_existing_blockhash(tx_failed, get_contract_with_gas_estimation):
code = """
@external
def get_future_blockhash() -> bytes32:
return blockhash(block.number + 1)
"""
c = get_contract_with_gas_estimation(code)
- assert_tx_failed(lambda: c.get_future_blockhash())
+ with tx_failed():
+ c.get_future_blockhash()
diff --git a/tests/parser/functions/test_tx.py b/tests/functional/codegen/environment_variables/test_tx.py
similarity index 100%
rename from tests/parser/functions/test_tx.py
rename to tests/functional/codegen/environment_variables/test_tx.py
diff --git a/tests/parser/features/decorators/test_nonreentrant.py b/tests/functional/codegen/features/decorators/test_nonreentrant.py
similarity index 90%
rename from tests/parser/features/decorators/test_nonreentrant.py
rename to tests/functional/codegen/features/decorators/test_nonreentrant.py
index ac73b35bec..9329605678 100644
--- a/tests/parser/features/decorators/test_nonreentrant.py
+++ b/tests/functional/codegen/features/decorators/test_nonreentrant.py
@@ -5,7 +5,7 @@
# TODO test functions in this module across all evm versions
# once we have cancun support.
-def test_nonreentrant_decorator(get_contract, assert_tx_failed):
+def test_nonreentrant_decorator(get_contract, tx_failed):
calling_contract_code = """
interface SpecialContract:
def unprotected_function(val: String[100], do_callback: bool): nonpayable
@@ -98,20 +98,23 @@ def unprotected_function(val: String[100], do_callback: bool):
assert reentrant_contract.special_value() == "some value"
assert reentrant_contract.protected_view_fn() == "some value"
- assert_tx_failed(lambda: reentrant_contract.protected_function("zzz value", True, transact={}))
+ with tx_failed():
+ reentrant_contract.protected_function("zzz value", True, transact={})
reentrant_contract.protected_function2("another value", False, transact={})
assert reentrant_contract.special_value() == "another value"
- assert_tx_failed(lambda: reentrant_contract.protected_function2("zzz value", True, transact={}))
+ with tx_failed():
+ reentrant_contract.protected_function2("zzz value", True, transact={})
reentrant_contract.protected_function3("another value", False, transact={})
assert reentrant_contract.special_value() == "another value"
- assert_tx_failed(lambda: reentrant_contract.protected_function3("zzz value", True, transact={}))
+ with tx_failed():
+ reentrant_contract.protected_function3("zzz value", True, transact={})
-def test_nonreentrant_decorator_for_default(w3, get_contract, assert_tx_failed):
+def test_nonreentrant_decorator_for_default(w3, get_contract, tx_failed):
calling_contract_code = """
@external
def send_funds(_amount: uint256):
@@ -142,7 +145,7 @@ def set_callback(c: address):
@external
@payable
-@nonreentrant('default')
+@nonreentrant("lock")
def protected_function(val: String[100], do_callback: bool) -> uint256:
self.special_value = val
_amount: uint256 = msg.value
@@ -166,7 +169,7 @@ def unprotected_function(val: String[100], do_callback: bool):
@external
@payable
-@nonreentrant('default')
+@nonreentrant("lock")
def __default__():
pass
"""
@@ -196,9 +199,8 @@ def __default__():
assert w3.eth.get_balance(calling_contract.address) == 2000
# Test protected function with callback to default.
- assert_tx_failed(
- lambda: reentrant_contract.protected_function("zzz value", True, transact={"value": 1000})
- )
+ with tx_failed():
+ reentrant_contract.protected_function("zzz value", True, transact={"value": 1000})
def test_disallow_on_init_function(get_contract):
diff --git a/tests/parser/features/decorators/test_payable.py b/tests/functional/codegen/features/decorators/test_payable.py
similarity index 90%
rename from tests/parser/features/decorators/test_payable.py
rename to tests/functional/codegen/features/decorators/test_payable.py
index 55c60236f4..ced58e1af0 100644
--- a/tests/parser/features/decorators/test_payable.py
+++ b/tests/functional/codegen/features/decorators/test_payable.py
@@ -177,14 +177,13 @@ def baz() -> bool:
@pytest.mark.parametrize("code", nonpayable_code)
-def test_nonpayable_runtime_assertion(w3, keccak, assert_tx_failed, get_contract, code):
+def test_nonpayable_runtime_assertion(w3, keccak, tx_failed, get_contract, code):
c = get_contract(code)
c.foo(transact={"value": 0})
sig = keccak("foo()".encode()).hex()[:10]
- assert_tx_failed(
- lambda: w3.eth.send_transaction({"to": c.address, "data": sig, "value": 10**18})
- )
+ with tx_failed():
+ w3.eth.send_transaction({"to": c.address, "data": sig, "value": 10**18})
payable_code = [
@@ -352,10 +351,10 @@ def __default__():
"""
c = get_contract(code)
- w3.eth.send_transaction({"to": c.address, "value": 100, "data": "0x12345678"}),
+ w3.eth.send_transaction({"to": c.address, "value": 100, "data": "0x12345678"})
-def test_nonpayable_default_func_invalid_calldata(get_contract, w3, assert_tx_failed):
+def test_nonpayable_default_func_invalid_calldata(get_contract, w3, tx_failed):
code = """
@external
@payable
@@ -369,12 +368,11 @@ def __default__():
c = get_contract(code)
w3.eth.send_transaction({"to": c.address, "value": 0, "data": "0x12345678"})
- assert_tx_failed(
- lambda: w3.eth.send_transaction({"to": c.address, "value": 100, "data": "0x12345678"})
- )
+ with tx_failed():
+ w3.eth.send_transaction({"to": c.address, "value": 100, "data": "0x12345678"})
-def test_batch_nonpayable(get_contract, w3, assert_tx_failed):
+def test_batch_nonpayable(get_contract, w3, tx_failed):
code = """
@external
def foo() -> bool:
@@ -390,6 +388,5 @@ def __default__():
data = bytes([1, 2, 3, 4])
for i in range(5):
calldata = "0x" + data[:i].hex()
- assert_tx_failed(
- lambda: w3.eth.send_transaction({"to": c.address, "value": 100, "data": calldata})
- )
+ with tx_failed():
+ w3.eth.send_transaction({"to": c.address, "value": 100, "data": calldata})
diff --git a/tests/parser/features/decorators/test_private.py b/tests/functional/codegen/features/decorators/test_private.py
similarity index 99%
rename from tests/parser/features/decorators/test_private.py
rename to tests/functional/codegen/features/decorators/test_private.py
index 7c92f72af9..39ea1bb9ae 100644
--- a/tests/parser/features/decorators/test_private.py
+++ b/tests/functional/codegen/features/decorators/test_private.py
@@ -304,7 +304,7 @@ def test(a: bytes32) -> (bytes32, uint256, int128):
b: uint256 = 1
c: int128 = 1
d: int128 = 123
- f: bytes32 = EMPTY_BYTES32
+ f: bytes32 = empty(bytes32)
f, b, c = self._test(a)
assert d == 123
return f, b, c
@@ -449,7 +449,7 @@ def whoami() -> address:
assert logged_addr == addr, "oh no"
-def test_nested_static_params_only(get_contract, assert_tx_failed):
+def test_nested_static_params_only(get_contract, tx_failed):
code1 = """
@internal
@view
diff --git a/tests/parser/features/decorators/test_public.py b/tests/functional/codegen/features/decorators/test_public.py
similarity index 100%
rename from tests/parser/features/decorators/test_public.py
rename to tests/functional/codegen/features/decorators/test_public.py
diff --git a/tests/parser/features/decorators/test_pure.py b/tests/functional/codegen/features/decorators/test_pure.py
similarity index 100%
rename from tests/parser/features/decorators/test_pure.py
rename to tests/functional/codegen/features/decorators/test_pure.py
diff --git a/tests/parser/features/decorators/test_view.py b/tests/functional/codegen/features/decorators/test_view.py
similarity index 100%
rename from tests/parser/features/decorators/test_view.py
rename to tests/functional/codegen/features/decorators/test_view.py
diff --git a/tests/parser/features/iteration/test_break.py b/tests/functional/codegen/features/iteration/test_break.py
similarity index 100%
rename from tests/parser/features/iteration/test_break.py
rename to tests/functional/codegen/features/iteration/test_break.py
diff --git a/tests/parser/features/iteration/test_continue.py b/tests/functional/codegen/features/iteration/test_continue.py
similarity index 100%
rename from tests/parser/features/iteration/test_continue.py
rename to tests/functional/codegen/features/iteration/test_continue.py
diff --git a/tests/parser/features/iteration/test_for_in_list.py b/tests/functional/codegen/features/iteration/test_for_in_list.py
similarity index 96%
rename from tests/parser/features/iteration/test_for_in_list.py
rename to tests/functional/codegen/features/iteration/test_for_in_list.py
index bfd960a787..bc1a12ae9e 100644
--- a/tests/parser/features/iteration/test_for_in_list.py
+++ b/tests/functional/codegen/features/iteration/test_for_in_list.py
@@ -1,3 +1,4 @@
+import re
from decimal import Decimal
import pytest
@@ -230,7 +231,7 @@ def iterate_return_second() -> address:
count += 1
if count == 2:
return i
- return ZERO_ADDRESS
+ return empty(address)
"""
c = get_contract_with_gas_estimation(code)
@@ -700,13 +701,16 @@ def foo():
""",
StateAccessViolation,
),
- """
+ (
+ """
@external
def foo():
a: int128 = 6
for i in range(a,a-3):
pass
""",
+ StateAccessViolation,
+ ),
# invalid argument length
(
"""
@@ -789,10 +793,13 @@ def test_for() -> int128:
),
]
+BAD_CODE = [code if isinstance(code, tuple) else (code, StructureException) for code in BAD_CODE]
+for_code_regex = re.compile(r"for .+ in (.*):")
+bad_code_names = [
+ f"{i} {for_code_regex.search(code).group(1)}" for i, (code, _) in enumerate(BAD_CODE)
+]
+
-@pytest.mark.parametrize("code", BAD_CODE)
-def test_bad_code(assert_compile_failed, get_contract, code):
- err = StructureException
- if not isinstance(code, str):
- code, err = code
+@pytest.mark.parametrize("code,err", BAD_CODE, ids=bad_code_names)
+def test_bad_code(assert_compile_failed, get_contract, code, err):
assert_compile_failed(lambda: get_contract(code), err)
diff --git a/tests/parser/features/iteration/test_for_range.py b/tests/functional/codegen/features/iteration/test_for_range.py
similarity index 68%
rename from tests/parser/features/iteration/test_for_range.py
rename to tests/functional/codegen/features/iteration/test_for_range.py
index 395dd28231..e946447285 100644
--- a/tests/parser/features/iteration/test_for_range.py
+++ b/tests/functional/codegen/features/iteration/test_for_range.py
@@ -14,21 +14,118 @@ def repeat(z: int128) -> int128:
assert c.repeat(9) == 54
-def test_range_bound(get_contract, assert_tx_failed):
+def test_range_bound(get_contract, tx_failed):
code = """
@external
def repeat(n: uint256) -> uint256:
x: uint256 = 0
for i in range(n, bound=6):
- x += i
+ x += i + 1
return x
"""
c = get_contract(code)
for n in range(7):
- assert c.repeat(n) == sum(range(n))
+ assert c.repeat(n) == sum(i + 1 for i in range(n))
# check codegen inserts assertion for n greater than bound
- assert_tx_failed(lambda: c.repeat(7))
+ with tx_failed():
+ c.repeat(7)
+
+
+def test_range_bound_constant_end(get_contract, tx_failed):
+ code = """
+@external
+def repeat(n: uint256) -> uint256:
+ x: uint256 = 0
+ for i in range(n, 7, bound=6):
+ x += i + 1
+ return x
+ """
+ c = get_contract(code)
+ for n in range(1, 5):
+ assert c.repeat(n) == sum(i + 1 for i in range(n, 7))
+
+ # check assertion for `start <= end`
+ with tx_failed():
+ c.repeat(8)
+ # check assertion for `start + bound <= end`
+ with tx_failed():
+ c.repeat(0)
+
+
+def test_range_bound_two_args(get_contract, tx_failed):
+ code = """
+@external
+def repeat(n: uint256) -> uint256:
+ x: uint256 = 0
+ for i in range(1, n, bound=6):
+ x += i + 1
+ return x
+ """
+ c = get_contract(code)
+ for n in range(1, 8):
+ assert c.repeat(n) == sum(i + 1 for i in range(1, n))
+
+ # check assertion for `start <= end`
+ with tx_failed():
+ c.repeat(0)
+
+ # check codegen inserts assertion for `start + bound <= end`
+ with tx_failed():
+ c.repeat(8)
+
+
+def test_range_bound_two_runtime_args(get_contract, tx_failed):
+ code = """
+@external
+def repeat(start: uint256, end: uint256) -> uint256:
+ x: uint256 = 0
+ for i in range(start, end, bound=6):
+ x += i
+ return x
+ """
+ c = get_contract(code)
+ for n in range(0, 7):
+ assert c.repeat(0, n) == sum(range(0, n))
+ assert c.repeat(n, n * 2) == sum(range(n, n * 2))
+
+ # check assertion for `start <= end`
+ with tx_failed():
+ c.repeat(1, 0)
+ with tx_failed():
+ c.repeat(7, 0)
+ with tx_failed():
+ c.repeat(8, 7)
+
+ # check codegen inserts assertion for `start + bound <= end`
+ with tx_failed():
+ c.repeat(0, 7)
+ with tx_failed():
+ c.repeat(14, 21)
+
+
+def test_range_overflow(get_contract, tx_failed):
+ code = """
+@external
+def get_last(start: uint256, end: uint256) -> uint256:
+ x: uint256 = 0
+ for i in range(start, end, bound=6):
+ x = i
+ return x
+ """
+ c = get_contract(code)
+ UINT_MAX = 2**256 - 1
+ assert c.get_last(UINT_MAX, UINT_MAX) == 0 # initial value of x
+
+ for n in range(1, 6):
+ assert c.get_last(UINT_MAX - n, UINT_MAX) == UINT_MAX - 1
+
+ # check for `start + bound <= end`, overflow cases
+ for n in range(1, 7):
+ with tx_failed():
+ c.get_last(UINT_MAX - n, 0)
+ with tx_failed():
+ c.get_last(UINT_MAX, UINT_MAX - n)
def test_digit_reverser(get_contract_with_gas_estimation):
@@ -88,7 +185,7 @@ def test_offset_repeater_2(get_contract_with_gas_estimation, typ):
@external
def sum(frm: {typ}, to: {typ}) -> {typ}:
out: {typ} = 0
- for i in range(frm, frm + 101):
+ for i in range(frm, frm + 101, bound=101):
if i == to:
break
out = out + i
@@ -145,26 +242,28 @@ def foo(a: {typ}) -> {typ}:
assert c.foo(100) == 31337
-# test that we can get to the upper range of an integer
@pytest.mark.parametrize("typ", ["uint8", "int128", "uint256"])
def test_for_range_edge(get_contract, typ):
+ """
+ Check that we can get to the upper range of an integer.
+ Note that to avoid overflow in the bounds check for range(),
+ we need to calculate i+1 inside the loop.
+ """
code = f"""
@external
def test():
found: bool = False
x: {typ} = max_value({typ})
- for i in range(x, x + 1):
- if i == max_value({typ}):
+ for i in range(x - 1, x, bound=1):
+ if i + 1 == max_value({typ}):
found = True
-
assert found
found = False
x = max_value({typ}) - 1
- for i in range(x, x + 2):
- if i == max_value({typ}):
+ for i in range(x - 1, x + 1, bound=2):
+ if i + 1 == max_value({typ}):
found = True
-
assert found
"""
c = get_contract(code)
@@ -172,16 +271,17 @@ def test():
@pytest.mark.parametrize("typ", ["uint8", "int128", "uint256"])
-def test_for_range_oob_check(get_contract, assert_tx_failed, typ):
+def test_for_range_oob_check(get_contract, tx_failed, typ):
code = f"""
@external
def test():
x: {typ} = max_value({typ})
- for i in range(x, x+2):
+ for i in range(x, x + 2, bound=2):
pass
"""
c = get_contract(code)
- assert_tx_failed(lambda: c.test())
+ with tx_failed():
+ c.test()
@pytest.mark.parametrize("typ", ["int128", "uint256"])
diff --git a/tests/parser/features/iteration/test_range_in.py b/tests/functional/codegen/features/iteration/test_range_in.py
similarity index 93%
rename from tests/parser/features/iteration/test_range_in.py
rename to tests/functional/codegen/features/iteration/test_range_in.py
index 062cd389a0..7540049778 100644
--- a/tests/parser/features/iteration/test_range_in.py
+++ b/tests/functional/codegen/features/iteration/test_range_in.py
@@ -110,7 +110,7 @@ def testin() -> bool:
assert_compile_failed(lambda: get_contract_with_gas_estimation(code), TypeMismatch)
-def test_ownership(w3, assert_tx_failed, get_contract_with_gas_estimation):
+def test_ownership(w3, tx_failed, get_contract_with_gas_estimation):
code = """
owners: address[2]
@@ -135,7 +135,8 @@ def is_owner() -> bool:
assert c.is_owner(call={"from": a1}) is False # no one else is.
# only an owner may set another owner.
- assert_tx_failed(lambda: c.set_owner(1, a1, call={"from": a1}))
+ with tx_failed():
+ c.set_owner(1, a1, call={"from": a1})
c.set_owner(1, a1, transact={})
assert c.is_owner(call={"from": a1}) is True
@@ -145,7 +146,7 @@ def is_owner() -> bool:
assert c.is_owner() is False
-def test_in_fails_when_types_dont_match(get_contract_with_gas_estimation, assert_tx_failed):
+def test_in_fails_when_types_dont_match(get_contract_with_gas_estimation, tx_failed):
code = """
@external
def testin(x: address) -> bool:
@@ -154,4 +155,5 @@ def testin(x: address) -> bool:
return True
return False
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(code), TypeMismatch)
+ with tx_failed(TypeMismatch):
+ get_contract_with_gas_estimation(code)
diff --git a/tests/parser/features/test_address_balance.py b/tests/functional/codegen/features/test_address_balance.py
similarity index 100%
rename from tests/parser/features/test_address_balance.py
rename to tests/functional/codegen/features/test_address_balance.py
diff --git a/tests/parser/features/test_assert.py b/tests/functional/codegen/features/test_assert.py
similarity index 81%
rename from tests/parser/features/test_assert.py
rename to tests/functional/codegen/features/test_assert.py
index 842b32d815..af189e6dca 100644
--- a/tests/parser/features/test_assert.py
+++ b/tests/functional/codegen/features/test_assert.py
@@ -3,12 +3,12 @@
# web3 returns f"execution reverted: {err_str}"
-# TODO move exception string parsing logic into assert_tx_failed
+# TODO move exception string parsing logic into tx_failed
def _fixup_err_str(s):
return s.replace("execution reverted: ", "")
-def test_assert_refund(w3, get_contract_with_gas_estimation, assert_tx_failed):
+def test_assert_refund(w3, get_contract_with_gas_estimation, tx_failed):
code = """
@external
def foo():
@@ -26,7 +26,7 @@ def foo():
assert tx_receipt["gasUsed"] < gas_sent
-def test_assert_reason(w3, get_contract_with_gas_estimation, assert_tx_failed, memory_mocker):
+def test_assert_reason(w3, get_contract_with_gas_estimation, tx_failed, memory_mocker):
code = """
@external
def test(a: int128) -> int128:
@@ -132,7 +132,7 @@ def test_valid_assertions(get_contract, code):
get_contract(code)
-def test_assert_staticcall(get_contract, assert_tx_failed, memory_mocker):
+def test_assert_staticcall(get_contract, tx_failed, memory_mocker):
foreign_code = """
state: uint256
@external
@@ -151,10 +151,11 @@ def test():
c1 = get_contract(foreign_code)
c2 = get_contract(code, *[c1.address])
# static call prohibits state change
- assert_tx_failed(lambda: c2.test())
+ with tx_failed():
+ c2.test()
-def test_assert_in_for_loop(get_contract, assert_tx_failed, memory_mocker):
+def test_assert_in_for_loop(get_contract, tx_failed, memory_mocker):
code = """
@external
def test(x: uint256[3]) -> bool:
@@ -166,12 +167,15 @@ def test(x: uint256[3]) -> bool:
c = get_contract(code)
c.test([1, 2, 3])
- assert_tx_failed(lambda: c.test([5, 1, 3]))
- assert_tx_failed(lambda: c.test([1, 5, 3]))
- assert_tx_failed(lambda: c.test([1, 3, 5]))
+ with tx_failed():
+ c.test([5, 1, 3])
+ with tx_failed():
+ c.test([1, 5, 3])
+ with tx_failed():
+ c.test([1, 3, 5])
-def test_assert_with_reason_in_for_loop(get_contract, assert_tx_failed, memory_mocker):
+def test_assert_with_reason_in_for_loop(get_contract, tx_failed, memory_mocker):
code = """
@external
def test(x: uint256[3]) -> bool:
@@ -183,12 +187,15 @@ def test(x: uint256[3]) -> bool:
c = get_contract(code)
c.test([1, 2, 3])
- assert_tx_failed(lambda: c.test([5, 1, 3]))
- assert_tx_failed(lambda: c.test([1, 5, 3]))
- assert_tx_failed(lambda: c.test([1, 3, 5]))
+ with tx_failed():
+ c.test([5, 1, 3])
+ with tx_failed():
+ c.test([1, 5, 3])
+ with tx_failed():
+ c.test([1, 3, 5])
-def test_assert_reason_revert_length(w3, get_contract, assert_tx_failed, memory_mocker):
+def test_assert_reason_revert_length(w3, get_contract, tx_failed, memory_mocker):
code = """
@external
def test() -> int128:
@@ -196,4 +203,5 @@ def test() -> int128:
return 1
"""
c = get_contract(code)
- assert_tx_failed(lambda: c.test(), exc_text="oops")
+ with tx_failed(exc_text="oops"):
+ c.test()
diff --git a/tests/parser/features/test_assert_unreachable.py b/tests/functional/codegen/features/test_assert_unreachable.py
similarity index 61%
rename from tests/parser/features/test_assert_unreachable.py
rename to tests/functional/codegen/features/test_assert_unreachable.py
index 90ed31a22e..4db00bce7c 100644
--- a/tests/parser/features/test_assert_unreachable.py
+++ b/tests/functional/codegen/features/test_assert_unreachable.py
@@ -15,7 +15,7 @@ def foo():
assert tx_receipt["gasUsed"] == gas_sent # Drains all gains sent
-def test_basic_unreachable(w3, get_contract, assert_tx_failed):
+def test_basic_unreachable(w3, get_contract, tx_failed):
code = """
@external
def foo(val: int128) -> bool:
@@ -28,12 +28,15 @@ def foo(val: int128) -> bool:
assert c.foo(2) is True
- assert_tx_failed(lambda: c.foo(1), exc_text="Invalid opcode 0xfe")
- assert_tx_failed(lambda: c.foo(-1), exc_text="Invalid opcode 0xfe")
- assert_tx_failed(lambda: c.foo(-2), exc_text="Invalid opcode 0xfe")
+ with tx_failed(exc_text="Invalid opcode 0xfe"):
+ c.foo(1)
+ with tx_failed(exc_text="Invalid opcode 0xfe"):
+ c.foo(-1)
+ with tx_failed(exc_text="Invalid opcode 0xfe"):
+ c.foo(-2)
-def test_basic_call_unreachable(w3, get_contract, assert_tx_failed):
+def test_basic_call_unreachable(w3, get_contract, tx_failed):
code = """
@view
@@ -51,11 +54,13 @@ def foo(val: int128) -> int128:
assert c.foo(33) == -123
- assert_tx_failed(lambda: c.foo(1), exc_text="Invalid opcode 0xfe")
- assert_tx_failed(lambda: c.foo(-1), exc_text="Invalid opcode 0xfe")
+ with tx_failed(exc_text="Invalid opcode 0xfe"):
+ c.foo(1)
+ with tx_failed(exc_text="Invalid opcode 0xfe"):
+ c.foo(-1)
-def test_raise_unreachable(w3, get_contract, assert_tx_failed):
+def test_raise_unreachable(w3, get_contract, tx_failed):
code = """
@external
def foo():
@@ -64,4 +69,5 @@ def foo():
c = get_contract(code)
- assert_tx_failed(lambda: c.foo(), exc_text="Invalid opcode 0xfe")
+ with tx_failed(exc_text="Invalid opcode 0xfe"):
+ c.foo()
diff --git a/tests/parser/features/test_assignment.py b/tests/functional/codegen/features/test_assignment.py
similarity index 88%
rename from tests/parser/features/test_assignment.py
rename to tests/functional/codegen/features/test_assignment.py
index e550f60541..9af7058250 100644
--- a/tests/parser/features/test_assignment.py
+++ b/tests/functional/codegen/features/test_assignment.py
@@ -66,7 +66,7 @@ def bar(x: {typ}) -> {typ}:
def test_internal_assign_struct(get_contract_with_gas_estimation):
code = """
-enum Bar:
+flag Bar:
BAD
BAK
BAZ
@@ -92,7 +92,7 @@ def bar(x: Foo) -> Foo:
def test_internal_assign_struct_member(get_contract_with_gas_estimation):
code = """
-enum Bar:
+flag Bar:
BAD
BAK
BAZ
@@ -331,7 +331,7 @@ def foo():
@external
def foo():
y: int128 = 1
- z: bytes32 = EMPTY_BYTES32
+ z: bytes32 = empty(bytes32)
z = y
""",
"""
@@ -344,7 +344,7 @@ def foo():
@external
def foo():
y: uint256 = 1
- z: bytes32 = EMPTY_BYTES32
+ z: bytes32 = empty(bytes32)
z = y
""",
],
@@ -442,3 +442,63 @@ def bug(p: Point) -> Point:
"""
c = get_contract(code)
assert c.bug((1, 2)) == (2, 1)
+
+
+mload_merge_codes = [
+ (
+ """
+@external
+def foo() -> uint256[4]:
+ # copy "backwards"
+ xs: uint256[4] = [1, 2, 3, 4]
+
+# dst < src
+ xs[0] = xs[1]
+ xs[1] = xs[2]
+ xs[2] = xs[3]
+
+ return xs
+ """,
+ [2, 3, 4, 4],
+ ),
+ (
+ """
+@external
+def foo() -> uint256[4]:
+ # copy "forwards"
+ xs: uint256[4] = [1, 2, 3, 4]
+
+# src < dst
+ xs[1] = xs[0]
+ xs[2] = xs[1]
+ xs[3] = xs[2]
+
+ return xs
+ """,
+ [1, 1, 1, 1],
+ ),
+ (
+ """
+@external
+def foo() -> uint256[5]:
+ # partial "forward" copy
+ xs: uint256[5] = [1, 2, 3, 4, 5]
+
+# src < dst
+ xs[2] = xs[0]
+ xs[3] = xs[1]
+ xs[4] = xs[2]
+
+ return xs
+ """,
+ [1, 2, 1, 2, 1],
+ ),
+]
+
+
+# functional test that mload merging does not occur when source and dest
+# buffers overlap. (note: mload merging only applies after cancun)
+@pytest.mark.parametrize("code,expected_result", mload_merge_codes)
+def test_mcopy_overlap(get_contract, code, expected_result):
+ c = get_contract(code)
+ assert c.foo() == expected_result
diff --git a/tests/parser/features/test_bytes_map_keys.py b/tests/functional/codegen/features/test_bytes_map_keys.py
similarity index 100%
rename from tests/parser/features/test_bytes_map_keys.py
rename to tests/functional/codegen/features/test_bytes_map_keys.py
diff --git a/tests/parser/features/test_clampers.py b/tests/functional/codegen/features/test_clampers.py
similarity index 83%
rename from tests/parser/features/test_clampers.py
rename to tests/functional/codegen/features/test_clampers.py
index ad7ea32b1e..6db8570fc7 100644
--- a/tests/parser/features/test_clampers.py
+++ b/tests/functional/codegen/features/test_clampers.py
@@ -33,7 +33,7 @@ def _make_invalid_dynarray_tx(w3, address, signature, data):
w3.eth.send_transaction({"to": address, "data": f"0x{sig}{data}"})
-def test_bytes_clamper(assert_tx_failed, get_contract_with_gas_estimation):
+def test_bytes_clamper(tx_failed, get_contract_with_gas_estimation):
clamper_test_code = """
@external
def foo(s: Bytes[3]) -> Bytes[3]:
@@ -43,10 +43,11 @@ def foo(s: Bytes[3]) -> Bytes[3]:
c = get_contract_with_gas_estimation(clamper_test_code)
assert c.foo(b"ca") == b"ca"
assert c.foo(b"cat") == b"cat"
- assert_tx_failed(lambda: c.foo(b"cate"))
+ with tx_failed():
+ c.foo(b"cate")
-def test_bytes_clamper_multiple_slots(assert_tx_failed, get_contract_with_gas_estimation):
+def test_bytes_clamper_multiple_slots(tx_failed, get_contract_with_gas_estimation):
clamper_test_code = """
@external
def foo(s: Bytes[40]) -> Bytes[40]:
@@ -58,10 +59,11 @@ def foo(s: Bytes[40]) -> Bytes[40]:
assert c.foo(data[:30]) == data[:30]
assert c.foo(data) == data
- assert_tx_failed(lambda: c.foo(data + b"!"))
+ with tx_failed():
+ c.foo(data + b"!")
-def test_bytes_clamper_on_init(assert_tx_failed, get_contract_with_gas_estimation):
+def test_bytes_clamper_on_init(tx_failed, get_contract_with_gas_estimation):
clamper_test_code = """
foo: Bytes[3]
@@ -77,7 +79,8 @@ def get_foo() -> Bytes[3]:
c = get_contract_with_gas_estimation(clamper_test_code, *[b"cat"])
assert c.get_foo() == b"cat"
- assert_tx_failed(lambda: get_contract_with_gas_estimation(clamper_test_code, *[b"cats"]))
+ with tx_failed():
+ get_contract_with_gas_estimation(clamper_test_code, *[b"cats"])
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@@ -99,7 +102,7 @@ def foo(s: bytes{n}) -> bytes{n}:
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@pytest.mark.parametrize("n", list(range(1, 32))) # bytes32 always passes
-def test_bytes_m_clamper_failing(w3, get_contract, assert_tx_failed, n, evm_version):
+def test_bytes_m_clamper_failing(w3, get_contract, tx_failed, n, evm_version):
values = []
values.append(b"\x00" * n + b"\x80") # just one bit set
values.append(b"\xff" * n + b"\x80") # n*8 + 1 bits set
@@ -118,8 +121,9 @@ def foo(s: bytes{n}) -> bytes{n}:
c = get_contract(code, evm_version=evm_version)
for v in values:
# munge for `_make_tx`
- v = int.from_bytes(v, byteorder="big")
- assert_tx_failed(lambda: _make_tx(w3, c.address, f"foo(bytes{n})", [v]))
+ with tx_failed():
+ int_value = int.from_bytes(v, byteorder="big")
+ _make_tx(w3, c.address, f"foo(bytes{n})", [int_value])
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@@ -141,7 +145,7 @@ def foo(s: int{bits}) -> int{bits}:
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@pytest.mark.parametrize("n", list(range(31))) # int256 does not clamp
-def test_sint_clamper_failing(w3, assert_tx_failed, get_contract, n, evm_version):
+def test_sint_clamper_failing(w3, tx_failed, get_contract, n, evm_version):
bits = 8 * (n + 1)
lo, hi = int_bounds(True, bits)
values = [-(2**255), 2**255 - 1, lo - 1, hi + 1]
@@ -153,7 +157,8 @@ def foo(s: int{bits}) -> int{bits}:
c = get_contract(code, evm_version=evm_version)
for v in values:
- assert_tx_failed(lambda: _make_tx(w3, c.address, f"foo(int{bits})", [v]))
+ with tx_failed():
+ _make_tx(w3, c.address, f"foo(int{bits})", [v])
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@@ -171,7 +176,7 @@ def foo(s: bool) -> bool:
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@pytest.mark.parametrize("value", [2, 3, 4, 8, 16, 2**256 - 1])
-def test_bool_clamper_failing(w3, assert_tx_failed, get_contract, value, evm_version):
+def test_bool_clamper_failing(w3, tx_failed, get_contract, value, evm_version):
code = """
@external
def foo(s: bool) -> bool:
@@ -179,14 +184,15 @@ def foo(s: bool) -> bool:
"""
c = get_contract(code, evm_version=evm_version)
- assert_tx_failed(lambda: _make_tx(w3, c.address, "foo(bool)", [value]))
+ with tx_failed():
+ _make_tx(w3, c.address, "foo(bool)", [value])
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@pytest.mark.parametrize("value", [0] + [2**i for i in range(5)])
-def test_enum_clamper_passing(w3, get_contract, value, evm_version):
+def test_flag_clamper_passing(w3, get_contract, value, evm_version):
code = """
-enum Roles:
+flag Roles:
USER
STAFF
ADMIN
@@ -204,9 +210,9 @@ def foo(s: Roles) -> Roles:
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@pytest.mark.parametrize("value", [2**i for i in range(5, 256)])
-def test_enum_clamper_failing(w3, assert_tx_failed, get_contract, value, evm_version):
+def test_flag_clamper_failing(w3, tx_failed, get_contract, value, evm_version):
code = """
-enum Roles:
+flag Roles:
USER
STAFF
ADMIN
@@ -219,7 +225,8 @@ def foo(s: Roles) -> Roles:
"""
c = get_contract(code, evm_version=evm_version)
- assert_tx_failed(lambda: _make_tx(w3, c.address, "foo(uint256)", [value]))
+ with tx_failed():
+ _make_tx(w3, c.address, "foo(uint256)", [value])
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@@ -240,7 +247,7 @@ def foo(s: uint{bits}) -> uint{bits}:
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@pytest.mark.parametrize("n", list(range(31))) # uint256 has no failing cases
-def test_uint_clamper_failing(w3, assert_tx_failed, get_contract, evm_version, n):
+def test_uint_clamper_failing(w3, tx_failed, get_contract, evm_version, n):
bits = 8 * (n + 1)
values = [-1, -(2**255), 2**bits]
code = f"""
@@ -250,7 +257,8 @@ def foo(s: uint{bits}) -> uint{bits}:
"""
c = get_contract(code, evm_version=evm_version)
for v in values:
- assert_tx_failed(lambda: _make_tx(w3, c.address, f"foo(uint{bits})", [v]))
+ with tx_failed():
+ _make_tx(w3, c.address, f"foo(uint{bits})", [v])
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@@ -281,7 +289,7 @@ def foo(s: address) -> address:
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@pytest.mark.parametrize("value", [2**160, 2**256 - 1])
-def test_address_clamper_failing(w3, assert_tx_failed, get_contract, value, evm_version):
+def test_address_clamper_failing(w3, tx_failed, get_contract, value, evm_version):
code = """
@external
def foo(s: address) -> address:
@@ -289,7 +297,8 @@ def foo(s: address) -> address:
"""
c = get_contract(code, evm_version=evm_version)
- assert_tx_failed(lambda: _make_tx(w3, c.address, "foo(address)", [value]))
+ with tx_failed():
+ _make_tx(w3, c.address, "foo(address)", [value])
@pytest.mark.parametrize("evm_version", list(EVM_VERSIONS))
@@ -334,7 +343,7 @@ def foo(s: decimal) -> decimal:
-187072209578355573530071658587684226515959365500929, # - (2 ** 127 - 1e-10)
],
)
-def test_decimal_clamper_failing(w3, assert_tx_failed, get_contract, value, evm_version):
+def test_decimal_clamper_failing(w3, tx_failed, get_contract, value, evm_version):
code = """
@external
def foo(s: decimal) -> decimal:
@@ -343,7 +352,8 @@ def foo(s: decimal) -> decimal:
c = get_contract(code, evm_version=evm_version)
- assert_tx_failed(lambda: _make_tx(w3, c.address, "foo(fixed168x10)", [value]))
+ with tx_failed():
+ _make_tx(w3, c.address, "foo(fixed168x10)", [value])
@pytest.mark.parametrize("value", [0, 1, -1, 2**127 - 1, -(2**127)])
@@ -363,7 +373,7 @@ def foo(a: uint256, b: int128[5], c: uint256) -> int128[5]:
@pytest.mark.parametrize("bad_value", [2**127, -(2**127) - 1, 2**255 - 1, -(2**255)])
@pytest.mark.parametrize("idx", range(5))
-def test_int128_array_clamper_failing(w3, assert_tx_failed, get_contract, bad_value, idx):
+def test_int128_array_clamper_failing(w3, tx_failed, get_contract, bad_value, idx):
# ensure the invalid value is detected at all locations in the array
code = """
@external
@@ -375,7 +385,8 @@ def foo(b: int128[5]) -> int128[5]:
values[idx] = bad_value
c = get_contract(code)
- assert_tx_failed(lambda: _make_tx(w3, c.address, "foo(int128[5])", values))
+ with tx_failed():
+ _make_tx(w3, c.address, "foo(int128[5])", values)
@pytest.mark.parametrize("value", [0, 1, -1, 2**127 - 1, -(2**127)])
@@ -394,7 +405,7 @@ def foo(a: uint256, b: int128[10], c: uint256) -> int128[10]:
@pytest.mark.parametrize("bad_value", [2**127, -(2**127) - 1, 2**255 - 1, -(2**255)])
@pytest.mark.parametrize("idx", range(10))
-def test_int128_array_looped_clamper_failing(w3, assert_tx_failed, get_contract, bad_value, idx):
+def test_int128_array_looped_clamper_failing(w3, tx_failed, get_contract, bad_value, idx):
code = """
@external
def foo(b: int128[10]) -> int128[10]:
@@ -405,7 +416,8 @@ def foo(b: int128[10]) -> int128[10]:
values[idx] = bad_value
c = get_contract(code)
- assert_tx_failed(lambda: _make_tx(w3, c.address, "foo(int128[10])", values))
+ with tx_failed():
+ _make_tx(w3, c.address, "foo(int128[10])", values)
@pytest.mark.parametrize("value", [0, 1, -1, 2**127 - 1, -(2**127)])
@@ -424,7 +436,7 @@ def foo(a: uint256, b: int128[6][3][1][8], c: uint256) -> int128[6][3][1][8]:
@pytest.mark.parametrize("bad_value", [2**127, -(2**127) - 1, 2**255 - 1, -(2**255)])
@pytest.mark.parametrize("idx", range(12))
-def test_multidimension_array_clamper_failing(w3, assert_tx_failed, get_contract, bad_value, idx):
+def test_multidimension_array_clamper_failing(w3, tx_failed, get_contract, bad_value, idx):
code = """
@external
def foo(b: int128[6][1][2]) -> int128[6][1][2]:
@@ -435,7 +447,8 @@ def foo(b: int128[6][1][2]) -> int128[6][1][2]:
values[idx] = bad_value
c = get_contract(code)
- assert_tx_failed(lambda: _make_tx(w3, c.address, "foo(int128[6][1][2]])", values))
+ with tx_failed():
+ _make_tx(w3, c.address, "foo(int128[6][1][2]])", values)
@pytest.mark.parametrize("value", [0, 1, -1, 2**127 - 1, -(2**127)])
@@ -455,7 +468,7 @@ def foo(a: uint256, b: DynArray[int128, 5], c: uint256) -> DynArray[int128, 5]:
@pytest.mark.parametrize("bad_value", [2**127, -(2**127) - 1, 2**255 - 1, -(2**255)])
@pytest.mark.parametrize("idx", range(5))
-def test_int128_dynarray_clamper_failing(w3, assert_tx_failed, get_contract, bad_value, idx):
+def test_int128_dynarray_clamper_failing(w3, tx_failed, get_contract, bad_value, idx):
# ensure the invalid value is detected at all locations in the array
code = """
@external
@@ -470,7 +483,8 @@ def foo(b: int128[5]) -> int128[5]:
c = get_contract(code)
data = _make_dynarray_data(32, 5, values)
- assert_tx_failed(lambda: _make_invalid_dynarray_tx(w3, c.address, signature, data))
+ with tx_failed():
+ _make_invalid_dynarray_tx(w3, c.address, signature, data)
@pytest.mark.parametrize("value", [0, 1, -1, 2**127 - 1, -(2**127)])
@@ -488,7 +502,7 @@ def foo(a: uint256, b: DynArray[int128, 10], c: uint256) -> DynArray[int128, 10]
@pytest.mark.parametrize("bad_value", [2**127, -(2**127) - 1, 2**255 - 1, -(2**255)])
@pytest.mark.parametrize("idx", range(10))
-def test_int128_dynarray_looped_clamper_failing(w3, assert_tx_failed, get_contract, bad_value, idx):
+def test_int128_dynarray_looped_clamper_failing(w3, tx_failed, get_contract, bad_value, idx):
code = """
@external
def foo(b: DynArray[int128, 10]) -> DynArray[int128, 10]:
@@ -502,7 +516,8 @@ def foo(b: DynArray[int128, 10]) -> DynArray[int128, 10]:
data = _make_dynarray_data(32, 10, values)
signature = "foo(int128[])"
- assert_tx_failed(lambda: _make_invalid_dynarray_tx(w3, c.address, signature, data))
+ with tx_failed():
+ _make_invalid_dynarray_tx(w3, c.address, signature, data)
@pytest.mark.parametrize("value", [0, 1, -1, 2**127 - 1, -(2**127)])
@@ -524,9 +539,7 @@ def foo(
@pytest.mark.parametrize("bad_value", [2**127, -(2**127) - 1, 2**255 - 1, -(2**255)])
@pytest.mark.parametrize("idx", range(4))
-def test_multidimension_dynarray_clamper_failing(
- w3, assert_tx_failed, get_contract, bad_value, idx
-):
+def test_multidimension_dynarray_clamper_failing(w3, tx_failed, get_contract, bad_value, idx):
code = """
@external
def foo(b: DynArray[DynArray[int128, 2], 2]) -> DynArray[DynArray[int128, 2], 2]:
@@ -546,7 +559,8 @@ def foo(b: DynArray[DynArray[int128, 2], 2]) -> DynArray[DynArray[int128, 2], 2]
signature = "foo(int128[][])"
c = get_contract(code)
- assert_tx_failed(lambda: _make_invalid_dynarray_tx(w3, c.address, signature, data))
+ with tx_failed():
+ _make_invalid_dynarray_tx(w3, c.address, signature, data)
@pytest.mark.parametrize("value", [0, 1, -1, 2**127 - 1, -(2**127)])
@@ -567,7 +581,7 @@ def foo(
@pytest.mark.parametrize("bad_value", [2**127, -(2**127) - 1, 2**255 - 1, -(2**255)])
@pytest.mark.parametrize("idx", range(10))
-def test_dynarray_list_clamper_failing(w3, assert_tx_failed, get_contract, bad_value, idx):
+def test_dynarray_list_clamper_failing(w3, tx_failed, get_contract, bad_value, idx):
# ensure the invalid value is detected at all locations in the array
code = """
@external
@@ -585,4 +599,5 @@ def foo(b: DynArray[int128[5], 2]) -> DynArray[int128[5], 2]:
c = get_contract(code)
signature = "foo(int128[5][])"
- assert_tx_failed(lambda: _make_invalid_dynarray_tx(w3, c.address, signature, data))
+ with tx_failed():
+ _make_invalid_dynarray_tx(w3, c.address, signature, data)
diff --git a/tests/parser/features/test_comments.py b/tests/functional/codegen/features/test_comments.py
similarity index 100%
rename from tests/parser/features/test_comments.py
rename to tests/functional/codegen/features/test_comments.py
diff --git a/tests/parser/features/test_comparison.py b/tests/functional/codegen/features/test_comparison.py
similarity index 100%
rename from tests/parser/features/test_comparison.py
rename to tests/functional/codegen/features/test_comparison.py
diff --git a/tests/parser/features/test_conditionals.py b/tests/functional/codegen/features/test_conditionals.py
similarity index 100%
rename from tests/parser/features/test_conditionals.py
rename to tests/functional/codegen/features/test_conditionals.py
diff --git a/tests/parser/features/test_constructor.py b/tests/functional/codegen/features/test_constructor.py
similarity index 100%
rename from tests/parser/features/test_constructor.py
rename to tests/functional/codegen/features/test_constructor.py
diff --git a/tests/parser/features/test_gas.py b/tests/functional/codegen/features/test_gas.py
similarity index 100%
rename from tests/parser/features/test_gas.py
rename to tests/functional/codegen/features/test_gas.py
diff --git a/tests/parser/features/test_immutable.py b/tests/functional/codegen/features/test_immutable.py
similarity index 100%
rename from tests/parser/features/test_immutable.py
rename to tests/functional/codegen/features/test_immutable.py
diff --git a/tests/parser/features/test_init.py b/tests/functional/codegen/features/test_init.py
similarity index 92%
rename from tests/parser/features/test_init.py
rename to tests/functional/codegen/features/test_init.py
index 83bcbc95ea..fc765f8ab3 100644
--- a/tests/parser/features/test_init.py
+++ b/tests/functional/codegen/features/test_init.py
@@ -15,7 +15,7 @@ def __init__(a: uint256):
assert c.val() == 123
# Make sure the init code does not access calldata
- assembly = vyper.compile_code(code, ["asm"])["asm"].split(" ")
+ assembly = vyper.compile_code(code, output_formats=["asm"])["asm"].split(" ")
ir_return_idx_start = assembly.index("{")
ir_return_idx_end = assembly.index("}")
@@ -24,7 +24,7 @@ def __init__(a: uint256):
assert "CALLDATALOAD" not in assembly[:ir_return_idx_start] + assembly[ir_return_idx_end:]
-def test_init_calls_internal(get_contract, assert_compile_failed, assert_tx_failed):
+def test_init_calls_internal(get_contract, assert_compile_failed, tx_failed):
code = """
foo: public(uint8)
@internal
@@ -46,7 +46,8 @@ def baz() -> uint8:
n = 6
c = get_contract(code, n)
assert c.foo() == n * 7
- assert_tx_failed(lambda: c.baz())
+ with tx_failed():
+ c.baz()
n = 255
assert_compile_failed(lambda: get_contract(code, n))
diff --git a/tests/parser/features/test_internal_call.py b/tests/functional/codegen/features/test_internal_call.py
similarity index 99%
rename from tests/parser/features/test_internal_call.py
rename to tests/functional/codegen/features/test_internal_call.py
index d7a41acbc0..f10d22ec99 100644
--- a/tests/parser/features/test_internal_call.py
+++ b/tests/functional/codegen/features/test_internal_call.py
@@ -669,7 +669,7 @@ def test_internal_call_kwargs(get_contract, typ1, strategy1, typ2, strategy2):
# GHSA-ph9x-4vc9-m39g
@given(kwarg1=strategy1, default1=strategy1, kwarg2=strategy2, default2=strategy2)
- @settings(deadline=None, max_examples=5) # len(cases) * len(cases) * 5 * 5
+ @settings(max_examples=5) # len(cases) * len(cases) * 5 * 5
def fuzz(kwarg1, kwarg2, default1, default2):
code = f"""
@internal
diff --git a/tests/parser/features/test_logging.py b/tests/functional/codegen/features/test_logging.py
similarity index 92%
rename from tests/parser/features/test_logging.py
rename to tests/functional/codegen/features/test_logging.py
index 84311c41f5..ba09be1991 100644
--- a/tests/parser/features/test_logging.py
+++ b/tests/functional/codegen/features/test_logging.py
@@ -3,6 +3,7 @@
import pytest
from eth.codecs import abi
+from vyper import compile_code
from vyper.exceptions import (
ArgumentException,
EventDeclarationException,
@@ -193,7 +194,7 @@ def bar():
def test_event_logging_cannot_have_more_than_three_topics(
- assert_tx_failed, get_contract_with_gas_estimation
+ tx_failed, get_contract_with_gas_estimation
):
loggy_code = """
event MyLog:
@@ -203,9 +204,8 @@ def test_event_logging_cannot_have_more_than_three_topics(
arg4: indexed(int128)
"""
- assert_tx_failed(
- lambda: get_contract_with_gas_estimation(loggy_code), EventDeclarationException
- )
+ with pytest.raises(EventDeclarationException):
+ compile_code(loggy_code)
def test_event_logging_with_data(w3, tester, keccak, get_logs, get_contract_with_gas_estimation):
@@ -555,7 +555,7 @@ def foo():
assert args.arg2 == {"x": 1, "y": b"abc", "z": {"t": "house", "w": Decimal("13.5")}}
-def test_fails_when_input_is_the_wrong_type(assert_tx_failed, get_contract_with_gas_estimation):
+def test_fails_when_input_is_the_wrong_type(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog:
arg1: indexed(int128)
@@ -565,10 +565,11 @@ def foo_():
log MyLog(b'yo')
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), InvalidType)
+ with tx_failed(InvalidType):
+ get_contract_with_gas_estimation(loggy_code)
-def test_fails_when_topic_is_the_wrong_size(assert_tx_failed, get_contract_with_gas_estimation):
+def test_fails_when_topic_is_the_wrong_size(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog:
arg1: indexed(Bytes[3])
@@ -579,12 +580,11 @@ def foo():
log MyLog(b'bars')
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), InvalidType)
+ with tx_failed(InvalidType):
+ get_contract_with_gas_estimation(loggy_code)
-def test_fails_when_input_topic_is_the_wrong_size(
- assert_tx_failed, get_contract_with_gas_estimation
-):
+def test_fails_when_input_topic_is_the_wrong_size(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog:
arg1: indexed(Bytes[3])
@@ -594,10 +594,11 @@ def foo(arg1: Bytes[4]):
log MyLog(arg1)
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), TypeMismatch)
+ with tx_failed(TypeMismatch):
+ get_contract_with_gas_estimation(loggy_code)
-def test_fails_when_data_is_the_wrong_size(assert_tx_failed, get_contract_with_gas_estimation):
+def test_fails_when_data_is_the_wrong_size(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog:
arg1: Bytes[3]
@@ -607,12 +608,11 @@ def foo():
log MyLog(b'bars')
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), InvalidType)
+ with tx_failed(InvalidType):
+ get_contract_with_gas_estimation(loggy_code)
-def test_fails_when_input_data_is_the_wrong_size(
- assert_tx_failed, get_contract_with_gas_estimation
-):
+def test_fails_when_input_data_is_the_wrong_size(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog:
arg1: Bytes[3]
@@ -622,7 +622,8 @@ def foo(arg1: Bytes[4]):
log MyLog(arg1)
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), TypeMismatch)
+ with tx_failed(TypeMismatch):
+ get_contract_with_gas_estimation(loggy_code)
def test_topic_over_32_bytes(get_contract_with_gas_estimation):
@@ -637,7 +638,7 @@ def foo():
get_contract_with_gas_estimation(loggy_code)
-def test_logging_fails_with_over_three_topics(assert_tx_failed, get_contract_with_gas_estimation):
+def test_logging_fails_with_over_three_topics(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog:
arg1: indexed(int128)
@@ -650,12 +651,11 @@ def __init__():
log MyLog(1, 2, 3, 4)
"""
- assert_tx_failed(
- lambda: get_contract_with_gas_estimation(loggy_code), EventDeclarationException
- )
+ with tx_failed(EventDeclarationException):
+ get_contract_with_gas_estimation(loggy_code)
-def test_logging_fails_with_duplicate_log_names(assert_tx_failed, get_contract_with_gas_estimation):
+def test_logging_fails_with_duplicate_log_names(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog: pass
event MyLog: pass
@@ -665,12 +665,11 @@ def foo():
log MyLog()
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), NamespaceCollision)
+ with tx_failed(NamespaceCollision):
+ get_contract_with_gas_estimation(loggy_code)
-def test_logging_fails_with_when_log_is_undeclared(
- assert_tx_failed, get_contract_with_gas_estimation
-):
+def test_logging_fails_with_when_log_is_undeclared(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
@external
@@ -678,10 +677,11 @@ def foo():
log MyLog()
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), UndeclaredDefinition)
+ with tx_failed(UndeclaredDefinition):
+ get_contract_with_gas_estimation(loggy_code)
-def test_logging_fails_with_topic_type_mismatch(assert_tx_failed, get_contract_with_gas_estimation):
+def test_logging_fails_with_topic_type_mismatch(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog:
arg1: indexed(int128)
@@ -691,10 +691,11 @@ def foo():
log MyLog(self)
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), TypeMismatch)
+ with tx_failed(TypeMismatch):
+ get_contract_with_gas_estimation(loggy_code)
-def test_logging_fails_with_data_type_mismatch(assert_tx_failed, get_contract_with_gas_estimation):
+def test_logging_fails_with_data_type_mismatch(tx_failed, get_contract_with_gas_estimation):
loggy_code = """
event MyLog:
arg1: Bytes[3]
@@ -704,11 +705,12 @@ def foo():
log MyLog(self)
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), TypeMismatch)
+ with tx_failed(TypeMismatch):
+ get_contract_with_gas_estimation(loggy_code)
def test_logging_fails_when_number_of_arguments_is_greater_than_declaration(
- assert_tx_failed, get_contract_with_gas_estimation
+ tx_failed, get_contract_with_gas_estimation
):
loggy_code = """
event MyLog:
@@ -718,11 +720,12 @@ def test_logging_fails_when_number_of_arguments_is_greater_than_declaration(
def foo():
log MyLog(1, 2)
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), ArgumentException)
+ with tx_failed(ArgumentException):
+ get_contract_with_gas_estimation(loggy_code)
def test_logging_fails_when_number_of_arguments_is_less_than_declaration(
- assert_tx_failed, get_contract_with_gas_estimation
+ tx_failed, get_contract_with_gas_estimation
):
loggy_code = """
event MyLog:
@@ -733,7 +736,8 @@ def test_logging_fails_when_number_of_arguments_is_less_than_declaration(
def foo():
log MyLog(1)
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(loggy_code), ArgumentException)
+ with tx_failed(ArgumentException):
+ get_contract_with_gas_estimation(loggy_code)
def test_loggy_code(w3, tester, get_contract_with_gas_estimation):
@@ -962,7 +966,7 @@ def set_list():
]
-def test_logging_fails_when_input_is_too_big(assert_tx_failed, get_contract_with_gas_estimation):
+def test_logging_fails_when_input_is_too_big(tx_failed, get_contract_with_gas_estimation):
code = """
event Bar:
_value: indexed(Bytes[32])
@@ -971,7 +975,8 @@ def test_logging_fails_when_input_is_too_big(assert_tx_failed, get_contract_with
def foo(inp: Bytes[33]):
log Bar(inp)
"""
- assert_tx_failed(lambda: get_contract_with_gas_estimation(code), TypeMismatch)
+ with tx_failed(TypeMismatch):
+ get_contract_with_gas_estimation(code)
def test_2nd_var_list_packing(get_logs, get_contract_with_gas_estimation):
diff --git a/tests/parser/features/test_logging_bytes_extended.py b/tests/functional/codegen/features/test_logging_bytes_extended.py
similarity index 100%
rename from tests/parser/features/test_logging_bytes_extended.py
rename to tests/functional/codegen/features/test_logging_bytes_extended.py
diff --git a/tests/parser/features/test_logging_from_call.py b/tests/functional/codegen/features/test_logging_from_call.py
similarity index 100%
rename from tests/parser/features/test_logging_from_call.py
rename to tests/functional/codegen/features/test_logging_from_call.py
diff --git a/tests/functional/codegen/features/test_memory_alloc.py b/tests/functional/codegen/features/test_memory_alloc.py
new file mode 100644
index 0000000000..ee6d15c67c
--- /dev/null
+++ b/tests/functional/codegen/features/test_memory_alloc.py
@@ -0,0 +1,16 @@
+import pytest
+
+from vyper.compiler import compile_code
+from vyper.exceptions import MemoryAllocationException
+
+
+def test_memory_overflow():
+ code = """
+@external
+def zzz(x: DynArray[uint256, 2**59]): # 2**64 / 32 bytes per word == 2**59
+ y: uint256[7] = [0,0,0,0,0,0,0]
+
+ y[6] = y[5]
+ """
+ with pytest.raises(MemoryAllocationException):
+ compile_code(code)
diff --git a/tests/parser/features/test_memory_dealloc.py b/tests/functional/codegen/features/test_memory_dealloc.py
similarity index 93%
rename from tests/parser/features/test_memory_dealloc.py
rename to tests/functional/codegen/features/test_memory_dealloc.py
index de82f03296..814bf0d3bb 100644
--- a/tests/parser/features/test_memory_dealloc.py
+++ b/tests/functional/codegen/features/test_memory_dealloc.py
@@ -9,7 +9,7 @@ def sendit(): nonpayable
@external
def foo(target: address) -> uint256[2]:
- log Shimmy(ZERO_ADDRESS, 3)
+ log Shimmy(empty(address), 3)
amount: uint256 = 1
flargen: uint256 = 42
Other(target).sendit()
diff --git a/tests/parser/features/test_packing.py b/tests/functional/codegen/features/test_packing.py
similarity index 100%
rename from tests/parser/features/test_packing.py
rename to tests/functional/codegen/features/test_packing.py
diff --git a/tests/functional/codegen/features/test_reverting.py b/tests/functional/codegen/features/test_reverting.py
new file mode 100644
index 0000000000..f24886ce96
--- /dev/null
+++ b/tests/functional/codegen/features/test_reverting.py
@@ -0,0 +1,50 @@
+import pytest
+from eth.codecs import abi
+from eth_tester.exceptions import TransactionFailed
+
+from vyper.utils import method_id
+
+pytestmark = pytest.mark.usefixtures("memory_mocker")
+
+
+def test_revert_reason(w3, tx_failed, get_contract_with_gas_estimation):
+ reverty_code = """
+@external
+def foo():
+ data: Bytes[4] = method_id("NoFives()")
+ raw_revert(data)
+ """
+
+ revert_bytes = method_id("NoFives()")
+
+ with tx_failed(TransactionFailed, exc_text=f"execution reverted: {revert_bytes}"):
+ get_contract_with_gas_estimation(reverty_code).foo(transact={})
+
+
+def test_revert_reason_typed(w3, tx_failed, get_contract_with_gas_estimation):
+ reverty_code = """
+@external
+def foo():
+ val: uint256 = 5
+ data: Bytes[100] = _abi_encode(val, method_id=method_id("NoFives(uint256)"))
+ raw_revert(data)
+ """
+
+ revert_bytes = method_id("NoFives(uint256)") + abi.encode("(uint256)", (5,))
+
+ with tx_failed(TransactionFailed, exc_text=f"execution reverted: {revert_bytes}"):
+ get_contract_with_gas_estimation(reverty_code).foo(transact={})
+
+
+def test_revert_reason_typed_no_variable(w3, tx_failed, get_contract_with_gas_estimation):
+ reverty_code = """
+@external
+def foo():
+ val: uint256 = 5
+ raw_revert(_abi_encode(val, method_id=method_id("NoFives(uint256)")))
+ """
+
+ revert_bytes = method_id("NoFives(uint256)") + abi.encode("(uint256)", (5,))
+
+ with tx_failed(TransactionFailed, exc_text=f"execution reverted: {revert_bytes}"):
+ get_contract_with_gas_estimation(reverty_code).foo(transact={})
diff --git a/tests/parser/features/test_short_circuiting.py b/tests/functional/codegen/features/test_short_circuiting.py
similarity index 100%
rename from tests/parser/features/test_short_circuiting.py
rename to tests/functional/codegen/features/test_short_circuiting.py
diff --git a/tests/parser/features/test_string_map_keys.py b/tests/functional/codegen/features/test_string_map_keys.py
similarity index 100%
rename from tests/parser/features/test_string_map_keys.py
rename to tests/functional/codegen/features/test_string_map_keys.py
diff --git a/tests/parser/features/test_ternary.py b/tests/functional/codegen/features/test_ternary.py
similarity index 100%
rename from tests/parser/features/test_ternary.py
rename to tests/functional/codegen/features/test_ternary.py
diff --git a/tests/parser/features/test_transient.py b/tests/functional/codegen/features/test_transient.py
similarity index 100%
rename from tests/parser/features/test_transient.py
rename to tests/functional/codegen/features/test_transient.py
diff --git a/tests/parser/integration/test_basics.py b/tests/functional/codegen/integration/test_basics.py
similarity index 100%
rename from tests/parser/integration/test_basics.py
rename to tests/functional/codegen/integration/test_basics.py
diff --git a/tests/parser/integration/test_crowdfund.py b/tests/functional/codegen/integration/test_crowdfund.py
similarity index 94%
rename from tests/parser/integration/test_crowdfund.py
rename to tests/functional/codegen/integration/test_crowdfund.py
index c45a60d9c7..671d424d60 100644
--- a/tests/parser/integration/test_crowdfund.py
+++ b/tests/functional/codegen/integration/test_crowdfund.py
@@ -1,3 +1,4 @@
+# TODO: check, this is probably redundant with examples/test_crowdfund.py
def test_crowdfund(w3, tester, get_contract_with_gas_estimation_for_constants):
crowdfund = """
@@ -51,7 +52,7 @@ def finalize():
@external
def refund():
ind: int128 = self.refundIndex
- for i in range(ind, ind + 30):
+ for i in range(ind, ind + 30, bound=30):
if i >= self.nextFunderIndex:
self.refundIndex = self.nextFunderIndex
return
@@ -62,10 +63,13 @@ def refund():
"""
a0, a1, a2, a3, a4, a5, a6 = w3.eth.accounts[:7]
+
c = get_contract_with_gas_estimation_for_constants(crowdfund, *[a1, 50, 60])
+ start_timestamp = w3.eth.get_block(w3.eth.block_number).timestamp
+
c.participate(transact={"value": 5})
assert c.timelimit() == 60
- assert c.deadline() - c.block_timestamp() == 59
+ assert c.deadline() - start_timestamp == 60
assert not c.expired()
assert not c.reached()
c.participate(transact={"value": 49})
@@ -143,7 +147,7 @@ def finalize():
@external
def refund():
ind: int128 = self.refundIndex
- for i in range(ind, ind + 30):
+ for i in range(ind, ind + 30, bound=30):
if i >= self.nextFunderIndex:
self.refundIndex = self.nextFunderIndex
return
diff --git a/tests/parser/integration/test_escrow.py b/tests/functional/codegen/integration/test_escrow.py
similarity index 83%
rename from tests/parser/integration/test_escrow.py
rename to tests/functional/codegen/integration/test_escrow.py
index 2982ff9eae..70e7cb4594 100644
--- a/tests/parser/integration/test_escrow.py
+++ b/tests/functional/codegen/integration/test_escrow.py
@@ -1,7 +1,7 @@
# from ethereum.tools import tester
-def test_arbitration_code(w3, get_contract_with_gas_estimation, assert_tx_failed):
+def test_arbitration_code(w3, get_contract_with_gas_estimation, tx_failed):
arbitration_code = """
buyer: address
seller: address
@@ -9,7 +9,7 @@ def test_arbitration_code(w3, get_contract_with_gas_estimation, assert_tx_failed
@external
def setup(_seller: address, _arbitrator: address):
- if self.buyer == ZERO_ADDRESS:
+ if self.buyer == empty(address):
self.buyer = msg.sender
self.seller = _seller
self.arbitrator = _arbitrator
@@ -28,13 +28,14 @@ def refund():
a0, a1, a2 = w3.eth.accounts[:3]
c = get_contract_with_gas_estimation(arbitration_code, value=1)
c.setup(a1, a2, transact={})
- assert_tx_failed(lambda: c.finalize(transact={"from": a1}))
+ with tx_failed():
+ c.finalize(transact={"from": a1})
c.finalize(transact={})
print("Passed escrow test")
-def test_arbitration_code_with_init(w3, assert_tx_failed, get_contract_with_gas_estimation):
+def test_arbitration_code_with_init(w3, tx_failed, get_contract_with_gas_estimation):
arbitration_code_with_init = """
buyer: address
seller: address
@@ -43,7 +44,7 @@ def test_arbitration_code_with_init(w3, assert_tx_failed, get_contract_with_gas_
@external
@payable
def __init__(_seller: address, _arbitrator: address):
- if self.buyer == ZERO_ADDRESS:
+ if self.buyer == empty(address):
self.buyer = msg.sender
self.seller = _seller
self.arbitrator = _arbitrator
@@ -60,7 +61,8 @@ def refund():
"""
a0, a1, a2 = w3.eth.accounts[:3]
c = get_contract_with_gas_estimation(arbitration_code_with_init, *[a1, a2], value=1)
- assert_tx_failed(lambda: c.finalize(transact={"from": a1}))
+ with tx_failed():
+ c.finalize(transact={"from": a1})
c.finalize(transact={"from": a0})
print("Passed escrow test with initializer")
diff --git a/tests/parser/globals/test_getters.py b/tests/functional/codegen/storage_variables/test_getters.py
similarity index 100%
rename from tests/parser/globals/test_getters.py
rename to tests/functional/codegen/storage_variables/test_getters.py
diff --git a/tests/parser/globals/test_setters.py b/tests/functional/codegen/storage_variables/test_setters.py
similarity index 100%
rename from tests/parser/globals/test_setters.py
rename to tests/functional/codegen/storage_variables/test_setters.py
diff --git a/tests/parser/globals/test_globals.py b/tests/functional/codegen/storage_variables/test_storage_variable.py
similarity index 100%
rename from tests/parser/globals/test_globals.py
rename to tests/functional/codegen/storage_variables/test_storage_variable.py
diff --git a/tests/parser/test_call_graph_stability.py b/tests/functional/codegen/test_call_graph_stability.py
similarity index 89%
rename from tests/parser/test_call_graph_stability.py
rename to tests/functional/codegen/test_call_graph_stability.py
index b651092d16..ca0e6c8c9e 100644
--- a/tests/parser/test_call_graph_stability.py
+++ b/tests/functional/codegen/test_call_graph_stability.py
@@ -6,8 +6,8 @@
from hypothesis import given, settings
import vyper.ast as vy_ast
+from vyper.ast.identifiers import RESERVED_KEYWORDS
from vyper.compiler.phases import CompilerData
-from vyper.semantics.namespace import RESERVED_KEYWORDS
def _valid_identifier(attr):
@@ -15,7 +15,7 @@ def _valid_identifier(attr):
# random names for functions
-@settings(max_examples=20, deadline=None)
+@settings(max_examples=20)
@given(
st.lists(
st.tuples(
@@ -54,8 +54,8 @@ def foo():
t = CompilerData(code)
# check the .called_functions data structure on foo() directly
- foo = t.vyper_module_folded.get_children(vy_ast.FunctionDef, filters={"name": "foo"})[0]
- foo_t = foo._metadata["type"]
+ foo = t.annotated_vyper_module.get_children(vy_ast.FunctionDef, filters={"name": "foo"})[0]
+ foo_t = foo._metadata["func_type"]
assert [f.name for f in foo_t.called_functions] == func_names
# now for sanity, ensure the order that the function definitions appear
diff --git a/tests/parser/functions/test_interfaces.py b/tests/functional/codegen/test_interfaces.py
similarity index 62%
rename from tests/parser/functions/test_interfaces.py
rename to tests/functional/codegen/test_interfaces.py
index c16e188cfd..7d363fadc0 100644
--- a/tests/parser/functions/test_interfaces.py
+++ b/tests/functional/codegen/test_interfaces.py
@@ -1,10 +1,15 @@
+import json
from decimal import Decimal
import pytest
-from vyper.cli.utils import extract_file_interface_imports
-from vyper.compiler import compile_code, compile_codes
-from vyper.exceptions import ArgumentException, InterfaceViolation, StructureException
+from vyper.compiler import compile_code
+from vyper.exceptions import (
+ ArgumentException,
+ DuplicateImport,
+ InterfaceViolation,
+ NamespaceCollision,
+)
def test_basic_extract_interface():
@@ -24,9 +29,9 @@ def allowance(_owner: address, _spender: address) -> (uint256, uint256):
return 1, 2
"""
- out = compile_code(code, ["interface"])
+ out = compile_code(code, output_formats=["interface"])
out = out["interface"]
- code_pass = "\n".join(code.split("\n")[:-2] + [" pass"]) # replace with a pass statement.
+ code_pass = "\n".join(code.split("\n")[:-2] + [" ..."]) # replace with a pass statement.
assert code_pass.strip() == out.strip()
@@ -55,8 +60,9 @@ def allowance(_owner: address, _spender: address) -> (uint256, uint256): view
def test(_owner: address): nonpayable
"""
- out = compile_codes({"one.vy": code}, ["external_interface"])["one.vy"]
- out = out["external_interface"]
+ out = compile_code(code, contract_path="One.vy", output_formats=["external_interface"])[
+ "external_interface"
+ ]
assert interface.strip() == out.strip()
@@ -75,18 +81,18 @@ def test() -> bool:
assert_compile_failed(lambda: compile_code(code), InterfaceViolation)
-def test_external_interface_parsing(assert_compile_failed):
+def test_external_interface_parsing(make_input_bundle, assert_compile_failed):
interface_code = """
@external
def foo() -> uint256:
- pass
+ ...
@external
def bar() -> uint256:
- pass
+ ...
"""
- interface_codes = {"FooBarInterface": {"type": "vyper", "code": interface_code}}
+ input_bundle = make_input_bundle({"a.vyi": interface_code})
code = """
import a as FooBarInterface
@@ -102,7 +108,7 @@ def bar() -> uint256:
return 2
"""
- assert compile_code(code, interface_codes=interface_codes)
+ assert compile_code(code, input_bundle=input_bundle)
not_implemented_code = """
import a as FooBarInterface
@@ -115,19 +121,17 @@ def foo() -> uint256:
"""
- assert_compile_failed(
- lambda: compile_code(not_implemented_code, interface_codes=interface_codes),
- InterfaceViolation,
- )
+ with pytest.raises(InterfaceViolation):
+ compile_code(not_implemented_code, input_bundle=input_bundle)
-def test_missing_event(assert_compile_failed):
+def test_missing_event(make_input_bundle, assert_compile_failed):
interface_code = """
event Foo:
a: uint256
"""
- interface_codes = {"FooBarInterface": {"type": "vyper", "code": interface_code}}
+ input_bundle = make_input_bundle({"a.vyi": interface_code})
not_implemented_code = """
import a as FooBarInterface
@@ -140,19 +144,18 @@ def bar() -> uint256:
"""
assert_compile_failed(
- lambda: compile_code(not_implemented_code, interface_codes=interface_codes),
- InterfaceViolation,
+ lambda: compile_code(not_implemented_code, input_bundle=input_bundle), InterfaceViolation
)
# check that event types match
-def test_malformed_event(assert_compile_failed):
+def test_malformed_event(make_input_bundle, assert_compile_failed):
interface_code = """
event Foo:
a: uint256
"""
- interface_codes = {"FooBarInterface": {"type": "vyper", "code": interface_code}}
+ input_bundle = make_input_bundle({"a.vyi": interface_code})
not_implemented_code = """
import a as FooBarInterface
@@ -168,19 +171,18 @@ def bar() -> uint256:
"""
assert_compile_failed(
- lambda: compile_code(not_implemented_code, interface_codes=interface_codes),
- InterfaceViolation,
+ lambda: compile_code(not_implemented_code, input_bundle=input_bundle), InterfaceViolation
)
# check that event non-indexed arg needs to match interface
-def test_malformed_events_indexed(assert_compile_failed):
+def test_malformed_events_indexed(make_input_bundle, assert_compile_failed):
interface_code = """
event Foo:
a: uint256
"""
- interface_codes = {"FooBarInterface": {"type": "vyper", "code": interface_code}}
+ input_bundle = make_input_bundle({"a.vyi": interface_code})
not_implemented_code = """
import a as FooBarInterface
@@ -197,19 +199,18 @@ def bar() -> uint256:
"""
assert_compile_failed(
- lambda: compile_code(not_implemented_code, interface_codes=interface_codes),
- InterfaceViolation,
+ lambda: compile_code(not_implemented_code, input_bundle=input_bundle), InterfaceViolation
)
# check that event indexed arg needs to match interface
-def test_malformed_events_indexed2(assert_compile_failed):
+def test_malformed_events_indexed2(make_input_bundle, assert_compile_failed):
interface_code = """
event Foo:
a: indexed(uint256)
"""
- interface_codes = {"FooBarInterface": {"type": "vyper", "code": interface_code}}
+ input_bundle = make_input_bundle({"a.vyi": interface_code})
not_implemented_code = """
import a as FooBarInterface
@@ -226,64 +227,87 @@ def bar() -> uint256:
"""
assert_compile_failed(
- lambda: compile_code(not_implemented_code, interface_codes=interface_codes),
- InterfaceViolation,
+ lambda: compile_code(not_implemented_code, input_bundle=input_bundle), InterfaceViolation
)
VALID_IMPORT_CODE = [
# import statement, import path without suffix
- ("import a as Foo", "a"),
- ("import b.a as Foo", "b/a"),
- ("import Foo as Foo", "Foo"),
- ("from a import Foo", "a/Foo"),
- ("from b.a import Foo", "b/a/Foo"),
- ("from .a import Foo", "./a/Foo"),
- ("from ..a import Foo", "../a/Foo"),
+ ("import a as Foo", "a.vyi"),
+ ("import b.a as Foo", "b/a.vyi"),
+ ("import Foo as Foo", "Foo.vyi"),
+ ("from a import Foo", "a/Foo.vyi"),
+ ("from b.a import Foo", "b/a/Foo.vyi"),
+ ("from .a import Foo", "./a/Foo.vyi"),
+ ("from ..a import Foo", "../a/Foo.vyi"),
]
-@pytest.mark.parametrize("code", VALID_IMPORT_CODE)
-def test_extract_file_interface_imports(code):
- assert extract_file_interface_imports(code[0]) == {"Foo": code[1]}
+@pytest.mark.parametrize("code,filename", VALID_IMPORT_CODE)
+def test_extract_file_interface_imports(code, filename, make_input_bundle):
+ input_bundle = make_input_bundle({filename: ""})
+
+ assert compile_code(code, input_bundle=input_bundle) is not None
BAD_IMPORT_CODE = [
- "import a", # must alias absolute imports
- "import a as A\nimport a as A", # namespace collisions
- "from b import a\nfrom a import a",
- "from . import a\nimport a as a",
- "import a as a\nfrom . import a",
+ ("import a as A\nimport a as A", DuplicateImport),
+ ("import a as A\nimport a as a", DuplicateImport),
+ ("from . import a\nimport a as a", DuplicateImport),
+ ("import a as a\nfrom . import a", DuplicateImport),
+ ("from b import a\nfrom . import a", NamespaceCollision),
+ ("import a\nimport c as a", NamespaceCollision),
]
-@pytest.mark.parametrize("code", BAD_IMPORT_CODE)
-def test_extract_file_interface_imports_raises(code, assert_compile_failed):
- assert_compile_failed(lambda: extract_file_interface_imports(code), StructureException)
+@pytest.mark.parametrize("code,exception_type", BAD_IMPORT_CODE)
+def test_extract_file_interface_imports_raises(
+ code, exception_type, assert_compile_failed, make_input_bundle
+):
+ input_bundle = make_input_bundle({"a.vyi": "", "b/a.vyi": "", "c.vyi": ""})
+ with pytest.raises(exception_type):
+ compile_code(code, input_bundle=input_bundle)
-def test_external_call_to_interface(w3, get_contract):
+def test_external_call_to_interface(w3, get_contract, make_input_bundle):
+ token_interface = """
+@view
+@external
+def balanceOf(addr: address) -> uint256:
+ ...
+
+@external
+def transfer(to: address, amount: uint256):
+ ...
+ """
+
token_code = """
+import itoken as IToken
+
+implements: IToken
+
balanceOf: public(HashMap[address, uint256])
@external
-def transfer(to: address, _value: uint256):
- self.balanceOf[to] += _value
+def transfer(to: address, amount: uint256):
+ self.balanceOf[to] += amount
"""
+ input_bundle = make_input_bundle({"token.vy": token_code, "itoken.vyi": token_interface})
+
code = """
-import one as TokenCode
+import itoken as IToken
interface EPI:
def test() -> uint256: view
-token_address: TokenCode
+token_address: IToken
@external
def __init__(_token_address: address):
- self.token_address = TokenCode(_token_address)
+ self.token_address = IToken(_token_address)
@external
@@ -291,16 +315,15 @@ def test():
self.token_address.transfer(msg.sender, 1000)
"""
- erc20 = get_contract(token_code)
- test_c = get_contract(
- code, *[erc20.address], interface_codes={"TokenCode": {"type": "vyper", "code": token_code}}
- )
+ token = get_contract(token_code, input_bundle=input_bundle)
+
+ test_c = get_contract(code, *[token.address], input_bundle=input_bundle)
sender = w3.eth.accounts[0]
- assert erc20.balanceOf(sender) == 0
+ assert token.balanceOf(sender) == 0
test_c.test(transact={})
- assert erc20.balanceOf(sender) == 1000
+ assert token.balanceOf(sender) == 1000
@pytest.mark.parametrize(
@@ -313,29 +336,37 @@ def test():
("epsilon(decimal)", "decimal", Decimal("1E-10")),
],
)
-def test_external_call_to_interface_kwarg(get_contract, kwarg, typ, expected):
- code_a = f"""
+def test_external_call_to_interface_kwarg(get_contract, kwarg, typ, expected, make_input_bundle):
+ interface_code = f"""
+@external
+@view
+def foo(_max: {typ} = {kwarg}) -> {typ}:
+ ...
+ """
+ code1 = f"""
+import one as IContract
+
+implements: IContract
+
@external
@view
def foo(_max: {typ} = {kwarg}) -> {typ}:
return _max
"""
- code_b = f"""
-import one as ContractA
+ input_bundle = make_input_bundle({"one.vyi": interface_code})
+
+ code2 = f"""
+import one as IContract
@external
@view
def bar(a_address: address) -> {typ}:
- return ContractA(a_address).foo()
+ return IContract(a_address).foo()
"""
- contract_a = get_contract(code_a)
- contract_b = get_contract(
- code_b,
- *[contract_a.address],
- interface_codes={"ContractA": {"type": "vyper", "code": code_a}},
- )
+ contract_a = get_contract(code1, input_bundle=input_bundle)
+ contract_b = get_contract(code2, *[contract_a.address], input_bundle=input_bundle)
assert contract_b.bar(contract_a.address) == expected
@@ -345,8 +376,8 @@ def test_external_call_to_builtin_interface(w3, get_contract):
balanceOf: public(HashMap[address, uint256])
@external
-def transfer(to: address, _value: uint256) -> bool:
- self.balanceOf[to] += _value
+def transfer(to: address, amount: uint256) -> bool:
+ self.balanceOf[to] += amount
return True
"""
@@ -368,9 +399,7 @@ def test():
"""
erc20 = get_contract(token_code)
- test_c = get_contract(
- code, *[erc20.address], interface_codes={"TokenCode": {"type": "vyper", "code": token_code}}
- )
+ test_c = get_contract(code, *[erc20.address])
sender = w3.eth.accounts[0]
assert erc20.balanceOf(sender) == 0
@@ -398,7 +427,7 @@ def test(addr: address):
# test data returned from external interface gets clamped
@pytest.mark.parametrize("typ", ("int128", "uint8"))
-def test_external_interface_int_clampers(get_contract, assert_tx_failed, typ):
+def test_external_interface_int_clampers(get_contract, tx_failed, typ):
external_contract = f"""
@external
def ok() -> {typ}:
@@ -406,7 +435,7 @@ def ok() -> {typ}:
@external
def should_fail() -> int256:
- return -2**255 # OOB for all int/uint types with less than 256 bits
+ return min_value(int256)
"""
code = f"""
@@ -440,22 +469,21 @@ def test_fail3() -> int256:
"""
bad_c = get_contract(external_contract)
- c = get_contract(
- code,
- bad_c.address,
- interface_codes={"BadCode": {"type": "vyper", "code": external_contract}},
- )
+ c = get_contract(code, bad_c.address)
assert bad_c.ok() == 1
assert bad_c.should_fail() == -(2**255)
assert c.test_ok() == 1
- assert_tx_failed(lambda: c.test_fail())
- assert_tx_failed(lambda: c.test_fail2())
- assert_tx_failed(lambda: c.test_fail3())
+ with tx_failed():
+ c.test_fail()
+ with tx_failed():
+ c.test_fail2()
+ with tx_failed():
+ c.test_fail3()
# test data returned from external interface gets clamped
-def test_external_interface_bytes_clampers(get_contract, assert_tx_failed):
+def test_external_interface_bytes_clampers(get_contract, tx_failed):
external_contract = """
@external
def ok() -> Bytes[2]:
@@ -497,12 +525,14 @@ def test_fail2() -> Bytes[3]:
assert bad_c.should_fail() == b"123"
assert c.test_ok() == b"12"
- assert_tx_failed(lambda: c.test_fail1())
- assert_tx_failed(lambda: c.test_fail2())
+ with tx_failed():
+ c.test_fail1()
+ with tx_failed():
+ c.test_fail2()
# test data returned from external interface gets clamped
-def test_json_abi_bytes_clampers(get_contract, assert_tx_failed, assert_compile_failed):
+def test_json_abi_bytes_clampers(get_contract, tx_failed, assert_compile_failed, make_input_bundle):
external_contract = """
@external
def returns_Bytes3() -> Bytes[3]:
@@ -510,14 +540,14 @@ def returns_Bytes3() -> Bytes[3]:
"""
should_not_compile = """
-import BadJSONInterface as BadJSONInterface
+import BadJSONInterface
@external
def foo(x: BadJSONInterface) -> Bytes[2]:
return slice(x.returns_Bytes3(), 0, 2)
"""
code = """
-import BadJSONInterface as BadJSONInterface
+import BadJSONInterface
foo: BadJSONInterface
@@ -546,26 +576,26 @@ def test_fail3() -> Bytes[3]:
"""
bad_c = get_contract(external_contract)
- bad_c_interface = {
- "BadJSONInterface": {
- "type": "json",
- "code": compile_code(external_contract, ["abi"])["abi"],
- }
- }
+
+ bad_json_interface = json.dumps(compile_code(external_contract, output_formats=["abi"])["abi"])
+ input_bundle = make_input_bundle({"BadJSONInterface.json": bad_json_interface})
assert_compile_failed(
- lambda: get_contract(should_not_compile, interface_codes=bad_c_interface), ArgumentException
+ lambda: get_contract(should_not_compile, input_bundle=input_bundle), ArgumentException
)
- c = get_contract(code, bad_c.address, interface_codes=bad_c_interface)
+ c = get_contract(code, bad_c.address, input_bundle=input_bundle)
assert bad_c.returns_Bytes3() == b"123"
- assert_tx_failed(lambda: c.test_fail1())
- assert_tx_failed(lambda: c.test_fail2())
- assert_tx_failed(lambda: c.test_fail3())
+ with tx_failed():
+ c.test_fail1()
+ with tx_failed():
+ c.test_fail2()
+ with tx_failed():
+ c.test_fail3()
-def test_units_interface(w3, get_contract):
+def test_units_interface(w3, get_contract, make_input_bundle):
code = """
import balanceof as BalanceOf
@@ -576,49 +606,41 @@ def test_units_interface(w3, get_contract):
def balanceOf(owner: address) -> uint256:
return as_wei_value(1, "ether")
"""
+
interface_code = """
@external
@view
def balanceOf(owner: address) -> uint256:
- pass
+ ...
"""
- interface_codes = {"BalanceOf": {"type": "vyper", "code": interface_code}}
- c = get_contract(code, interface_codes=interface_codes)
+
+ input_bundle = make_input_bundle({"balanceof.vyi": interface_code})
+
+ c = get_contract(code, input_bundle=input_bundle)
assert c.balanceOf(w3.eth.accounts[0]) == w3.to_wei(1, "ether")
-def test_local_and_global_interface_namespaces():
+def test_simple_implements(make_input_bundle):
interface_code = """
@external
def foo() -> uint256:
- pass
+ ...
"""
- global_interface_codes = {
- "FooInterface": {"type": "vyper", "code": interface_code},
- "BarInterface": {"type": "vyper", "code": interface_code},
- }
- local_interface_codes = {
- "FooContract": {"FooInterface": {"type": "vyper", "code": interface_code}},
- "BarContract": {"BarInterface": {"type": "vyper", "code": interface_code}},
- }
-
code = """
-import a as {0}
+import a as FooInterface
-implements: {0}
+implements: FooInterface
@external
def foo() -> uint256:
return 1
"""
- codes = {"FooContract": code.format("FooInterface"), "BarContract": code.format("BarInterface")}
+ input_bundle = make_input_bundle({"a.vyi": interface_code})
- global_compiled = compile_codes(codes, interface_codes=global_interface_codes)
- local_compiled = compile_codes(codes, interface_codes=local_interface_codes)
- assert global_compiled == local_compiled
+ assert compile_code(code, input_bundle=input_bundle) is not None
def test_self_interface_is_allowed(get_contract):
@@ -724,20 +746,28 @@ def convert_v1_abi(abi):
@pytest.mark.parametrize("type_str", [i[0] for i in type_str_params])
-def test_json_interface_implements(type_str):
+def test_json_interface_implements(type_str, make_input_bundle, make_file):
code = interface_test_code.format(type_str)
- abi = compile_code(code, ["abi"])["abi"]
+ abi = compile_code(code, output_formats=["abi"])["abi"]
+
code = f"import jsonabi as jsonabi\nimplements: jsonabi\n{code}"
- compile_code(code, interface_codes={"jsonabi": {"type": "json", "code": abi}})
- compile_code(code, interface_codes={"jsonabi": {"type": "json", "code": convert_v1_abi(abi)}})
+
+ input_bundle = make_input_bundle({"jsonabi.json": json.dumps(abi)})
+
+ compile_code(code, input_bundle=input_bundle)
+
+ # !!! overwrite the file
+ make_file("jsonabi.json", json.dumps(convert_v1_abi(abi)))
+
+ compile_code(code, input_bundle=input_bundle)
@pytest.mark.parametrize("type_str,value", type_str_params)
-def test_json_interface_calls(get_contract, type_str, value):
+def test_json_interface_calls(get_contract, type_str, value, make_input_bundle, make_file):
code = interface_test_code.format(type_str)
- abi = compile_code(code, ["abi"])["abi"]
+ abi = compile_code(code, output_formats=["abi"])["abi"]
c1 = get_contract(code)
code = f"""
@@ -748,9 +778,11 @@ def test_json_interface_calls(get_contract, type_str, value):
def test_call(a: address, b: {type_str}) -> {type_str}:
return jsonabi(a).test_json(b)
"""
- c2 = get_contract(code, interface_codes={"jsonabi": {"type": "json", "code": abi}})
+ input_bundle = make_input_bundle({"jsonabi.json": json.dumps(abi)})
+
+ c2 = get_contract(code, input_bundle=input_bundle)
assert c2.test_call(c1.address, value) == value
- c3 = get_contract(
- code, interface_codes={"jsonabi": {"type": "json", "code": convert_v1_abi(abi)}}
- )
+
+ make_file("jsonabi.json", json.dumps(convert_v1_abi(abi)))
+ c3 = get_contract(code, input_bundle=input_bundle)
assert c3.test_call(c1.address, value) == value
diff --git a/tests/functional/codegen/test_selector_table.py b/tests/functional/codegen/test_selector_table.py
new file mode 100644
index 0000000000..94233977c9
--- /dev/null
+++ b/tests/functional/codegen/test_selector_table.py
@@ -0,0 +1,637 @@
+import hypothesis.strategies as st
+import pytest
+from hypothesis import given, settings
+
+import vyper.utils as utils
+from vyper.codegen.jumptable_utils import (
+ generate_dense_jumptable_info,
+ generate_sparse_jumptable_buckets,
+)
+from vyper.compiler.settings import OptimizationLevel
+
+
+def test_dense_selector_table_empty_buckets(get_contract):
+ # some special combination of selectors which can result in
+ # some empty bucket being returned from _mk_buckets (that is,
+ # len(_mk_buckets(..., n_buckets)) != n_buckets
+ code = """
+@external
+def aX61QLPWF()->uint256:
+ return 1
+@external
+def aQHG0P2L1()->uint256:
+ return 2
+@external
+def a2G8ME94W()->uint256:
+ return 3
+@external
+def a0GNA21AY()->uint256:
+ return 4
+@external
+def a4U1XA4T5()->uint256:
+ return 5
+@external
+def aAYLMGOBZ()->uint256:
+ return 6
+@external
+def a0KXRLHKE()->uint256:
+ return 7
+@external
+def aDQS32HTR()->uint256:
+ return 8
+@external
+def aP4K6SA3S()->uint256:
+ return 9
+@external
+def aEB94ZP5S()->uint256:
+ return 10
+@external
+def aTOIMN0IM()->uint256:
+ return 11
+@external
+def aXV2N81OW()->uint256:
+ return 12
+@external
+def a66PP6Y5X()->uint256:
+ return 13
+@external
+def a5MWMTEWN()->uint256:
+ return 14
+@external
+def a5ZFST4Z8()->uint256:
+ return 15
+@external
+def aR13VXULX()->uint256:
+ return 16
+@external
+def aWITH917Y()->uint256:
+ return 17
+@external
+def a59NP6C5O()->uint256:
+ return 18
+@external
+def aJ02590EX()->uint256:
+ return 19
+@external
+def aUAXAAUQ8()->uint256:
+ return 20
+@external
+def aWR1XNC6J()->uint256:
+ return 21
+@external
+def aJABKZOKH()->uint256:
+ return 22
+@external
+def aO1TT0RJT()->uint256:
+ return 23
+@external
+def a41442IOK()->uint256:
+ return 24
+@external
+def aMVXV9FHQ()->uint256:
+ return 25
+@external
+def aNN0KJDZM()->uint256:
+ return 26
+@external
+def aOX965047()->uint256:
+ return 27
+@external
+def a575NX2J3()->uint256:
+ return 28
+@external
+def a16EN8O7W()->uint256:
+ return 29
+@external
+def aSZXLFF7O()->uint256:
+ return 30
+@external
+def aQKQCIPH9()->uint256:
+ return 31
+@external
+def aIP8021DL()->uint256:
+ return 32
+@external
+def aQAV0HSHX()->uint256:
+ return 33
+@external
+def aZVPAD745()->uint256:
+ return 34
+@external
+def aJYBSNST4()->uint256:
+ return 35
+@external
+def aQGWC4NYQ()->uint256:
+ return 36
+@external
+def aFMBB9CXJ()->uint256:
+ return 37
+@external
+def aYWM7ZUH1()->uint256:
+ return 38
+@external
+def aJAZONIX1()->uint256:
+ return 39
+@external
+def aQZ1HJK0H()->uint256:
+ return 40
+@external
+def aKIH9LOUB()->uint256:
+ return 41
+@external
+def aF4ZT80XL()->uint256:
+ return 42
+@external
+def aYQD8UKR5()->uint256:
+ return 43
+@external
+def aP6NCCAI4()->uint256:
+ return 44
+@external
+def aY92U2EAZ()->uint256:
+ return 45
+@external
+def aHMQ49D7P()->uint256:
+ return 46
+@external
+def aMC6YX8VF()->uint256:
+ return 47
+@external
+def a734X6YSI()->uint256:
+ return 48
+@external
+def aRXXPNSMU()->uint256:
+ return 49
+@external
+def aL5XKDTGT()->uint256:
+ return 50
+@external
+def a86V1Y18A()->uint256:
+ return 51
+@external
+def aAUM8PL5J()->uint256:
+ return 52
+@external
+def aBAEC1ERZ()->uint256:
+ return 53
+@external
+def a1U1VA3UE()->uint256:
+ return 54
+@external
+def aC9FGVAHC()->uint256:
+ return 55
+@external
+def aWN81WYJ3()->uint256:
+ return 56
+@external
+def a3KK1Y07J()->uint256:
+ return 57
+@external
+def aAZ6P6OSG()->uint256:
+ return 58
+@external
+def aWP5HCIB3()->uint256:
+ return 59
+@external
+def aVEK161C5()->uint256:
+ return 60
+@external
+def aY0Q3O519()->uint256:
+ return 61
+@external
+def aDHHHFIAE()->uint256:
+ return 62
+@external
+def aGSJBCZKQ()->uint256:
+ return 63
+@external
+def aZQQIUDHY()->uint256:
+ return 64
+@external
+def a12O9QDH5()->uint256:
+ return 65
+@external
+def aRQ1178XR()->uint256:
+ return 66
+@external
+def aDT25C832()->uint256:
+ return 67
+@external
+def aCSB01C4E()->uint256:
+ return 68
+@external
+def aYGBPKZSD()->uint256:
+ return 69
+@external
+def aP24N3EJ8()->uint256:
+ return 70
+@external
+def a531Y9X3C()->uint256:
+ return 71
+@external
+def a4727IKVS()->uint256:
+ return 72
+@external
+def a2EX1L2BS()->uint256:
+ return 73
+@external
+def a6145RN68()->uint256:
+ return 74
+@external
+def aDO1ZNX97()->uint256:
+ return 75
+@external
+def a3R28EU6M()->uint256:
+ return 76
+@external
+def a9BFC867L()->uint256:
+ return 77
+@external
+def aPL1MBGYC()->uint256:
+ return 78
+@external
+def aI6H11O48()->uint256:
+ return 79
+@external
+def aX0248DZY()->uint256:
+ return 80
+@external
+def aE4JBUJN4()->uint256:
+ return 81
+@external
+def aXBDB2ZBO()->uint256:
+ return 82
+@external
+def a7O7MYYHL()->uint256:
+ return 83
+@external
+def aERFF4PB6()->uint256:
+ return 84
+@external
+def aJCUBG6TJ()->uint256:
+ return 85
+@external
+def aQ5ELXM0F()->uint256:
+ return 86
+@external
+def aWDT9UQVV()->uint256:
+ return 87
+@external
+def a7UU40DJK()->uint256:
+ return 88
+@external
+def aH01IT5VS()->uint256:
+ return 89
+@external
+def aSKYTZ0FC()->uint256:
+ return 90
+@external
+def aNX5LYRAW()->uint256:
+ return 91
+@external
+def aUDKAOSGG()->uint256:
+ return 92
+@external
+def aZ86YGAAO()->uint256:
+ return 93
+@external
+def aIHWQGKLO()->uint256:
+ return 94
+@external
+def aKIKFLAR9()->uint256:
+ return 95
+@external
+def aCTPE0KRS()->uint256:
+ return 96
+@external
+def aAD75X00P()->uint256:
+ return 97
+@external
+def aDROUEF2F()->uint256:
+ return 98
+@external
+def a8CDIF6YN()->uint256:
+ return 99
+@external
+def aD2X7TM83()->uint256:
+ return 100
+@external
+def a3W5UUB4L()->uint256:
+ return 101
+@external
+def aG4MOBN4B()->uint256:
+ return 102
+@external
+def aPRS0MSG7()->uint256:
+ return 103
+@external
+def aKN3GHBUR()->uint256:
+ return 104
+@external
+def aGE435RHQ()->uint256:
+ return 105
+@external
+def a4E86BNFE()->uint256:
+ return 106
+@external
+def aYDG928YW()->uint256:
+ return 107
+@external
+def a2HFP5GQE()->uint256:
+ return 108
+@external
+def a5DPMVXKA()->uint256:
+ return 109
+@external
+def a3OFVC3DR()->uint256:
+ return 110
+@external
+def aK8F62DAN()->uint256:
+ return 111
+@external
+def aJS9EY3U6()->uint256:
+ return 112
+@external
+def aWW789JQH()->uint256:
+ return 113
+@external
+def a8AJJN3YR()->uint256:
+ return 114
+@external
+def a4D0MUIDU()->uint256:
+ return 115
+@external
+def a35W41JQR()->uint256:
+ return 116
+@external
+def a07DQOI1E()->uint256:
+ return 117
+@external
+def aFT43YNCT()->uint256:
+ return 118
+@external
+def a0E75I8X3()->uint256:
+ return 119
+@external
+def aT6NXIRO4()->uint256:
+ return 120
+@external
+def aXB2UBAKQ()->uint256:
+ return 121
+@external
+def aHWH55NW6()->uint256:
+ return 122
+@external
+def a7TCFE6C2()->uint256:
+ return 123
+@external
+def a8XYAM81I()->uint256:
+ return 124
+@external
+def aHQTQ4YBY()->uint256:
+ return 125
+@external
+def aGCZEHG6Y()->uint256:
+ return 126
+@external
+def a6LJTKIW0()->uint256:
+ return 127
+@external
+def aBDIXTD9S()->uint256:
+ return 128
+@external
+def aCB83G21P()->uint256:
+ return 129
+@external
+def aZC525N4K()->uint256:
+ return 130
+@external
+def a40LC94U6()->uint256:
+ return 131
+@external
+def a8X9TI93D()->uint256:
+ return 132
+@external
+def aGUG9CD8Y()->uint256:
+ return 133
+@external
+def a0LAERVAY()->uint256:
+ return 134
+@external
+def aXQ0UEX19()->uint256:
+ return 135
+@external
+def aKK9C7NE7()->uint256:
+ return 136
+@external
+def aS2APW8UE()->uint256:
+ return 137
+@external
+def a65NT07MM()->uint256:
+ return 138
+@external
+def aGRMT6ZW5()->uint256:
+ return 139
+@external
+def aILR4U1Z()->uint256:
+ return 140
+ """
+ c = get_contract(code)
+
+ assert c.aX61QLPWF() == 1 # will revert if the header section is misaligned
+
+
+@given(
+ n_methods=st.integers(min_value=1, max_value=100),
+ seed=st.integers(min_value=0, max_value=2**64 - 1),
+)
+@pytest.mark.fuzzing
+@settings(max_examples=10)
+def test_sparse_jumptable_probe_depth(n_methods, seed):
+ sigs = [f"foo{i + seed}()" for i in range(n_methods)]
+ _, buckets = generate_sparse_jumptable_buckets(sigs)
+ bucket_sizes = [len(bucket) for bucket in buckets.values()]
+
+ # generally bucket sizes should be bounded at around 4, but
+ # just test that they don't get really out of hand
+ assert max(bucket_sizes) <= 8
+
+ # generally mean bucket size should be around 1.6, here just
+ # test they don't get really out of hand
+ assert sum(bucket_sizes) / len(bucket_sizes) <= 4
+
+
+@given(
+ n_methods=st.integers(min_value=4, max_value=100),
+ seed=st.integers(min_value=0, max_value=2**64 - 1),
+)
+@pytest.mark.fuzzing
+@settings(max_examples=10)
+def test_dense_jumptable_bucket_size(n_methods, seed):
+ sigs = [f"foo{i + seed}()" for i in range(n_methods)]
+ n = len(sigs)
+ buckets = generate_dense_jumptable_info(sigs)
+ n_buckets = len(buckets)
+
+ # generally should be around 14 buckets per 100 methods, here
+ # we test they don't get really out of hand
+ assert n_buckets / n < 0.4 or n < 10
+
+
+@st.composite
+def generate_methods(draw, max_calldata_bytes):
+ max_default_args = draw(st.integers(min_value=0, max_value=4))
+ default_fn_mutability = draw(st.sampled_from(["", "@pure", "@view", "@nonpayable", "@payable"]))
+
+ return (
+ max_default_args,
+ default_fn_mutability,
+ draw(
+ st.lists(
+ st.tuples(
+ # function id:
+ st.integers(min_value=0),
+ # mutability:
+ st.sampled_from(["@pure", "@view", "@nonpayable", "@payable"]),
+ # n calldata words:
+ st.integers(min_value=0, max_value=max_calldata_bytes // 32),
+ # n bytes to strip from calldata
+ st.integers(min_value=1, max_value=4),
+ # n default args
+ st.integers(min_value=0, max_value=max_default_args),
+ ),
+ unique_by=lambda x: x[0],
+ min_size=1,
+ max_size=100,
+ )
+ ),
+ )
+
+
+@pytest.mark.parametrize("opt_level", list(OptimizationLevel))
+# dense selector table packing boundaries at 256 and 65336
+@pytest.mark.parametrize("max_calldata_bytes", [255, 256, 65336])
+@pytest.mark.fuzzing
+def test_selector_table_fuzz(max_calldata_bytes, opt_level, w3, get_contract, tx_failed, get_logs):
+ def abi_sig(func_id, calldata_words, n_default_args):
+ params = [] if not calldata_words else [f"uint256[{calldata_words}]"]
+ params.extend(["uint256"] * n_default_args)
+ paramstr = ",".join(params)
+ return f"foo{func_id}({paramstr})"
+
+ def generate_func_def(func_id, mutability, calldata_words, n_default_args):
+ arglist = [] if not calldata_words else [f"x: uint256[{calldata_words}]"]
+ for j in range(n_default_args):
+ arglist.append(f"x{j}: uint256 = 0")
+ args = ", ".join(arglist)
+ _log_return = f"log _Return({func_id})" if mutability == "@payable" else ""
+
+ return f"""
+@external
+{mutability}
+def foo{func_id}({args}) -> uint256:
+ {_log_return}
+ return {func_id}
+ """
+
+ @given(_input=generate_methods(max_calldata_bytes))
+ @settings(max_examples=125)
+ def _test(_input):
+ max_default_args, default_fn_mutability, methods = _input
+
+ func_defs = "\n".join(
+ generate_func_def(func_id, mutability, calldata_words, n_default_args)
+ for (func_id, mutability, calldata_words, _, n_default_args) in (methods)
+ )
+
+ if default_fn_mutability == "":
+ default_fn_code = ""
+ elif default_fn_mutability in ("@nonpayable", "@payable"):
+ default_fn_code = f"""
+@external
+{default_fn_mutability}
+def __default__():
+ log CalledDefault()
+ """
+ else:
+ # can't log from pure/view functions, just test that it returns
+ default_fn_code = """
+@external
+def __default__():
+ pass
+ """
+
+ code = f"""
+event CalledDefault:
+ pass
+
+event _Return:
+ val: uint256
+
+{func_defs}
+
+{default_fn_code}
+ """
+
+ c = get_contract(code, override_opt_level=opt_level)
+
+ for func_id, mutability, n_calldata_words, n_strip_bytes, n_default_args in methods:
+ funcname = f"foo{func_id}"
+ func = getattr(c, funcname)
+
+ for j in range(n_default_args + 1):
+ args = [[1] * n_calldata_words] if n_calldata_words else []
+ args.extend([1] * j)
+
+ # check the function returns as expected
+ assert func(*args) == func_id
+
+ method_id = utils.method_id(abi_sig(func_id, n_calldata_words, j))
+
+ argsdata = b"\x00" * (n_calldata_words * 32 + j * 32)
+
+ # do payable check
+ if mutability == "@payable":
+ tx = func(*args, transact={"value": 1})
+ (event,) = get_logs(tx, c, "_Return")
+ assert event.args.val == func_id
+ else:
+ hexstr = (method_id + argsdata).hex()
+ txdata = {"to": c.address, "data": hexstr, "value": 1}
+ with tx_failed():
+ w3.eth.send_transaction(txdata)
+
+ # now do calldatasize check
+ # strip some bytes
+ calldata = (method_id + argsdata)[:-n_strip_bytes]
+ hexstr = calldata.hex()
+ tx_params = {"to": c.address, "data": hexstr}
+ if n_calldata_words == 0 and j == 0:
+ # no args, hit default function
+ if default_fn_mutability == "":
+ with tx_failed():
+ w3.eth.send_transaction(tx_params)
+ elif default_fn_mutability == "@payable":
+ # we should be able to send eth to it
+ tx_params["value"] = 1
+ tx = w3.eth.send_transaction(tx_params)
+ logs = get_logs(tx, c, "CalledDefault")
+ assert len(logs) == 1
+ else:
+ tx = w3.eth.send_transaction(tx_params)
+
+ # note: can't emit logs from view/pure functions,
+ # so the logging is not tested.
+ if default_fn_mutability == "@nonpayable":
+ logs = get_logs(tx, c, "CalledDefault")
+ assert len(logs) == 1
+
+ # check default function reverts
+ tx_params["value"] = 1
+ with tx_failed():
+ w3.eth.send_transaction(tx_params)
+ else:
+ with tx_failed():
+ w3.eth.send_transaction(tx_params)
+
+ _test()
diff --git a/tests/functional/codegen/test_selector_table_stability.py b/tests/functional/codegen/test_selector_table_stability.py
new file mode 100644
index 0000000000..27f82416d6
--- /dev/null
+++ b/tests/functional/codegen/test_selector_table_stability.py
@@ -0,0 +1,55 @@
+from vyper.codegen.jumptable_utils import generate_sparse_jumptable_buckets
+from vyper.compiler import compile_code
+from vyper.compiler.settings import OptimizationLevel, Settings
+
+
+def test_dense_jumptable_stability():
+ function_names = [f"foo{i}" for i in range(30)]
+
+ code = "\n".join(f"@external\ndef {name}():\n pass" for name in function_names)
+
+ output = compile_code(
+ code, output_formats=["asm"], settings=Settings(optimize=OptimizationLevel.CODESIZE)
+ )
+
+ # test that the selector table data is stable across different runs
+ # (tox should provide different PYTHONHASHSEEDs).
+ expected_asm = """{ DATA _sym_BUCKET_HEADERS b\'\\x0bB\' _sym_bucket_0 b\'\\n\' b\'+\\x8d\' _sym_bucket_1 b\'\\x0c\' b\'\\x00\\x85\' _sym_bucket_2 b\'\\x08\' } { DATA _sym_bucket_1 b\'\\xd8\\xee\\xa1\\xe8\' _sym_external 6 foo6()3639517672 b\'\\x05\' b\'\\xd2\\x9e\\xe0\\xf9\' _sym_external 0 foo0()3533627641 b\'\\x05\' b\'\\x05\\xf1\\xe0_\' _sym_external 2 foo2()99737695 b\'\\x05\' b\'\\x91\\t\\xb4{\' _sym_external 23 foo23()2433332347 b\'\\x05\' b\'np3\\x7f\' _sym_external 11 foo11()1852846975 b\'\\x05\' b\'&\\xf5\\x96\\xf9\' _sym_external 13 foo13()653629177 b\'\\x05\' b\'\\x04ga\\xeb\' _sym_external 14 foo14()73884139 b\'\\x05\' b\'\\x89\\x06\\xad\\xc6\' _sym_external 17 foo17()2298916294 b\'\\x05\' b\'\\xe4%\\xac\\xd1\' _sym_external 4 foo4()3827674321 b\'\\x05\' b\'yj\\x01\\xac\' _sym_external 7 foo7()2036990380 b\'\\x05\' b\'\\xf1\\xe6K\\xe5\' _sym_external 29 foo29()4058401765 b\'\\x05\' b\'\\xd2\\x89X\\xb8\' _sym_external 3 foo3()3532216504 b\'\\x05\' } { DATA _sym_bucket_2 b\'\\x06p\\xffj\' _sym_external 25 foo25()108068714 b\'\\x05\' b\'\\x964\\x99I\' _sym_external 24 foo24()2520029513 b\'\\x05\' b\'s\\x81\\xe7\\xc1\' _sym_external 10 foo10()1937893313 b\'\\x05\' b\'\\x85\\xad\\xc11\' _sym_external 28 foo28()2242756913 b\'\\x05\' b\'\\xfa"\\xb1\\xed\' _sym_external 5 foo5()4196577773 b\'\\x05\' b\'A\\xe7[\\x05\' _sym_external 22 foo22()1105681157 b\'\\x05\' b\'\\xd3\\x89U\\xe8\' _sym_external 1 foo1()3548993000 b\'\\x05\' b\'hL\\xf8\\xf3\' _sym_external 20 foo20()1749874931 b\'\\x05\' } { DATA _sym_bucket_0 b\'\\xee\\xd9\\x1d\\xe3\' _sym_external 9 foo9()4007206371 b\'\\x05\' b\'a\\xbc\\x1ch\' _sym_external 16 foo16()1639717992 b\'\\x05\' b\'\\xd3*\\xa7\\x0c\' _sym_external 21 foo21()3542787852 b\'\\x05\' b\'\\x18iG\\xd9\' _sym_external 19 foo19()409552857 b\'\\x05\' b\'\\n\\xf1\\xf9\\x7f\' _sym_external 18 foo18()183630207 b\'\\x05\' b\')\\xda\\xd7`\' _sym_external 27 foo27()702207840 b\'\\x05\' b\'2\\xf6\\xaa\\xda\' _sym_external 12 foo12()855026394 b\'\\x05\' b\'\\xbe\\xb5\\x05\\xf5\' _sym_external 15 foo15()3199534581 b\'\\x05\' b\'\\xfc\\xa7_\\xe6\' _sym_external 8 foo8()4238827494 b\'\\x05\' b\'\\x1b\\x12C8\' _sym_external 26 foo26()454181688 b\'\\x05\' } }""" # noqa: E501
+ assert expected_asm in output["asm"]
+
+
+def test_sparse_jumptable_stability():
+ function_names = [f"foo{i}()" for i in range(30)]
+
+ # sparse jumptable is not as complicated in assembly.
+ # here just test the data structure is stable
+
+ n_buckets, buckets = generate_sparse_jumptable_buckets(function_names)
+ assert n_buckets == 33
+
+ # the buckets sorted by id are what go into the IR, check equality against
+ # expected:
+ assert sorted(buckets.items()) == [
+ (0, [4238827494, 1639717992]),
+ (1, [1852846975]),
+ (2, [1749874931]),
+ (3, [4007206371]),
+ (4, [2298916294]),
+ (7, [2036990380]),
+ (10, [3639517672, 73884139]),
+ (12, [3199534581]),
+ (13, [99737695]),
+ (14, [3548993000, 4196577773]),
+ (15, [454181688, 702207840]),
+ (16, [3533627641]),
+ (17, [108068714]),
+ (20, [1105681157]),
+ (21, [409552857, 3542787852]),
+ (22, [4058401765]),
+ (23, [2520029513, 2242756913]),
+ (24, [855026394, 183630207]),
+ (25, [3532216504, 653629177]),
+ (26, [1937893313]),
+ (28, [2433332347]),
+ (31, [3827674321]),
+ ]
diff --git a/tests/functional/codegen/test_stateless_modules.py b/tests/functional/codegen/test_stateless_modules.py
new file mode 100644
index 0000000000..2abc164689
--- /dev/null
+++ b/tests/functional/codegen/test_stateless_modules.py
@@ -0,0 +1,336 @@
+import hypothesis.strategies as st
+import pytest
+from hypothesis import given, settings
+
+from vyper import compiler
+from vyper.exceptions import (
+ CallViolation,
+ DuplicateImport,
+ ImportCycle,
+ StructureException,
+ TypeMismatch,
+)
+
+# test modules which have no variables - "libraries"
+
+
+def test_simple_library(get_contract, make_input_bundle, w3):
+ library_source = """
+@internal
+def foo() -> uint256:
+ return block.number + 1
+ """
+ main = """
+import library
+
+@external
+def bar() -> uint256:
+ return library.foo() - 1
+ """
+ input_bundle = make_input_bundle({"library.vy": library_source})
+
+ c = get_contract(main, input_bundle=input_bundle)
+
+ assert c.bar() == w3.eth.block_number
+
+
+# is this the best place for this?
+def test_import_cycle(make_input_bundle):
+ code_a = "import b\n"
+ code_b = "import a\n"
+
+ input_bundle = make_input_bundle({"a.vy": code_a, "b.vy": code_b})
+
+ with pytest.raises(ImportCycle):
+ compiler.compile_code(code_a, input_bundle=input_bundle)
+
+
+# test we can have a function in the library with the same name as
+# in the main contract
+def test_library_function_same_name(get_contract, make_input_bundle):
+ library = """
+@internal
+def foo() -> uint256:
+ return 10
+ """
+
+ main = """
+import library
+
+@internal
+def foo() -> uint256:
+ return 100
+
+@external
+def self_foo() -> uint256:
+ return self.foo()
+
+@external
+def library_foo() -> uint256:
+ return library.foo()
+ """
+
+ input_bundle = make_input_bundle({"library.vy": library})
+
+ c = get_contract(main, input_bundle=input_bundle)
+
+ assert c.self_foo() == 100
+ assert c.library_foo() == 10
+
+
+def test_transitive_import(get_contract, make_input_bundle):
+ a = """
+@internal
+def foo() -> uint256:
+ return 1
+ """
+ b = """
+import a
+
+@internal
+def bar() -> uint256:
+ return a.foo() + 1
+ """
+ c = """
+import b
+
+@external
+def baz() -> uint256:
+ return b.bar() + 1
+ """
+ # more complicated call graph, with `a` imported twice.
+ d = """
+import b
+import a
+
+@external
+def qux() -> uint256:
+ s: uint256 = a.foo()
+ return s + b.bar() + 1
+ """
+ input_bundle = make_input_bundle({"a.vy": a, "b.vy": b, "c.vy": c, "d.vy": d})
+
+ contract = get_contract(c, input_bundle=input_bundle)
+ assert contract.baz() == 3
+ contract = get_contract(d, input_bundle=input_bundle)
+ assert contract.qux() == 4
+
+
+def test_cannot_call_library_external_functions(make_input_bundle):
+ library_source = """
+@external
+def foo():
+ pass
+ """
+ contract_source = """
+import library
+
+@external
+def bar():
+ library.foo()
+ """
+ input_bundle = make_input_bundle({"library.vy": library_source, "contract.vy": contract_source})
+ with pytest.raises(CallViolation):
+ compiler.compile_code(contract_source, input_bundle=input_bundle)
+
+
+def test_library_external_functions_not_in_abi(get_contract, make_input_bundle):
+ library_source = """
+@external
+def foo():
+ pass
+ """
+ contract_source = """
+import library
+
+@external
+def bar():
+ pass
+ """
+ input_bundle = make_input_bundle({"library.vy": library_source, "contract.vy": contract_source})
+ c = get_contract(contract_source, input_bundle=input_bundle)
+ assert not hasattr(c, "foo")
+
+
+def test_library_structs(get_contract, make_input_bundle):
+ library_source = """
+struct SomeStruct:
+ x: uint256
+
+@internal
+def foo() -> SomeStruct:
+ return SomeStruct({x: 1})
+ """
+ contract_source = """
+import library
+
+@external
+def bar(s: library.SomeStruct):
+ pass
+
+@external
+def baz() -> library.SomeStruct:
+ return library.SomeStruct({x: 2})
+
+@external
+def qux() -> library.SomeStruct:
+ return library.foo()
+ """
+ input_bundle = make_input_bundle({"library.vy": library_source, "contract.vy": contract_source})
+ c = get_contract(contract_source, input_bundle=input_bundle)
+
+ assert c.bar((1,)) == []
+
+ assert c.baz() == (2,)
+ assert c.qux() == (1,)
+
+
+# test calls to library functions in statement position
+def test_library_statement_calls(get_contract, make_input_bundle, tx_failed):
+ library_source = """
+from vyper.interfaces import ERC20
+@internal
+def check_adds_to_ten(x: uint256, y: uint256):
+ assert x + y == 10
+ """
+ contract_source = """
+import library
+
+counter: public(uint256)
+
+@external
+def foo(x: uint256):
+ library.check_adds_to_ten(3, x)
+ self.counter = x
+ """
+ input_bundle = make_input_bundle({"library.vy": library_source, "contract.vy": contract_source})
+
+ c = get_contract(contract_source, input_bundle=input_bundle)
+
+ c.foo(7, transact={})
+
+ assert c.counter() == 7
+
+ with tx_failed():
+ c.foo(8)
+
+
+def test_library_is_typechecked(make_input_bundle):
+ library_source = """
+@internal
+def foo():
+ asdlkfjasdflkajsdf
+ """
+ contract_source = """
+import library
+ """
+
+ input_bundle = make_input_bundle({"library.vy": library_source, "contract.vy": contract_source})
+ with pytest.raises(StructureException):
+ compiler.compile_code(contract_source, input_bundle=input_bundle)
+
+
+def test_library_is_typechecked2(make_input_bundle):
+ # check that we typecheck against imported function signatures
+ library_source = """
+@internal
+def foo() -> uint256:
+ return 1
+ """
+ contract_source = """
+import library
+
+@external
+def foo() -> bytes32:
+ return library.foo()
+ """
+
+ input_bundle = make_input_bundle({"library.vy": library_source, "contract.vy": contract_source})
+ with pytest.raises(TypeMismatch):
+ compiler.compile_code(contract_source, input_bundle=input_bundle)
+
+
+def test_reject_duplicate_imports(make_input_bundle):
+ library_source = """
+ """
+
+ contract_source = """
+import library
+import library as library2
+ """
+ input_bundle = make_input_bundle({"library.vy": library_source, "contract.vy": contract_source})
+ with pytest.raises(DuplicateImport):
+ compiler.compile_code(contract_source, input_bundle=input_bundle)
+
+
+def test_nested_module_access(get_contract, make_input_bundle):
+ lib1 = """
+import lib2
+
+@internal
+def lib2_foo() -> uint256:
+ return lib2.foo()
+ """
+ lib2 = """
+@internal
+def foo() -> uint256:
+ return 1337
+ """
+
+ main = """
+import lib1
+import lib2
+
+@external
+def lib1_foo() -> uint256:
+ return lib1.lib2_foo()
+
+@external
+def lib2_foo() -> uint256:
+ return lib1.lib2.foo()
+ """
+ input_bundle = make_input_bundle({"lib1.vy": lib1, "lib2.vy": lib2})
+ c = get_contract(main, input_bundle=input_bundle)
+
+ assert c.lib1_foo() == c.lib2_foo() == 1337
+
+
+_int_127 = st.integers(min_value=0, max_value=127)
+_bytes_128 = st.binary(min_size=0, max_size=128)
+
+
+def test_slice_builtin(get_contract, make_input_bundle):
+ lib = """
+@internal
+def slice_input(x: Bytes[128], start: uint256, length: uint256) -> Bytes[128]:
+ return slice(x, start, length)
+ """
+
+ main = """
+import lib
+@external
+def lib_slice_input(x: Bytes[128], start: uint256, length: uint256) -> Bytes[128]:
+ return lib.slice_input(x, start, length)
+
+@external
+def slice_input(x: Bytes[128], start: uint256, length: uint256) -> Bytes[128]:
+ return slice(x, start, length)
+ """
+ input_bundle = make_input_bundle({"lib.vy": lib})
+ c = get_contract(main, input_bundle=input_bundle)
+
+ # use an inner test so that we can cache the result of get_contract()
+ @given(start=_int_127, length=_int_127, bytesdata=_bytes_128)
+ @settings(max_examples=100)
+ def _test(bytesdata, start, length):
+ # surjectively map start into allowable range
+ if start > len(bytesdata):
+ start = start % (len(bytesdata) or 1)
+ # surjectively map length into allowable range
+ if length > (len(bytesdata) - start):
+ length = length % ((len(bytesdata) - start) or 1)
+ main_result = c.slice_input(bytesdata, start, length)
+ library_result = c.lib_slice_input(bytesdata, start, length)
+ assert main_result == library_result == bytesdata[start : start + length]
+
+ _test()
diff --git a/tests/parser/types/numbers/test_constants.py b/tests/functional/codegen/types/numbers/test_constants.py
similarity index 88%
rename from tests/parser/types/numbers/test_constants.py
rename to tests/functional/codegen/types/numbers/test_constants.py
index 0d5e386dad..af871983ab 100644
--- a/tests/parser/types/numbers/test_constants.py
+++ b/tests/functional/codegen/types/numbers/test_constants.py
@@ -4,20 +4,27 @@
import pytest
from vyper.compiler import compile_code
-from vyper.exceptions import InvalidType
+from vyper.exceptions import TypeMismatch
from vyper.utils import MemoryPositions
+def search_for_sublist(ir, sublist):
+ _list = ir.to_list() if hasattr(ir, "to_list") else ir
+ if _list == sublist:
+ return True
+ return isinstance(_list, list) and any(search_for_sublist(i, sublist) for i in _list)
+
+
def test_builtin_constants(get_contract_with_gas_estimation):
code = """
@external
def test_zaddress(a: address) -> bool:
- return a == ZERO_ADDRESS
+ return a == empty(address)
@external
def test_empty_bytes32(a: bytes32) -> bool:
- return a == EMPTY_BYTES32
+ return a == empty(bytes32)
@external
@@ -81,12 +88,12 @@ def goo() -> int128:
@external
def hoo() -> bytes32:
- bar: bytes32 = EMPTY_BYTES32
+ bar: bytes32 = empty(bytes32)
return bar
@external
def joo() -> address:
- bar: address = ZERO_ADDRESS
+ bar: address = empty(address)
return bar
@external
@@ -151,7 +158,7 @@ def test_custom_constants_fail(get_contract, assert_compile_failed, storage_type
def foo() -> {return_type}:
return MY_CONSTANT
"""
- assert_compile_failed(lambda: get_contract(code), InvalidType)
+ assert_compile_failed(lambda: get_contract(code), TypeMismatch)
def test_constant_address(get_contract):
@@ -192,7 +199,7 @@ def test() -> Bytes[100]:
assert c.test() == test_str
-def test_constant_folds(search_for_sublist):
+def test_constant_folds():
some_prime = 10013677
code = f"""
SOME_CONSTANT: constant(uint256) = 11 + 1
@@ -205,11 +212,9 @@ def test() -> uint256:
ret: uint256 = 2**SOME_CONSTANT * SOME_PRIME
return ret
"""
-
- ir = compile_code(code, ["ir"])["ir"]
- assert search_for_sublist(
- ir, ["mstore", [MemoryPositions.RESERVED_MEMORY], [2**12 * some_prime]]
- )
+ ir = compile_code(code, output_formats=["ir"])["ir"]
+ search = ["mstore", [MemoryPositions.RESERVED_MEMORY], [2**12 * some_prime]]
+ assert search_for_sublist(ir, search)
def test_constant_lists(get_contract):
diff --git a/tests/parser/types/numbers/test_decimals.py b/tests/functional/codegen/types/numbers/test_decimals.py
similarity index 76%
rename from tests/parser/types/numbers/test_decimals.py
rename to tests/functional/codegen/types/numbers/test_decimals.py
index 1418eab063..fcf71f12f0 100644
--- a/tests/parser/types/numbers/test_decimals.py
+++ b/tests/functional/codegen/types/numbers/test_decimals.py
@@ -3,7 +3,13 @@
import pytest
-from vyper.exceptions import DecimalOverrideException, InvalidOperation, TypeMismatch
+from vyper import compile_code
+from vyper.exceptions import (
+ DecimalOverrideException,
+ InvalidOperation,
+ OverflowException,
+ TypeMismatch,
+)
from vyper.utils import DECIMAL_EPSILON, SizeLimits
@@ -24,23 +30,25 @@ def test_decimal_override():
@pytest.mark.parametrize("op", ["**", "&", "|", "^"])
-def test_invalid_ops(get_contract, assert_compile_failed, op):
+def test_invalid_ops(op):
code = f"""
@external
def foo(x: decimal, y: decimal) -> decimal:
return x {op} y
"""
- assert_compile_failed(lambda: get_contract(code), InvalidOperation)
+ with pytest.raises(InvalidOperation):
+ compile_code(code)
@pytest.mark.parametrize("op", ["not"])
-def test_invalid_unary_ops(get_contract, assert_compile_failed, op):
+def test_invalid_unary_ops(op):
code = f"""
@external
def foo(x: decimal) -> decimal:
return {op} x
"""
- assert_compile_failed(lambda: get_contract(code), InvalidOperation)
+ with pytest.raises(InvalidOperation):
+ compile_code(code)
def quantize(x: Decimal) -> Decimal:
@@ -156,7 +164,7 @@ def iarg() -> uint256:
print("Passed fractional multiplication test")
-def test_mul_overflow(assert_tx_failed, get_contract_with_gas_estimation):
+def test_mul_overflow(tx_failed, get_contract_with_gas_estimation):
mul_code = """
@external
@@ -170,12 +178,14 @@ def _num_mul(x: decimal, y: decimal) -> decimal:
x = Decimal("85070591730234615865843651857942052864")
y = Decimal("136112946768375385385349842973")
- assert_tx_failed(lambda: c._num_mul(x, y))
+ with tx_failed():
+ c._num_mul(x, y)
x = SizeLimits.MAX_AST_DECIMAL
y = 1 + DECIMAL_EPSILON
- assert_tx_failed(lambda: c._num_mul(x, y))
+ with tx_failed():
+ c._num_mul(x, y)
assert c._num_mul(x, Decimal(1)) == x
@@ -186,7 +196,7 @@ def _num_mul(x: decimal, y: decimal) -> decimal:
# division failure modes(!)
-def test_div_overflow(get_contract, assert_tx_failed):
+def test_div_overflow(get_contract, tx_failed):
code = """
@external
def foo(x: decimal, y: decimal) -> decimal:
@@ -198,32 +208,39 @@ def foo(x: decimal, y: decimal) -> decimal:
x = SizeLimits.MIN_AST_DECIMAL
y = -DECIMAL_EPSILON
- assert_tx_failed(lambda: c.foo(x, y))
- assert_tx_failed(lambda: c.foo(x, Decimal(0)))
- assert_tx_failed(lambda: c.foo(y, Decimal(0)))
+ with tx_failed():
+ c.foo(x, y)
+ with tx_failed():
+ c.foo(x, Decimal(0))
+ with tx_failed():
+ c.foo(y, Decimal(0))
y = Decimal(1) - DECIMAL_EPSILON # 0.999999999
- assert_tx_failed(lambda: c.foo(x, y))
+ with tx_failed():
+ c.foo(x, y)
y = Decimal(-1)
- assert_tx_failed(lambda: c.foo(x, y))
+ with tx_failed():
+ c.foo(x, y)
assert c.foo(x, Decimal(1)) == x
assert c.foo(x, 1 + DECIMAL_EPSILON) == quantize(x / (1 + DECIMAL_EPSILON))
x = SizeLimits.MAX_AST_DECIMAL
- assert_tx_failed(lambda: c.foo(x, DECIMAL_EPSILON))
+ with tx_failed():
+ c.foo(x, DECIMAL_EPSILON)
y = Decimal(1) - DECIMAL_EPSILON
- assert_tx_failed(lambda: c.foo(x, y))
+ with tx_failed():
+ c.foo(x, y)
assert c.foo(x, Decimal(1)) == x
assert c.foo(x, 1 + DECIMAL_EPSILON) == quantize(x / (1 + DECIMAL_EPSILON))
-def test_decimal_min_max_literals(assert_tx_failed, get_contract_with_gas_estimation):
+def test_decimal_min_max_literals(tx_failed, get_contract_with_gas_estimation):
code = """
@external
def maximum():
@@ -254,11 +271,32 @@ def bar(num: decimal) -> decimal:
assert c.bar(Decimal("1e37")) == Decimal("-9e37") # Math lines up
-def test_exponents(assert_compile_failed, get_contract):
+def test_exponents():
code = """
@external
def foo() -> decimal:
return 2.2 ** 2.0
"""
- assert_compile_failed(lambda: get_contract(code), TypeMismatch)
+ with pytest.raises(TypeMismatch):
+ compile_code(code)
+
+
+def test_decimal_nested_intermediate_overflow():
+ code = """
+@external
+def foo():
+ a: decimal = 18707220957835557353007165858768422651595.9365500927 + 1e-10 - 1e-10
+ """
+ with pytest.raises(OverflowException):
+ compile_code(code)
+
+
+def test_replace_decimal_nested_intermediate_underflow(dummy_input_bundle):
+ code = """
+@external
+def foo():
+ a: decimal = -18707220957835557353007165858768422651595.9365500928 - 1e-10 + 1e-10
+ """
+ with pytest.raises(OverflowException):
+ compile_code(code)
diff --git a/tests/parser/features/arithmetic/test_division.py b/tests/functional/codegen/types/numbers/test_division.py
similarity index 100%
rename from tests/parser/features/arithmetic/test_division.py
rename to tests/functional/codegen/types/numbers/test_division.py
diff --git a/tests/fuzzing/test_exponents.py b/tests/functional/codegen/types/numbers/test_exponents.py
similarity index 79%
rename from tests/fuzzing/test_exponents.py
rename to tests/functional/codegen/types/numbers/test_exponents.py
index 29c1f198ed..e958436efb 100644
--- a/tests/fuzzing/test_exponents.py
+++ b/tests/functional/codegen/types/numbers/test_exponents.py
@@ -7,7 +7,7 @@
@pytest.mark.fuzzing
@pytest.mark.parametrize("power", range(2, 255))
-def test_exp_uint256(get_contract, assert_tx_failed, power):
+def test_exp_uint256(get_contract, tx_failed, power):
code = f"""
@external
def foo(a: uint256) -> uint256:
@@ -20,12 +20,13 @@ def foo(a: uint256) -> uint256:
c = get_contract(code)
c.foo(max_base)
- assert_tx_failed(lambda: c.foo(max_base + 1))
+ with tx_failed():
+ c.foo(max_base + 1)
@pytest.mark.fuzzing
@pytest.mark.parametrize("power", range(2, 127))
-def test_exp_int128(get_contract, assert_tx_failed, power):
+def test_exp_int128(get_contract, tx_failed, power):
code = f"""
@external
def foo(a: int128) -> int128:
@@ -44,13 +45,15 @@ def foo(a: int128) -> int128:
c.foo(max_base)
c.foo(min_base)
- assert_tx_failed(lambda: c.foo(max_base + 1))
- assert_tx_failed(lambda: c.foo(min_base - 1))
+ with tx_failed():
+ c.foo(max_base + 1)
+ with tx_failed():
+ c.foo(min_base - 1)
@pytest.mark.fuzzing
@pytest.mark.parametrize("power", range(2, 15))
-def test_exp_int16(get_contract, assert_tx_failed, power):
+def test_exp_int16(get_contract, tx_failed, power):
code = f"""
@external
def foo(a: int16) -> int16:
@@ -69,8 +72,10 @@ def foo(a: int16) -> int16:
c.foo(max_base)
c.foo(min_base)
- assert_tx_failed(lambda: c.foo(max_base + 1))
- assert_tx_failed(lambda: c.foo(min_base - 1))
+ with tx_failed():
+ c.foo(max_base + 1)
+ with tx_failed():
+ c.foo(min_base - 1)
@pytest.mark.fuzzing
@@ -92,8 +97,8 @@ def foo(a: int16) -> int16:
@example(a=2**127 - 1)
# 256 bits
@example(a=2**256 - 1)
-@settings(max_examples=200, deadline=1000)
-def test_max_exp(get_contract, assert_tx_failed, a):
+@settings(max_examples=200)
+def test_max_exp(get_contract, tx_failed, a):
code = f"""
@external
def foo(b: uint256) -> uint256:
@@ -108,7 +113,8 @@ def foo(b: uint256) -> uint256:
assert a ** (max_power + 1) >= 2**256
c.foo(max_power)
- assert_tx_failed(lambda: c.foo(max_power + 1))
+ with tx_failed():
+ c.foo(max_power + 1)
@pytest.mark.fuzzing
@@ -127,8 +133,8 @@ def foo(b: uint256) -> uint256:
@example(a=2**63 - 1)
# 128 bits
@example(a=2**127 - 1)
-@settings(max_examples=200, deadline=1000)
-def test_max_exp_int128(get_contract, assert_tx_failed, a):
+@settings(max_examples=200)
+def test_max_exp_int128(get_contract, tx_failed, a):
code = f"""
@external
def foo(b: int128) -> int128:
@@ -143,4 +149,5 @@ def foo(b: int128) -> int128:
assert not -(2**127) <= a ** (max_power + 1) < 2**127
c.foo(max_power)
- assert_tx_failed(lambda: c.foo(max_power + 1))
+ with tx_failed():
+ c.foo(max_power + 1)
diff --git a/tests/parser/types/numbers/test_isqrt.py b/tests/functional/codegen/types/numbers/test_isqrt.py
similarity index 98%
rename from tests/parser/types/numbers/test_isqrt.py
rename to tests/functional/codegen/types/numbers/test_isqrt.py
index ce26d24d06..b734323a6e 100644
--- a/tests/parser/types/numbers/test_isqrt.py
+++ b/tests/functional/codegen/types/numbers/test_isqrt.py
@@ -119,7 +119,6 @@ def test(a: uint256) -> (uint256, uint256, uint256, uint256, uint256, String[100
@hypothesis.example(2704)
@hypothesis.example(110889)
@hypothesis.example(32239684)
-@hypothesis.settings(deadline=1000)
def test_isqrt_valid_range(isqrt_contract, value):
vyper_isqrt = isqrt_contract.test(value)
actual_isqrt = math.isqrt(value)
diff --git a/tests/parser/features/arithmetic/test_modulo.py b/tests/functional/codegen/types/numbers/test_modulo.py
similarity index 92%
rename from tests/parser/features/arithmetic/test_modulo.py
rename to tests/functional/codegen/types/numbers/test_modulo.py
index 018a406baa..465426cd1d 100644
--- a/tests/parser/features/arithmetic/test_modulo.py
+++ b/tests/functional/codegen/types/numbers/test_modulo.py
@@ -31,14 +31,15 @@ def num_modulo_decimal() -> decimal:
assert c.num_modulo_decimal() == Decimal(".5")
-def test_modulo_with_input_of_zero(assert_tx_failed, get_contract_with_gas_estimation):
+def test_modulo_with_input_of_zero(tx_failed, get_contract_with_gas_estimation):
code = """
@external
def foo(a: decimal, b: decimal) -> decimal:
return a % b
"""
c = get_contract_with_gas_estimation(code)
- assert_tx_failed(lambda: c.foo(Decimal("1"), Decimal("0")))
+ with tx_failed():
+ c.foo(Decimal("1"), Decimal("0"))
def test_literals_vs_evm(get_contract):
diff --git a/tests/parser/types/numbers/test_signed_ints.py b/tests/functional/codegen/types/numbers/test_signed_ints.py
similarity index 77%
rename from tests/parser/types/numbers/test_signed_ints.py
rename to tests/functional/codegen/types/numbers/test_signed_ints.py
index 281aab429c..a10eaee408 100644
--- a/tests/parser/types/numbers/test_signed_ints.py
+++ b/tests/functional/codegen/types/numbers/test_signed_ints.py
@@ -4,6 +4,7 @@
import pytest
+from vyper import compile_code
from vyper.exceptions import InvalidOperation, InvalidType, OverflowException, ZeroDivisionException
from vyper.semantics.types import IntegerT
from vyper.utils import evm_div, evm_mod
@@ -12,7 +13,7 @@
@pytest.mark.parametrize("typ", types)
-def test_exponent_base_zero(get_contract, assert_tx_failed, typ):
+def test_exponent_base_zero(get_contract, tx_failed, typ):
code = f"""
@external
def foo(x: {typ}) -> {typ}:
@@ -25,12 +26,14 @@ def foo(x: {typ}) -> {typ}:
assert c.foo(1) == 0
assert c.foo(hi) == 0
- assert_tx_failed(lambda: c.foo(-1))
- assert_tx_failed(lambda: c.foo(lo)) # note: lo < 0
+ with tx_failed():
+ c.foo(-1)
+ with tx_failed():
+ c.foo(lo) # note: lo < 0
@pytest.mark.parametrize("typ", types)
-def test_exponent_base_one(get_contract, assert_tx_failed, typ):
+def test_exponent_base_one(get_contract, tx_failed, typ):
code = f"""
@external
def foo(x: {typ}) -> {typ}:
@@ -43,8 +46,10 @@ def foo(x: {typ}) -> {typ}:
assert c.foo(1) == 1
assert c.foo(hi) == 1
- assert_tx_failed(lambda: c.foo(-1))
- assert_tx_failed(lambda: c.foo(lo))
+ with tx_failed():
+ c.foo(-1)
+ with tx_failed():
+ c.foo(lo)
def test_exponent_base_minus_one(get_contract):
@@ -63,7 +68,7 @@ def foo(x: int256) -> int256:
# TODO: make this test pass
@pytest.mark.parametrize("base", (0, 1))
-def test_exponent_negative_power(get_contract, assert_tx_failed, base):
+def test_exponent_negative_power(get_contract, tx_failed, base):
# #2985
code = f"""
@external
@@ -73,7 +78,8 @@ def bar() -> int16:
"""
c = get_contract(code)
# known bug: 2985
- assert_tx_failed(lambda: c.bar())
+ with tx_failed():
+ c.bar()
def test_exponent_min_int16(get_contract):
@@ -103,7 +109,7 @@ def foo() -> int256:
@pytest.mark.parametrize("typ", types)
-def test_exponent(get_contract, assert_tx_failed, typ):
+def test_exponent(get_contract, tx_failed, typ):
code = f"""
@external
def foo(x: {typ}) -> {typ}:
@@ -116,7 +122,8 @@ def foo(x: {typ}) -> {typ}:
test_cases = [0, 1, 3, 4, 126, 127, -1, lo, hi]
for x in test_cases:
if x * 2 >= typ.bits or x < 0: # out of bounds
- assert_tx_failed(lambda: c.foo(x))
+ with tx_failed():
+ c.foo(x)
else:
assert c.foo(x) == 4**x
@@ -145,7 +152,7 @@ def negative_four() -> {typ}:
@pytest.mark.parametrize("typ", types)
-def test_num_bound(assert_tx_failed, get_contract_with_gas_estimation, typ):
+def test_num_bound(tx_failed, get_contract_with_gas_estimation, typ):
lo, hi = typ.ast_bounds
num_bound_code = f"""
@@ -180,31 +187,36 @@ def _num_min() -> {typ}:
assert c._num_sub(lo, 0) == lo
assert c._num_add(hi - 1, 1) == hi
assert c._num_sub(lo + 1, 1) == lo
- assert_tx_failed(lambda: c._num_add(hi, 1))
- assert_tx_failed(lambda: c._num_sub(lo, 1))
- assert_tx_failed(lambda: c._num_add(hi - 1, 2))
- assert_tx_failed(lambda: c._num_sub(lo + 1, 2))
+ with tx_failed():
+ c._num_add(hi, 1)
+ with tx_failed():
+ c._num_sub(lo, 1)
+ with tx_failed():
+ c._num_add(hi - 1, 2)
+ with tx_failed():
+ c._num_sub(lo + 1, 2)
assert c._num_max() == hi
assert c._num_min() == lo
- assert_tx_failed(lambda: c._num_add3(hi, 1, -1))
+ with tx_failed():
+ c._num_add3(hi, 1, -1)
assert c._num_add3(hi, -1, 1) == hi - 1 + 1
- assert_tx_failed(lambda: c._num_add3(lo, -1, 1))
+ with tx_failed():
+ c._num_add3(lo, -1, 1)
assert c._num_add3(lo, 1, -1) == lo + 1 - 1
@pytest.mark.parametrize("typ", types)
-def test_overflow_out_of_range(get_contract, assert_compile_failed, typ):
+def test_overflow_out_of_range(get_contract, typ):
code = f"""
@external
def num_sub() -> {typ}:
return 1-2**{typ.bits}
"""
- if typ.bits == 256:
- assert_compile_failed(lambda: get_contract(code), OverflowException)
- else:
- assert_compile_failed(lambda: get_contract(code), InvalidType)
+ exc = OverflowException if typ.bits == 256 else InvalidType
+ with pytest.raises(exc):
+ compile_code(code)
ARITHMETIC_OPS = {
@@ -219,7 +231,7 @@ def num_sub() -> {typ}:
@pytest.mark.parametrize("op", sorted(ARITHMETIC_OPS.keys()))
@pytest.mark.parametrize("typ", types)
@pytest.mark.fuzzing
-def test_arithmetic_thorough(get_contract, assert_tx_failed, assert_compile_failed, op, typ):
+def test_arithmetic_thorough(get_contract, tx_failed, op, typ):
# both variables
code_1 = f"""
@external
@@ -304,15 +316,23 @@ def foo() -> {typ}:
assert get_contract(code_3).foo(y) == expected
assert get_contract(code_4).foo() == expected
elif div_by_zero:
- assert_tx_failed(lambda: c.foo(x, y))
- assert_compile_failed(lambda: get_contract(code_2), ZeroDivisionException)
- assert_tx_failed(lambda: get_contract(code_3).foo(y))
- assert_compile_failed(lambda: get_contract(code_4), ZeroDivisionException)
+ with tx_failed():
+ c.foo(x, y)
+ with pytest.raises(ZeroDivisionException):
+ compile_code(code_2)
+ with tx_failed():
+ get_contract(code_3).foo(y)
+ with pytest.raises(ZeroDivisionException):
+ compile_code(code_4)
else:
- assert_tx_failed(lambda: c.foo(x, y))
- assert_tx_failed(lambda: get_contract(code_2).foo(x))
- assert_tx_failed(lambda: get_contract(code_3).foo(y))
- assert_compile_failed(lambda: get_contract(code_4), (InvalidType, OverflowException))
+ with tx_failed():
+ c.foo(x, y)
+ with tx_failed():
+ get_contract(code_2).foo(x)
+ with tx_failed():
+ get_contract(code_3).foo(y)
+ with pytest.raises((InvalidType, OverflowException)):
+ compile_code(code_4)
COMPARISON_OPS = {
@@ -340,7 +360,7 @@ def foo(x: {typ}, y: {typ}) -> bool:
fn = COMPARISON_OPS[op]
c = get_contract(code_1)
- # note: constant folding is tested in tests/ast/folding
+ # note: constant folding is tested in tests/unit/ast/nodes
special_cases = [
lo,
lo + 1,
@@ -370,7 +390,7 @@ def foo(x: {typ}, y: {typ}) -> bool:
@pytest.mark.parametrize("typ", types)
-def test_negation(get_contract, assert_tx_failed, typ):
+def test_negation(get_contract, tx_failed, typ):
code = f"""
@external
def foo(a: {typ}) -> {typ}:
@@ -388,15 +408,27 @@ def foo(a: {typ}) -> {typ}:
assert c.foo(2) == -2
assert c.foo(-2) == 2
- assert_tx_failed(lambda: c.foo(lo))
+ with tx_failed():
+ c.foo(lo)
@pytest.mark.parametrize("typ", types)
@pytest.mark.parametrize("op", ["not"])
-def test_invalid_unary_ops(get_contract, assert_compile_failed, typ, op):
+def test_invalid_unary_ops(typ, op):
code = f"""
@external
def foo(a: {typ}) -> {typ}:
return {op} a
"""
- assert_compile_failed(lambda: get_contract(code), InvalidOperation)
+ with pytest.raises(InvalidOperation):
+ compile_code(code)
+
+
+def test_binop_nested_intermediate_underflow():
+ code = """
+@external
+def foo():
+ a: int256 = -2**255 * 2 - 10 + 100
+ """
+ with pytest.raises(InvalidType):
+ compile_code(code)
diff --git a/tests/parser/types/numbers/test_sqrt.py b/tests/functional/codegen/types/numbers/test_sqrt.py
similarity index 98%
rename from tests/parser/types/numbers/test_sqrt.py
rename to tests/functional/codegen/types/numbers/test_sqrt.py
index df1ed0539c..020a79e7ef 100644
--- a/tests/parser/types/numbers/test_sqrt.py
+++ b/tests/functional/codegen/types/numbers/test_sqrt.py
@@ -145,7 +145,6 @@ def test_sqrt_bounds(sqrt_contract, value):
)
@hypothesis.example(value=Decimal(SizeLimits.MAX_INT128))
@hypothesis.example(value=Decimal(0))
-@hypothesis.settings(deadline=1000)
def test_sqrt_valid_range(sqrt_contract, value):
vyper_sqrt = sqrt_contract.test(value)
actual_sqrt = decimal_sqrt(value)
@@ -158,7 +157,6 @@ def test_sqrt_valid_range(sqrt_contract, value):
min_value=Decimal(SizeLimits.MIN_INT128), max_value=Decimal("-1E10"), places=DECIMAL_PLACES
)
)
-@hypothesis.settings(deadline=400)
@hypothesis.example(value=Decimal(SizeLimits.MIN_INT128))
@hypothesis.example(value=Decimal("-1E10"))
def test_sqrt_invalid_range(sqrt_contract, value):
diff --git a/tests/parser/types/numbers/test_unsigned_ints.py b/tests/functional/codegen/types/numbers/test_unsigned_ints.py
similarity index 78%
rename from tests/parser/types/numbers/test_unsigned_ints.py
rename to tests/functional/codegen/types/numbers/test_unsigned_ints.py
index 683684e6be..f10e861689 100644
--- a/tests/parser/types/numbers/test_unsigned_ints.py
+++ b/tests/functional/codegen/types/numbers/test_unsigned_ints.py
@@ -4,9 +4,10 @@
import pytest
+from vyper import compile_code
from vyper.exceptions import InvalidOperation, InvalidType, OverflowException, ZeroDivisionException
from vyper.semantics.types import IntegerT
-from vyper.utils import evm_div, evm_mod
+from vyper.utils import SizeLimits, evm_div, evm_mod
types = sorted(IntegerT.unsigneds())
@@ -85,7 +86,7 @@ def foo(x: {typ}) -> {typ}:
@pytest.mark.parametrize("op", sorted(ARITHMETIC_OPS.keys()))
@pytest.mark.parametrize("typ", types)
@pytest.mark.fuzzing
-def test_arithmetic_thorough(get_contract, assert_tx_failed, assert_compile_failed, op, typ):
+def test_arithmetic_thorough(get_contract, tx_failed, op, typ):
# both variables
code_1 = f"""
@external
@@ -148,15 +149,23 @@ def foo() -> {typ}:
assert get_contract(code_3).foo(y) == expected
assert get_contract(code_4).foo() == expected
elif div_by_zero:
- assert_tx_failed(lambda: c.foo(x, y))
- assert_compile_failed(lambda: get_contract(code_2), ZeroDivisionException)
- assert_tx_failed(lambda: get_contract(code_3).foo(y))
- assert_compile_failed(lambda: get_contract(code_4), ZeroDivisionException)
+ with tx_failed():
+ c.foo(x, y)
+ with pytest.raises(ZeroDivisionException):
+ get_contract(code_2)
+ with tx_failed():
+ get_contract(code_3).foo(y)
+ with pytest.raises(ZeroDivisionException):
+ get_contract(code_4)
else:
- assert_tx_failed(lambda: c.foo(x, y))
- assert_tx_failed(lambda: get_contract(code_2).foo(x))
- assert_tx_failed(lambda: get_contract(code_3).foo(y))
- assert_compile_failed(lambda: get_contract(code_4), (InvalidType, OverflowException))
+ with tx_failed():
+ c.foo(x, y)
+ with tx_failed():
+ get_contract(code_2).foo(x)
+ with tx_failed():
+ get_contract(code_3).foo(y)
+ with pytest.raises((InvalidType, OverflowException)):
+ get_contract(code_4)
COMPARISON_OPS = {
@@ -184,7 +193,7 @@ def foo(x: {typ}, y: {typ}) -> bool:
lo, hi = typ.ast_bounds
- # note: constant folding is tested in tests/ast/folding
+ # note: folding is tested in tests/unit/ast/nodes
special_cases = [0, 1, 2, 3, hi // 2 - 1, hi // 2, hi // 2 + 1, hi - 2, hi - 1, hi]
xs = special_cases.copy()
@@ -196,7 +205,7 @@ def foo(x: {typ}, y: {typ}) -> bool:
@pytest.mark.parametrize("typ", types)
-def test_uint_literal(get_contract, assert_compile_failed, typ):
+def test_uint_literal(get_contract, typ):
lo, hi = typ.ast_bounds
good_cases = [0, 1, 2, 3, hi // 2 - 1, hi // 2, hi // 2 + 1, hi - 1, hi]
@@ -213,7 +222,13 @@ def test() -> {typ}:
assert c.test() == val
for val in bad_cases:
- assert_compile_failed(lambda: get_contract(code_template.format(typ=typ, val=val)))
+ exc = (
+ InvalidType
+ if SizeLimits.MIN_INT256 <= val <= SizeLimits.MAX_UINT256
+ else OverflowException
+ )
+ with pytest.raises(exc):
+ compile_code(code_template.format(typ=typ, val=val))
@pytest.mark.parametrize("typ", types)
@@ -224,4 +239,15 @@ def test_invalid_unary_ops(get_contract, assert_compile_failed, typ, op):
def foo(a: {typ}) -> {typ}:
return {op} a
"""
- assert_compile_failed(lambda: get_contract(code), InvalidOperation)
+ with pytest.raises(InvalidOperation):
+ compile_code(code)
+
+
+def test_binop_nested_intermediate_overflow():
+ code = """
+@external
+def foo():
+ a: uint256 = 2**255 * 2 / 10
+ """
+ with pytest.raises(OverflowException):
+ compile_code(code)
diff --git a/tests/parser/types/test_bytes.py b/tests/functional/codegen/types/test_bytes.py
similarity index 98%
rename from tests/parser/types/test_bytes.py
rename to tests/functional/codegen/types/test_bytes.py
index 01ec75d5c1..1ee9b8d835 100644
--- a/tests/parser/types/test_bytes.py
+++ b/tests/functional/codegen/types/test_bytes.py
@@ -3,7 +3,7 @@
from vyper.exceptions import InvalidType, TypeMismatch
-def test_test_bytes(get_contract_with_gas_estimation, assert_tx_failed):
+def test_test_bytes(get_contract_with_gas_estimation, tx_failed):
test_bytes = """
@external
def foo(x: Bytes[100]) -> Bytes[100]:
@@ -21,7 +21,8 @@ def foo(x: Bytes[100]) -> Bytes[100]:
print("Passed max-length bytes test")
# test for greater than 100 bytes, should raise exception
- assert_tx_failed(lambda: c.foo(b"\x35" * 101))
+ with tx_failed():
+ c.foo(b"\x35" * 101)
print("Passed input-too-long test")
diff --git a/tests/parser/types/test_bytes_literal.py b/tests/functional/codegen/types/test_bytes_literal.py
similarity index 100%
rename from tests/parser/types/test_bytes_literal.py
rename to tests/functional/codegen/types/test_bytes_literal.py
diff --git a/tests/parser/types/test_bytes_zero_padding.py b/tests/functional/codegen/types/test_bytes_zero_padding.py
similarity index 96%
rename from tests/parser/types/test_bytes_zero_padding.py
rename to tests/functional/codegen/types/test_bytes_zero_padding.py
index ee938fdffb..f9fcf37b25 100644
--- a/tests/parser/types/test_bytes_zero_padding.py
+++ b/tests/functional/codegen/types/test_bytes_zero_padding.py
@@ -26,7 +26,6 @@ def get_count(counter: uint256) -> Bytes[24]:
@pytest.mark.fuzzing
@hypothesis.given(value=hypothesis.strategies.integers(min_value=0, max_value=2**64))
-@hypothesis.settings(deadline=400)
def test_zero_pad_range(little_endian_contract, value):
actual_bytes = value.to_bytes(8, byteorder="little")
contract_bytes = little_endian_contract.get_count(value)
diff --git a/tests/parser/types/test_dynamic_array.py b/tests/functional/codegen/types/test_dynamic_array.py
similarity index 96%
rename from tests/parser/types/test_dynamic_array.py
rename to tests/functional/codegen/types/test_dynamic_array.py
index 9231d1979f..70a68e3206 100644
--- a/tests/parser/types/test_dynamic_array.py
+++ b/tests/functional/codegen/types/test_dynamic_array.py
@@ -2,6 +2,7 @@
import pytest
+from vyper.compiler import compile_code
from vyper.exceptions import (
ArgumentException,
ArrayIndexException,
@@ -102,7 +103,7 @@ def foo6() -> DynArray[DynArray[String[32], 2], 2]:
def test_list_output_tester_code(get_contract_with_gas_estimation):
list_output_tester_code = """
-enum Foobar:
+flag Foobar:
FOO
BAR
@@ -315,6 +316,21 @@ def test_array(x: int128, y: int128, z: int128, w: int128) -> int128:
def test_array_negative_accessor(get_contract_with_gas_estimation, assert_compile_failed):
+ array_constant_negative_accessor = """
+FOO: constant(int128) = -1
+@external
+def test_array(x: int128, y: int128, z: int128, w: int128) -> int128:
+ a: int128[4] = [0, 0, 0, 0]
+ a[0] = x
+ a[1] = y
+ a[2] = z
+ a[3] = w
+ return a[-4] * 1000 + a[-3] * 100 + a[-2] * 10 + a[FOO]
+ """
+
+ with pytest.raises(ArrayIndexException):
+ compile_code(array_constant_negative_accessor)
+
array_negative_accessor = """
@external
def test_array(x: int128, y: int128, z: int128, w: int128) -> int128:
@@ -759,27 +775,30 @@ def test_multi4_2() -> DynArray[DynArray[DynArray[DynArray[uint256, 2], 2], 2],
assert c.test_multi4_2() == nest4
-def test_uint256_accessor(get_contract_with_gas_estimation, assert_tx_failed):
+def test_uint256_accessor(get_contract_with_gas_estimation, tx_failed):
code = """
@external
def bounds_check_uint256(xs: DynArray[uint256, 3], ix: uint256) -> uint256:
return xs[ix]
"""
c = get_contract_with_gas_estimation(code)
- assert_tx_failed(lambda: c.bounds_check_uint256([], 0))
+ with tx_failed():
+ c.bounds_check_uint256([], 0)
assert c.bounds_check_uint256([1], 0) == 1
- assert_tx_failed(lambda: c.bounds_check_uint256([1], 1))
+ with tx_failed():
+ c.bounds_check_uint256([1], 1)
assert c.bounds_check_uint256([1, 2, 3], 0) == 1
assert c.bounds_check_uint256([1, 2, 3], 2) == 3
- assert_tx_failed(lambda: c.bounds_check_uint256([1, 2, 3], 3))
+ with tx_failed():
+ c.bounds_check_uint256([1, 2, 3], 3)
# TODO do bounds checks for nested darrays
@pytest.mark.parametrize("list_", ([], [11], [11, 12], [11, 12, 13]))
-def test_dynarray_len(get_contract_with_gas_estimation, assert_tx_failed, list_):
+def test_dynarray_len(get_contract_with_gas_estimation, tx_failed, list_):
code = """
@external
def darray_len(xs: DynArray[uint256, 3]) -> uint256:
@@ -790,7 +809,7 @@ def darray_len(xs: DynArray[uint256, 3]) -> uint256:
assert c.darray_len(list_) == len(list_)
-def test_dynarray_too_large(get_contract_with_gas_estimation, assert_tx_failed):
+def test_dynarray_too_large(get_contract_with_gas_estimation, tx_failed):
code = """
@external
def darray_len(xs: DynArray[uint256, 3]) -> uint256:
@@ -798,10 +817,11 @@ def darray_len(xs: DynArray[uint256, 3]) -> uint256:
"""
c = get_contract_with_gas_estimation(code)
- assert_tx_failed(lambda: c.darray_len([1, 2, 3, 4]))
+ with tx_failed():
+ c.darray_len([1, 2, 3, 4])
-def test_int128_accessor(get_contract_with_gas_estimation, assert_tx_failed):
+def test_int128_accessor(get_contract_with_gas_estimation, tx_failed):
code = """
@external
def bounds_check_int128(ix: int128) -> uint256:
@@ -811,8 +831,10 @@ def bounds_check_int128(ix: int128) -> uint256:
c = get_contract_with_gas_estimation(code)
assert c.bounds_check_int128(0) == 1
assert c.bounds_check_int128(2) == 3
- assert_tx_failed(lambda: c.bounds_check_int128(3))
- assert_tx_failed(lambda: c.bounds_check_int128(-1))
+ with tx_failed():
+ c.bounds_check_int128(3)
+ with tx_failed():
+ c.bounds_check_int128(-1)
def test_index_exception(get_contract_with_gas_estimation, assert_compile_failed):
@@ -1164,12 +1186,13 @@ def test_invalid_append_pop(get_contract, assert_compile_failed, code, exception
@pytest.mark.parametrize("code,check_result", append_pop_tests)
# TODO change this to fuzz random data
@pytest.mark.parametrize("test_data", [[1, 2, 3, 4, 5][:i] for i in range(6)])
-def test_append_pop(get_contract, assert_tx_failed, code, check_result, test_data):
+def test_append_pop(get_contract, tx_failed, code, check_result, test_data):
c = get_contract(code)
expected_result = check_result(test_data)
if expected_result is None:
# None is sentinel to indicate txn should revert
- assert_tx_failed(lambda: c.foo(test_data))
+ with tx_failed():
+ c.foo(test_data)
else:
assert c.foo(test_data) == expected_result
@@ -1234,7 +1257,7 @@ def foo(x: {typ}) -> {typ}:
["uint256[3]", "DynArray[uint256,3]", "DynArray[uint8, 4]", "Foo", "DynArray[Foobar, 3]"],
)
# TODO change this to fuzz random data
-def test_append_pop_complex(get_contract, assert_tx_failed, code_template, check_result, subtype):
+def test_append_pop_complex(get_contract, tx_failed, code_template, check_result, subtype):
code = code_template.format(typ=subtype)
test_data = [1, 2, 3]
if subtype == "Foo":
@@ -1247,20 +1270,21 @@ def test_append_pop_complex(get_contract, assert_tx_failed, code_template, check
"""
code = struct_def + "\n" + code
elif subtype == "DynArray[Foobar, 3]":
- enum_def = """
-enum Foobar:
+ flag_def = """
+flag Foobar:
FOO
BAR
BAZ
"""
- code = enum_def + "\n" + code
+ code = flag_def + "\n" + code
test_data = [2 ** (i - 1) for i in test_data]
c = get_contract(code)
expected_result = check_result(test_data)
if expected_result is None:
# None is sentinel to indicate txn should revert
- assert_tx_failed(lambda: c.foo(test_data))
+ with tx_failed():
+ c.foo(test_data)
else:
assert c.foo(test_data) == expected_result
@@ -1292,7 +1316,7 @@ def foo() -> (uint256, DynArray[uint256, 3], DynArray[uint256, 2]):
def test_list_of_structs_arg(get_contract):
code = """
-enum Foobar:
+flag Foobar:
FOO
BAR
@@ -1330,7 +1354,7 @@ def bar(_baz: DynArray[Foo, 3]) -> String[96]:
assert c.bar(c_input) == "Hello world!!!!"
-def test_list_of_structs_lists_with_nested_lists(get_contract, assert_tx_failed):
+def test_list_of_structs_lists_with_nested_lists(get_contract, tx_failed):
code = """
struct Bar:
a: DynArray[uint8[2], 2]
@@ -1351,7 +1375,8 @@ def foo(x: uint8) -> uint8:
"""
c = get_contract(code)
assert c.foo(17) == 98
- assert_tx_failed(lambda: c.foo(241))
+ with tx_failed():
+ c.foo(241)
def test_list_of_nested_struct_arrays(get_contract):
@@ -1622,7 +1647,7 @@ def bar() -> uint256:
assert c.bar() == 58
-def test_constant_list(get_contract, assert_tx_failed):
+def test_constant_list(get_contract, tx_failed):
some_good_primes = [5.0, 11.0, 17.0, 29.0, 37.0, 41.0]
code = f"""
MY_LIST: constant(DynArray[decimal, 6]) = {some_good_primes}
@@ -1634,7 +1659,8 @@ def ix(i: uint256) -> decimal:
for i, p in enumerate(some_good_primes):
assert c.ix(i) == p
# assert oob
- assert_tx_failed(lambda: c.ix(len(some_good_primes) + 1))
+ with tx_failed():
+ c.ix(len(some_good_primes) + 1)
def test_public_dynarray(get_contract):
@@ -1718,7 +1744,7 @@ def test_constant_list_fail(get_contract, assert_compile_failed, storage_type, r
def foo() -> DynArray[{return_type}, 3]:
return MY_CONSTANT
"""
- assert_compile_failed(lambda: get_contract(code), InvalidType)
+ assert_compile_failed(lambda: get_contract(code), TypeMismatch)
@pytest.mark.parametrize("storage_type,return_type", itertools.permutations(integer_types, 2))
@@ -1730,7 +1756,7 @@ def test_constant_list_fail_2(get_contract, assert_compile_failed, storage_type,
def foo() -> {return_type}:
return MY_CONSTANT[0]
"""
- assert_compile_failed(lambda: get_contract(code), InvalidType)
+ assert_compile_failed(lambda: get_contract(code), TypeMismatch)
@pytest.mark.parametrize("storage_type,return_type", itertools.permutations(integer_types, 2))
@@ -1831,7 +1857,8 @@ def should_revert() -> DynArray[String[65], 2]:
@pytest.mark.parametrize("code", dynarray_length_no_clobber_cases)
-def test_dynarray_length_no_clobber(get_contract, assert_tx_failed, code):
+def test_dynarray_length_no_clobber(get_contract, tx_failed, code):
# check that length is not clobbered before dynarray data copy happens
c = get_contract(code)
- assert_tx_failed(lambda: c.should_revert())
+ with tx_failed():
+ c.should_revert()
diff --git a/tests/parser/types/test_enum.py b/tests/functional/codegen/types/test_flag.py
similarity index 84%
rename from tests/parser/types/test_enum.py
rename to tests/functional/codegen/types/test_flag.py
index c66efff566..5da6d57558 100644
--- a/tests/parser/types/test_enum.py
+++ b/tests/functional/codegen/types/test_flag.py
@@ -1,6 +1,6 @@
def test_values_should_be_increasing_ints(get_contract):
code = """
-enum Action:
+flag Action:
BUY
SELL
CANCEL
@@ -26,9 +26,9 @@ def cancel() -> Action:
assert c.cancel() == 4
-def test_enum_storage(get_contract):
+def test_flag_storage(get_contract):
code = """
-enum Actions:
+flag Actions:
BUY
SELL
CANCEL
@@ -49,7 +49,7 @@ def set_and_get(a: Actions) -> Actions:
def test_eq_neq(get_contract):
code = """
-enum Roles:
+flag Roles:
USER
STAFF
ADMIN
@@ -74,9 +74,9 @@ def is_not_boss(a: Roles) -> bool:
assert c.is_not_boss(2**4) is False
-def test_bitwise(get_contract, assert_tx_failed):
+def test_bitwise(get_contract, tx_failed):
code = """
-enum Roles:
+flag Roles:
USER
STAFF
ADMIN
@@ -134,20 +134,27 @@ def binv_arg(a: Roles) -> Roles:
assert c.binv_arg(0b00000) == 0b11111
# LHS is out of bound
- assert_tx_failed(lambda: c.bor_arg(32, 3))
- assert_tx_failed(lambda: c.band_arg(32, 3))
- assert_tx_failed(lambda: c.bxor_arg(32, 3))
- assert_tx_failed(lambda: c.binv_arg(32))
+ with tx_failed():
+ c.bor_arg(32, 3)
+ with tx_failed():
+ c.band_arg(32, 3)
+ with tx_failed():
+ c.bxor_arg(32, 3)
+ with tx_failed():
+ c.binv_arg(32)
# RHS
- assert_tx_failed(lambda: c.bor_arg(3, 32))
- assert_tx_failed(lambda: c.band_arg(3, 32))
- assert_tx_failed(lambda: c.bxor_arg(3, 32))
+ with tx_failed():
+ c.bor_arg(3, 32)
+ with tx_failed():
+ c.band_arg(3, 32)
+ with tx_failed():
+ c.bxor_arg(3, 32)
-def test_augassign_storage(get_contract, w3, assert_tx_failed):
+def test_augassign_storage(get_contract, w3, tx_failed):
code = """
-enum Roles:
+flag Roles:
ADMIN
MINTER
@@ -190,7 +197,8 @@ def checkMinter(minter: address):
assert c.roles(minter_address) == 0b10
# admin is not a minter
- assert_tx_failed(lambda: c.checkMinter(admin_address))
+ with tx_failed():
+ c.checkMinter(admin_address)
c.addMinter(admin_address, transact={})
@@ -201,7 +209,8 @@ def checkMinter(minter: address):
# revoke minter
c.revokeMinter(admin_address, transact={})
assert c.roles(admin_address) == 0b01
- assert_tx_failed(lambda: c.checkMinter(admin_address))
+ with tx_failed():
+ c.checkMinter(admin_address)
# flip minter
c.flipMinter(admin_address, transact={})
@@ -211,12 +220,13 @@ def checkMinter(minter: address):
# flip minter
c.flipMinter(admin_address, transact={})
assert c.roles(admin_address) == 0b01
- assert_tx_failed(lambda: c.checkMinter(admin_address))
+ with tx_failed():
+ c.checkMinter(admin_address)
-def test_in_enum(get_contract_with_gas_estimation):
+def test_in_flag(get_contract_with_gas_estimation):
code = """
-enum Roles:
+flag Roles:
USER
STAFF
ADMIN
@@ -259,9 +269,9 @@ def baz(a: Roles) -> bool:
assert c.baz(0b01000) is False # Roles.MANAGER should fail
-def test_struct_with_enum(get_contract_with_gas_estimation):
+def test_struct_with_flag(get_contract_with_gas_estimation):
code = """
-enum Foobar:
+flag Foobar:
FOO
BAR
@@ -270,17 +280,17 @@ def test_struct_with_enum(get_contract_with_gas_estimation):
b: Foobar
@external
-def get_enum_from_struct() -> Foobar:
+def get_flag_from_struct() -> Foobar:
f: Foo = Foo({a: 1, b: Foobar.BAR})
return f.b
"""
c = get_contract_with_gas_estimation(code)
- assert c.get_enum_from_struct() == 2
+ assert c.get_flag_from_struct() == 2
-def test_mapping_with_enum(get_contract_with_gas_estimation):
+def test_mapping_with_flag(get_contract_with_gas_estimation):
code = """
-enum Foobar:
+flag Foobar:
FOO
BAR
diff --git a/tests/parser/types/test_identifier_naming.py b/tests/functional/codegen/types/test_identifier_naming.py
old mode 100755
new mode 100644
similarity index 88%
rename from tests/parser/types/test_identifier_naming.py
rename to tests/functional/codegen/types/test_identifier_naming.py
index f4f602f471..0a93329848
--- a/tests/parser/types/test_identifier_naming.py
+++ b/tests/functional/codegen/types/test_identifier_naming.py
@@ -1,16 +1,12 @@
import pytest
-from vyper.ast.folding import BUILTIN_CONSTANTS
+from vyper.ast.identifiers import RESERVED_KEYWORDS
from vyper.builtins.functions import BUILTIN_FUNCTIONS
from vyper.codegen.expr import ENVIRONMENT_VARIABLES
from vyper.exceptions import NamespaceCollision, StructureException, SyntaxException
-from vyper.semantics.namespace import RESERVED_KEYWORDS
from vyper.semantics.types.primitives import AddressT
-BUILTIN_CONSTANTS = set(BUILTIN_CONSTANTS.keys())
-ALL_RESERVED_KEYWORDS = (
- BUILTIN_CONSTANTS | BUILTIN_FUNCTIONS | RESERVED_KEYWORDS | ENVIRONMENT_VARIABLES
-)
+ALL_RESERVED_KEYWORDS = BUILTIN_FUNCTIONS | RESERVED_KEYWORDS | ENVIRONMENT_VARIABLES
@pytest.mark.parametrize("constant", sorted(ALL_RESERVED_KEYWORDS))
@@ -46,7 +42,7 @@ def test({constant}: int128):
SELF_NAMESPACE_MEMBERS = set(AddressT._type_members.keys())
-DISALLOWED_FN_NAMES = SELF_NAMESPACE_MEMBERS | RESERVED_KEYWORDS | BUILTIN_CONSTANTS
+DISALLOWED_FN_NAMES = SELF_NAMESPACE_MEMBERS | RESERVED_KEYWORDS
ALLOWED_FN_NAMES = ALL_RESERVED_KEYWORDS - DISALLOWED_FN_NAMES
diff --git a/tests/parser/types/test_lists.py b/tests/functional/codegen/types/test_lists.py
similarity index 94%
rename from tests/parser/types/test_lists.py
rename to tests/functional/codegen/types/test_lists.py
index 832b679e5e..b5b9538c20 100644
--- a/tests/parser/types/test_lists.py
+++ b/tests/functional/codegen/types/test_lists.py
@@ -2,7 +2,7 @@
import pytest
-from vyper.exceptions import ArrayIndexException, InvalidType, OverflowException, TypeMismatch
+from vyper.exceptions import ArrayIndexException, OverflowException, TypeMismatch
def test_list_tester_code(get_contract_with_gas_estimation):
@@ -353,7 +353,7 @@ def test_multi4() -> uint256[2][2][2][2]:
@pytest.mark.parametrize("type_", ["uint8", "uint256"])
-def test_unsigned_accessors(get_contract_with_gas_estimation, assert_tx_failed, type_):
+def test_unsigned_accessors(get_contract_with_gas_estimation, tx_failed, type_):
code = f"""
@external
def bounds_check(ix: {type_}) -> uint256:
@@ -363,11 +363,12 @@ def bounds_check(ix: {type_}) -> uint256:
c = get_contract_with_gas_estimation(code)
assert c.bounds_check(0) == 1
assert c.bounds_check(2) == 3
- assert_tx_failed(lambda: c.bounds_check(3))
+ with tx_failed():
+ c.bounds_check(3)
@pytest.mark.parametrize("type_", ["int128", "int256"])
-def test_signed_accessors(get_contract_with_gas_estimation, assert_tx_failed, type_):
+def test_signed_accessors(get_contract_with_gas_estimation, tx_failed, type_):
code = f"""
@external
def bounds_check(ix: {type_}) -> uint256:
@@ -377,8 +378,10 @@ def bounds_check(ix: {type_}) -> uint256:
c = get_contract_with_gas_estimation(code)
assert c.bounds_check(0) == 1
assert c.bounds_check(2) == 3
- assert_tx_failed(lambda: c.bounds_check(3))
- assert_tx_failed(lambda: c.bounds_check(-1))
+ with tx_failed():
+ c.bounds_check(3)
+ with tx_failed():
+ c.bounds_check(-1)
def test_list_check_heterogeneous_types(get_contract_with_gas_estimation, assert_compile_failed):
@@ -662,7 +665,7 @@ def foo(x: Bar[2][2][2]) -> uint256:
("bool", [True, False, True, False, True, False]),
],
)
-def test_constant_list(get_contract, assert_tx_failed, type, value):
+def test_constant_list(get_contract, tx_failed, type, value):
code = f"""
MY_LIST: constant({type}[{len(value)}]) = {value}
@external
@@ -673,7 +676,8 @@ def ix(i: uint256) -> {type}:
for i, p in enumerate(value):
assert c.ix(i) == p
# assert oob
- assert_tx_failed(lambda: c.ix(len(value) + 1))
+ with tx_failed():
+ c.ix(len(value) + 1)
def test_nested_constant_list_accessor(get_contract):
@@ -701,7 +705,7 @@ def test_constant_list_fail(get_contract, assert_compile_failed, storage_type, r
def foo() -> {return_type}[3]:
return MY_CONSTANT
"""
- assert_compile_failed(lambda: get_contract(code), InvalidType)
+ assert_compile_failed(lambda: get_contract(code), TypeMismatch)
@pytest.mark.parametrize("storage_type,return_type", itertools.permutations(integer_types, 2))
@@ -713,7 +717,7 @@ def test_constant_list_fail_2(get_contract, assert_compile_failed, storage_type,
def foo() -> {return_type}:
return MY_CONSTANT[0]
"""
- assert_compile_failed(lambda: get_contract(code), InvalidType)
+ assert_compile_failed(lambda: get_contract(code), TypeMismatch)
@pytest.mark.parametrize("storage_type,return_type", itertools.permutations(integer_types, 2))
@@ -728,7 +732,7 @@ def foo(i: uint256) -> {return_type}:
assert_compile_failed(lambda: get_contract(code), TypeMismatch)
-def test_constant_list_address(get_contract, assert_tx_failed):
+def test_constant_list_address(get_contract, tx_failed):
some_good_address = [
"0x0000000000000000000000000000000000012345",
"0x0000000000000000000000000000000000023456",
@@ -754,10 +758,11 @@ def ix(i: uint256) -> address:
for i, p in enumerate(some_good_address):
assert c.ix(i) == p
# assert oob
- assert_tx_failed(lambda: c.ix(len(some_good_address) + 1))
+ with tx_failed():
+ c.ix(len(some_good_address) + 1)
-def test_list_index_complex_expr(get_contract, assert_tx_failed):
+def test_list_index_complex_expr(get_contract, tx_failed):
# test subscripts where the index is not a literal
code = """
@external
@@ -771,7 +776,8 @@ def foo(xs: uint256[257], i: uint8) -> uint256:
assert c.foo(xs, ix) == xs[ix + 1]
# safemath should fail for uint8: 255 + 1.
- assert_tx_failed(lambda: c.foo(xs, 255))
+ with tx_failed():
+ c.foo(xs, 255)
@pytest.mark.parametrize(
@@ -793,7 +799,7 @@ def foo(xs: uint256[257], i: uint8) -> uint256:
("bool", [[True, False], [True, False], [True, False]]),
],
)
-def test_constant_nested_list(get_contract, assert_tx_failed, type, value):
+def test_constant_nested_list(get_contract, tx_failed, type, value):
code = f"""
MY_LIST: constant({type}[{len(value[0])}][{len(value)}]) = {value}
@external
@@ -805,7 +811,8 @@ def ix(i: uint256, j: uint256) -> {type}:
for j, q in enumerate(p):
assert c.ix(i, j) == q
# assert oob
- assert_tx_failed(lambda: c.ix(len(value) + 1, len(value[0]) + 1))
+ with tx_failed():
+ c.ix(len(value) + 1, len(value[0]) + 1)
@pytest.mark.parametrize("storage_type,return_type", itertools.permutations(integer_types, 2))
@@ -817,7 +824,7 @@ def test_constant_nested_list_fail(get_contract, assert_compile_failed, storage_
def foo() -> {return_type}[2][3]:
return MY_CONSTANT
"""
- assert_compile_failed(lambda: get_contract(code), InvalidType)
+ assert_compile_failed(lambda: get_contract(code), TypeMismatch)
@pytest.mark.parametrize("storage_type,return_type", itertools.permutations(integer_types, 2))
@@ -831,4 +838,4 @@ def test_constant_nested_list_fail_2(
def foo() -> {return_type}:
return MY_CONSTANT[0][0]
"""
- assert_compile_failed(lambda: get_contract(code), InvalidType)
+ assert_compile_failed(lambda: get_contract(code), TypeMismatch)
diff --git a/tests/parser/types/test_node_types.py b/tests/functional/codegen/types/test_node_types.py
similarity index 98%
rename from tests/parser/types/test_node_types.py
rename to tests/functional/codegen/types/test_node_types.py
index b6561ae8eb..8a2b1681d7 100644
--- a/tests/parser/types/test_node_types.py
+++ b/tests/functional/codegen/types/test_node_types.py
@@ -63,5 +63,5 @@ def test_type_storage_sizes():
assert struct_.storage_size_in_words == 2
# Don't allow unknown types.
- with raises(Exception):
+ with raises(AttributeError):
_ = int.storage_size_in_words
diff --git a/tests/parser/types/test_string.py b/tests/functional/codegen/types/test_string.py
similarity index 96%
rename from tests/parser/types/test_string.py
rename to tests/functional/codegen/types/test_string.py
index a5eef66dae..9d50f8df38 100644
--- a/tests/parser/types/test_string.py
+++ b/tests/functional/codegen/types/test_string.py
@@ -61,7 +61,7 @@ def get(k: String[34]) -> int128:
assert c.get("a" * 34) == 6789
-def test_string_slice(get_contract_with_gas_estimation, assert_tx_failed):
+def test_string_slice(get_contract_with_gas_estimation, tx_failed):
test_slice4 = """
@external
def foo(inp: String[10], start: uint256, _len: uint256) -> String[10]:
@@ -76,10 +76,14 @@ def foo(inp: String[10], start: uint256, _len: uint256) -> String[10]:
assert c.foo("badminton", 1, 0) == ""
assert c.foo("badminton", 9, 0) == ""
- assert_tx_failed(lambda: c.foo("badminton", 0, 10))
- assert_tx_failed(lambda: c.foo("badminton", 1, 9))
- assert_tx_failed(lambda: c.foo("badminton", 9, 1))
- assert_tx_failed(lambda: c.foo("badminton", 10, 0))
+ with tx_failed():
+ c.foo("badminton", 0, 10)
+ with tx_failed():
+ c.foo("badminton", 1, 9)
+ with tx_failed():
+ c.foo("badminton", 9, 1)
+ with tx_failed():
+ c.foo("badminton", 10, 0)
def test_private_string(get_contract_with_gas_estimation):
@@ -139,7 +143,7 @@ def out_literals() -> (int128, address, String[10]) : view
@external
def test(addr: address) -> (int128, address, String[10]):
a: int128 = 0
- b: address = ZERO_ADDRESS
+ b: address = empty(address)
c: String[10] = ""
(a, b, c) = Test(addr).out_literals()
return a, b,c
diff --git a/tests/parser/types/test_string_literal.py b/tests/functional/codegen/types/test_string_literal.py
similarity index 100%
rename from tests/parser/types/test_string_literal.py
rename to tests/functional/codegen/types/test_string_literal.py
diff --git a/tests/examples/auctions/test_blind_auction.py b/tests/functional/examples/auctions/test_blind_auction.py
similarity index 93%
rename from tests/examples/auctions/test_blind_auction.py
rename to tests/functional/examples/auctions/test_blind_auction.py
index d814ab0cad..dcd4e0bf8b 100644
--- a/tests/examples/auctions/test_blind_auction.py
+++ b/tests/functional/examples/auctions/test_blind_auction.py
@@ -33,15 +33,15 @@ def test_initial_state(w3, tester, auction_contract):
assert auction_contract.highestBidder() is None
-def test_late_bid(w3, auction_contract, assert_tx_failed):
+def test_late_bid(w3, auction_contract, tx_failed):
k1 = w3.eth.accounts[1]
# Move time forward past bidding end
w3.testing.mine(BIDDING_TIME + TEST_INCREMENT)
# Try to bid after bidding has ended
- assert_tx_failed(
- lambda: auction_contract.bid(
+ with tx_failed():
+ auction_contract.bid(
w3.keccak(
b"".join(
[
@@ -53,10 +53,9 @@ def test_late_bid(w3, auction_contract, assert_tx_failed):
),
transact={"value": 200, "from": k1},
)
- )
-def test_too_many_bids(w3, auction_contract, assert_tx_failed):
+def test_too_many_bids(w3, auction_contract, tx_failed):
k1 = w3.eth.accounts[1]
# First 128 bids should be able to be placed successfully
@@ -75,8 +74,8 @@ def test_too_many_bids(w3, auction_contract, assert_tx_failed):
)
# 129th bid should fail
- assert_tx_failed(
- lambda: auction_contract.bid(
+ with tx_failed():
+ auction_contract.bid(
w3.keccak(
b"".join(
[
@@ -88,10 +87,9 @@ def test_too_many_bids(w3, auction_contract, assert_tx_failed):
),
transact={"value": 128, "from": k1},
)
- )
-def test_early_reval(w3, auction_contract, assert_tx_failed):
+def test_early_reval(w3, auction_contract, tx_failed):
k1 = w3.eth.accounts[1]
# k1 places 1 real bid
@@ -119,11 +117,10 @@ def test_early_reval(w3, auction_contract, assert_tx_failed):
_values[0] = 100
_fakes[0] = False
_secrets[0] = (8675309).to_bytes(32, byteorder="big")
- assert_tx_failed(
- lambda: auction_contract.reveal(
+ with tx_failed():
+ auction_contract.reveal(
_numBids, _values, _fakes, _secrets, transact={"value": 0, "from": k1}
)
- )
# Check highest bidder is still empty
assert auction_contract.highestBidder() is None
@@ -131,7 +128,7 @@ def test_early_reval(w3, auction_contract, assert_tx_failed):
assert auction_contract.highestBid() == 0
-def test_late_reveal(w3, auction_contract, assert_tx_failed):
+def test_late_reveal(w3, auction_contract, tx_failed):
k1 = w3.eth.accounts[1]
# k1 places 1 real bid
@@ -159,11 +156,10 @@ def test_late_reveal(w3, auction_contract, assert_tx_failed):
_values[0] = 100
_fakes[0] = False
_secrets[0] = (8675309).to_bytes(32, byteorder="big")
- assert_tx_failed(
- lambda: auction_contract.reveal(
+ with tx_failed():
+ auction_contract.reveal(
_numBids, _values, _fakes, _secrets, transact={"value": 0, "from": k1}
)
- )
# Check highest bidder is still empty
assert auction_contract.highestBidder() is None
@@ -171,14 +167,15 @@ def test_late_reveal(w3, auction_contract, assert_tx_failed):
assert auction_contract.highestBid() == 0
-def test_early_end(w3, auction_contract, assert_tx_failed):
+def test_early_end(w3, auction_contract, tx_failed):
k0 = w3.eth.accounts[0]
# Should not be able to end auction before reveal time has ended
- assert_tx_failed(lambda: auction_contract.auctionEnd(transact={"value": 0, "from": k0}))
+ with tx_failed():
+ auction_contract.auctionEnd(transact={"value": 0, "from": k0})
-def test_double_end(w3, auction_contract, assert_tx_failed):
+def test_double_end(w3, auction_contract, tx_failed):
k0 = w3.eth.accounts[0]
# Move time forward past bidding and reveal end
@@ -188,7 +185,8 @@ def test_double_end(w3, auction_contract, assert_tx_failed):
auction_contract.auctionEnd(transact={"value": 0, "from": k0})
# Should not be able to end auction twice
- assert_tx_failed(lambda: auction_contract.auctionEnd(transact={"value": 0, "from": k0}))
+ with tx_failed():
+ auction_contract.auctionEnd(transact={"value": 0, "from": k0})
def test_blind_auction(w3, auction_contract):
diff --git a/tests/examples/auctions/test_simple_open_auction.py b/tests/functional/examples/auctions/test_simple_open_auction.py
similarity index 87%
rename from tests/examples/auctions/test_simple_open_auction.py
rename to tests/functional/examples/auctions/test_simple_open_auction.py
index cf0bb8cc20..c80b44d976 100644
--- a/tests/examples/auctions/test_simple_open_auction.py
+++ b/tests/functional/examples/auctions/test_simple_open_auction.py
@@ -33,17 +33,19 @@ def test_initial_state(w3, tester, auction_contract, auction_start):
assert auction_contract.auctionEnd() >= tester.get_block_by_number("latest")["timestamp"]
-def test_bid(w3, tester, auction_contract, assert_tx_failed):
+def test_bid(w3, tester, auction_contract, tx_failed):
k1, k2, k3, k4, k5 = w3.eth.accounts[:5]
# Bidder cannot bid 0
- assert_tx_failed(lambda: auction_contract.bid(transact={"value": 0, "from": k1}))
+ with tx_failed():
+ auction_contract.bid(transact={"value": 0, "from": k1})
# Bidder can bid
auction_contract.bid(transact={"value": 1, "from": k1})
# Check that highest bidder and highest bid have changed accordingly
assert auction_contract.highestBidder() == k1
assert auction_contract.highestBid() == 1
# Bidder bid cannot equal current highest bid
- assert_tx_failed(lambda: auction_contract.bid(transact={"value": 1, "from": k1}))
+ with tx_failed():
+ auction_contract.bid(transact={"value": 1, "from": k1})
# Higher bid can replace current highest bid
auction_contract.bid(transact={"value": 2, "from": k2})
# Check that highest bidder and highest bid have changed accordingly
@@ -72,10 +74,11 @@ def test_bid(w3, tester, auction_contract, assert_tx_failed):
assert auction_contract.pendingReturns(k1) == 0
-def test_end_auction(w3, tester, auction_contract, assert_tx_failed):
+def test_end_auction(w3, tester, auction_contract, tx_failed):
k1, k2, k3, k4, k5 = w3.eth.accounts[:5]
# Fails if auction end time has not been reached
- assert_tx_failed(lambda: auction_contract.endAuction())
+ with tx_failed():
+ auction_contract.endAuction()
auction_contract.bid(transact={"value": 1 * 10**10, "from": k2})
# Move block timestamp foreward to reach auction end time
# tester.time_travel(tester.get_block_by_number('latest')['timestamp'] + EXPIRY)
@@ -86,6 +89,8 @@ def test_end_auction(w3, tester, auction_contract, assert_tx_failed):
# Beneficiary receives the highest bid
assert balance_after_end == balance_before_end + 1 * 10**10
# Bidder cannot bid after auction end time has been reached
- assert_tx_failed(lambda: auction_contract.bid(transact={"value": 10, "from": k1}))
+ with tx_failed():
+ auction_contract.bid(transact={"value": 10, "from": k1})
# Auction cannot be ended twice
- assert_tx_failed(lambda: auction_contract.endAuction())
+ with tx_failed():
+ auction_contract.endAuction()
diff --git a/tests/examples/company/test_company.py b/tests/functional/examples/company/test_company.py
similarity index 73%
rename from tests/examples/company/test_company.py
rename to tests/functional/examples/company/test_company.py
index 71141b8bb5..5933a14e86 100644
--- a/tests/examples/company/test_company.py
+++ b/tests/functional/examples/company/test_company.py
@@ -9,7 +9,7 @@ def c(w3, get_contract):
return contract
-def test_overbuy(w3, c, assert_tx_failed):
+def test_overbuy(w3, c, tx_failed):
# If all the stock has been bought, no one can buy more
a1, a2 = w3.eth.accounts[1:3]
test_shares = int(c.totalShares() / 2)
@@ -19,15 +19,19 @@ def test_overbuy(w3, c, assert_tx_failed):
assert c.stockAvailable() == 0
assert c.getHolding(a1) == (test_shares * 2)
one_stock = c.price()
- assert_tx_failed(lambda: c.buyStock(transact={"from": a1, "value": one_stock}))
- assert_tx_failed(lambda: c.buyStock(transact={"from": a2, "value": one_stock}))
+ with tx_failed():
+ c.buyStock(transact={"from": a1, "value": one_stock})
+ with tx_failed():
+ c.buyStock(transact={"from": a2, "value": one_stock})
-def test_sell_without_stock(w3, c, assert_tx_failed):
+def test_sell_without_stock(w3, c, tx_failed):
a1, a2 = w3.eth.accounts[1:3]
# If you don't have any stock, you can't sell
- assert_tx_failed(lambda: c.sellStock(1, transact={"from": a1}))
- assert_tx_failed(lambda: c.sellStock(1, transact={"from": a2}))
+ with tx_failed():
+ c.sellStock(1, transact={"from": a1})
+ with tx_failed():
+ c.sellStock(1, transact={"from": a2})
# But if you do, you can!
test_shares = int(c.totalShares())
test_value = int(test_shares * c.price())
@@ -35,48 +39,57 @@ def test_sell_without_stock(w3, c, assert_tx_failed):
assert c.getHolding(a1) == test_shares
c.sellStock(test_shares, transact={"from": a1})
# But only until you run out
- assert_tx_failed(lambda: c.sellStock(1, transact={"from": a1}))
+ with tx_failed():
+ c.sellStock(1, transact={"from": a1})
-def test_oversell(w3, c, assert_tx_failed):
+def test_oversell(w3, c, tx_failed):
a0, a1, a2 = w3.eth.accounts[:3]
# You can't sell more than you own
test_shares = int(c.totalShares())
test_value = int(test_shares * c.price())
c.buyStock(transact={"from": a1, "value": test_value})
- assert_tx_failed(lambda: c.sellStock(test_shares + 1, transact={"from": a1}))
+ with tx_failed():
+ c.sellStock(test_shares + 1, transact={"from": a1})
-def test_transfer(w3, c, assert_tx_failed):
+def test_transfer(w3, c, tx_failed):
# If you don't have any stock, you can't transfer
a1, a2 = w3.eth.accounts[1:3]
- assert_tx_failed(lambda: c.transferStock(a2, 1, transact={"from": a1}))
- assert_tx_failed(lambda: c.transferStock(a1, 1, transact={"from": a2}))
+ with tx_failed():
+ c.transferStock(a2, 1, transact={"from": a1})
+ with tx_failed():
+ c.transferStock(a1, 1, transact={"from": a2})
# If you transfer, you don't have the stock anymore
test_shares = int(c.totalShares())
test_value = int(test_shares * c.price())
c.buyStock(transact={"from": a1, "value": test_value})
assert c.getHolding(a1) == test_shares
c.transferStock(a2, test_shares, transact={"from": a1})
- assert_tx_failed(lambda: c.sellStock(1, transact={"from": a1}))
+ with tx_failed():
+ c.sellStock(1, transact={"from": a1})
# But the other person does
c.sellStock(test_shares, transact={"from": a2})
-def test_paybill(w3, c, assert_tx_failed):
+def test_paybill(w3, c, tx_failed):
a0, a1, a2, a3 = w3.eth.accounts[:4]
# Only the company can authorize payments
- assert_tx_failed(lambda: c.payBill(a2, 1, transact={"from": a1}))
+ with tx_failed():
+ c.payBill(a2, 1, transact={"from": a1})
# A company can only pay someone if it has the money
- assert_tx_failed(lambda: c.payBill(a2, 1, transact={"from": a0}))
+ with tx_failed():
+ c.payBill(a2, 1, transact={"from": a0})
# If it has the money, it can pay someone
test_value = int(c.totalShares() * c.price())
c.buyStock(transact={"from": a1, "value": test_value})
c.payBill(a2, test_value, transact={"from": a0})
# Until it runs out of money
- assert_tx_failed(lambda: c.payBill(a3, 1, transact={"from": a0}))
+ with tx_failed():
+ c.payBill(a3, 1, transact={"from": a0})
# Then no stockholders can sell their stock either
- assert_tx_failed(lambda: c.sellStock(1, transact={"from": a1}))
+ with tx_failed():
+ c.sellStock(1, transact={"from": a1})
def test_valuation(w3, c):
diff --git a/tests/examples/conftest.py b/tests/functional/examples/conftest.py
similarity index 100%
rename from tests/examples/conftest.py
rename to tests/functional/examples/conftest.py
diff --git a/tests/examples/crowdfund/test_crowdfund_example.py b/tests/functional/examples/crowdfund/test_crowdfund_example.py
similarity index 90%
rename from tests/examples/crowdfund/test_crowdfund_example.py
rename to tests/functional/examples/crowdfund/test_crowdfund_example.py
index 9a08d9241c..e75a88bf48 100644
--- a/tests/examples/crowdfund/test_crowdfund_example.py
+++ b/tests/functional/examples/crowdfund/test_crowdfund_example.py
@@ -27,7 +27,7 @@ def test_crowdfund_example(c, w3):
assert post_bal - pre_bal == 54
-def test_crowdfund_example2(c, w3, assert_tx_failed):
+def test_crowdfund_example2(c, w3, tx_failed):
a0, a1, a2, a3, a4, a5, a6 = w3.eth.accounts[:7]
c.participate(transact={"value": 1, "from": a3})
c.participate(transact={"value": 2, "from": a4})
@@ -39,9 +39,11 @@ def test_crowdfund_example2(c, w3, assert_tx_failed):
# assert c.expired()
# assert not c.reached()
pre_bals = [w3.eth.get_balance(x) for x in [a3, a4, a5, a6]]
- assert_tx_failed(lambda: c.refund(transact={"from": a0}))
+ with tx_failed():
+ c.refund(transact={"from": a0})
c.refund(transact={"from": a3})
- assert_tx_failed(lambda: c.refund(transact={"from": a3}))
+ with tx_failed():
+ c.refund(transact={"from": a3})
c.refund(transact={"from": a4})
c.refund(transact={"from": a5})
c.refund(transact={"from": a6})
diff --git a/tests/examples/factory/test_factory.py b/tests/functional/examples/factory/test_factory.py
similarity index 100%
rename from tests/examples/factory/test_factory.py
rename to tests/functional/examples/factory/test_factory.py
diff --git a/tests/examples/market_maker/test_on_chain_market_maker.py b/tests/functional/examples/market_maker/test_on_chain_market_maker.py
similarity index 84%
rename from tests/examples/market_maker/test_on_chain_market_maker.py
rename to tests/functional/examples/market_maker/test_on_chain_market_maker.py
index db9700da3b..235a0ea66f 100644
--- a/tests/examples/market_maker/test_on_chain_market_maker.py
+++ b/tests/functional/examples/market_maker/test_on_chain_market_maker.py
@@ -31,25 +31,21 @@ def test_initial_state(market_maker):
assert market_maker.owner() is None
-def test_initiate(w3, market_maker, erc20, assert_tx_failed):
+def test_initiate(w3, market_maker, erc20, tx_failed):
a0 = w3.eth.accounts[0]
- erc20.approve(market_maker.address, w3.to_wei(2, "ether"), transact={})
- market_maker.initiate(
- erc20.address, w3.to_wei(1, "ether"), transact={"value": w3.to_wei(2, "ether")}
- )
- assert market_maker.totalEthQty() == w3.to_wei(2, "ether")
- assert market_maker.totalTokenQty() == w3.to_wei(1, "ether")
+ ether, ethers = w3.to_wei(1, "ether"), w3.to_wei(2, "ether")
+ erc20.approve(market_maker.address, ethers, transact={})
+ market_maker.initiate(erc20.address, ether, transact={"value": ethers})
+ assert market_maker.totalEthQty() == ethers
+ assert market_maker.totalTokenQty() == ether
assert market_maker.invariant() == 2 * 10**36
assert market_maker.owner() == a0
assert erc20.name() == TOKEN_NAME
assert erc20.decimals() == TOKEN_DECIMALS
# Initiate cannot be called twice
- assert_tx_failed(
- lambda: market_maker.initiate(
- erc20.address, w3.to_wei(1, "ether"), transact={"value": w3.to_wei(2, "ether")}
- )
- ) # noqa: E501
+ with tx_failed():
+ market_maker.initiate(erc20.address, ether, transact={"value": ethers})
def test_eth_to_tokens(w3, market_maker, erc20):
@@ -95,7 +91,7 @@ def test_tokens_to_eth(w3, market_maker, erc20):
assert market_maker.totalEthQty() == w3.to_wei(1, "ether")
-def test_owner_withdraw(w3, market_maker, erc20, assert_tx_failed):
+def test_owner_withdraw(w3, market_maker, erc20, tx_failed):
a0, a1 = w3.eth.accounts[:2]
a0_balance_before = w3.eth.get_balance(a0)
# Approve 2 eth transfers.
@@ -110,7 +106,8 @@ def test_owner_withdraw(w3, market_maker, erc20, assert_tx_failed):
assert erc20.balanceOf(a0) == TOKEN_TOTAL_SUPPLY - w3.to_wei(1, "ether")
# Only owner can call ownerWithdraw
- assert_tx_failed(lambda: market_maker.ownerWithdraw(transact={"from": a1}))
+ with tx_failed():
+ market_maker.ownerWithdraw(transact={"from": a1})
market_maker.ownerWithdraw(transact={})
assert w3.eth.get_balance(a0) == a0_balance_before # Eth balance restored.
assert erc20.balanceOf(a0) == TOKEN_TOTAL_SUPPLY # Tokens returned to a0.
diff --git a/tests/examples/name_registry/test_name_registry.py b/tests/functional/examples/name_registry/test_name_registry.py
similarity index 66%
rename from tests/examples/name_registry/test_name_registry.py
rename to tests/functional/examples/name_registry/test_name_registry.py
index 26f5844484..a2e92a7c52 100644
--- a/tests/examples/name_registry/test_name_registry.py
+++ b/tests/functional/examples/name_registry/test_name_registry.py
@@ -1,8 +1,9 @@
-def test_name_registry(w3, get_contract, assert_tx_failed):
+def test_name_registry(w3, get_contract, tx_failed):
a0, a1 = w3.eth.accounts[:2]
with open("examples/name_registry/name_registry.vy") as f:
code = f.read()
c = get_contract(code)
c.register(b"jacques", a0, transact={})
assert c.lookup(b"jacques") == a0
- assert_tx_failed(lambda: c.register(b"jacques", a1))
+ with tx_failed():
+ c.register(b"jacques", a1)
diff --git a/tests/examples/safe_remote_purchase/test_safe_remote_purchase.py b/tests/functional/examples/safe_remote_purchase/test_safe_remote_purchase.py
similarity index 82%
rename from tests/examples/safe_remote_purchase/test_safe_remote_purchase.py
rename to tests/functional/examples/safe_remote_purchase/test_safe_remote_purchase.py
index 9a806ed885..2cc5dd8d4a 100644
--- a/tests/examples/safe_remote_purchase/test_safe_remote_purchase.py
+++ b/tests/functional/examples/safe_remote_purchase/test_safe_remote_purchase.py
@@ -31,9 +31,10 @@ def get_balance():
return get_balance
-def test_initial_state(w3, assert_tx_failed, get_contract, get_balance, contract_code):
+def test_initial_state(w3, tx_failed, get_contract, get_balance, contract_code):
# Inital deposit has to be divisible by two
- assert_tx_failed(lambda: get_contract(contract_code, value=13))
+ with tx_failed():
+ get_contract(contract_code, value=13)
# Seller puts item up for sale
a0_pre_bal, a1_pre_bal = get_balance()
c = get_contract(contract_code, value_in_eth=2)
@@ -47,30 +48,34 @@ def test_initial_state(w3, assert_tx_failed, get_contract, get_balance, contract
assert get_balance() == ((a0_pre_bal - w3.to_wei(2, "ether")), a1_pre_bal)
-def test_abort(w3, assert_tx_failed, get_balance, get_contract, contract_code):
+def test_abort(w3, tx_failed, get_balance, get_contract, contract_code):
a0, a1, a2 = w3.eth.accounts[:3]
a0_pre_bal, a1_pre_bal = get_balance()
c = get_contract(contract_code, value=w3.to_wei(2, "ether"))
assert c.value() == w3.to_wei(1, "ether")
# Only sender can trigger refund
- assert_tx_failed(lambda: c.abort(transact={"from": a2}))
+ with tx_failed():
+ c.abort(transact={"from": a2})
# Refund works correctly
c.abort(transact={"from": a0})
assert get_balance() == (a0_pre_bal, a1_pre_bal)
# Purchase in process, no refund possible
c = get_contract(contract_code, value=2)
c.purchase(transact={"value": 2, "from": a1})
- assert_tx_failed(lambda: c.abort(transact={"from": a0}))
+ with tx_failed():
+ c.abort(transact={"from": a0})
-def test_purchase(w3, get_contract, assert_tx_failed, get_balance, contract_code):
+def test_purchase(w3, get_contract, tx_failed, get_balance, contract_code):
a0, a1, a2, a3 = w3.eth.accounts[:4]
init_bal_a0, init_bal_a1 = get_balance()
c = get_contract(contract_code, value=2)
# Purchase for too low/high price
- assert_tx_failed(lambda: c.purchase(transact={"value": 1, "from": a1}))
- assert_tx_failed(lambda: c.purchase(transact={"value": 3, "from": a1}))
+ with tx_failed():
+ c.purchase(transact={"value": 1, "from": a1})
+ with tx_failed():
+ c.purchase(transact={"value": 3, "from": a1})
# Purchase for the correct price
c.purchase(transact={"value": 2, "from": a1})
# Check if buyer is set correctly
@@ -80,26 +85,29 @@ def test_purchase(w3, get_contract, assert_tx_failed, get_balance, contract_code
# Check balances, both deposits should have been deducted
assert get_balance() == (init_bal_a0 - 2, init_bal_a1 - 2)
# Allow nobody else to purchase
- assert_tx_failed(lambda: c.purchase(transact={"value": 2, "from": a3}))
+ with tx_failed():
+ c.purchase(transact={"value": 2, "from": a3})
-def test_received(w3, get_contract, assert_tx_failed, get_balance, contract_code):
+def test_received(w3, get_contract, tx_failed, get_balance, contract_code):
a0, a1 = w3.eth.accounts[:2]
init_bal_a0, init_bal_a1 = get_balance()
c = get_contract(contract_code, value=2)
# Can only be called after purchase
- assert_tx_failed(lambda: c.received(transact={"from": a1}))
+ with tx_failed():
+ c.received(transact={"from": a1})
# Purchase completed
c.purchase(transact={"value": 2, "from": a1})
# Check that e.g. sender cannot trigger received
- assert_tx_failed(lambda: c.received(transact={"from": a0}))
+ with tx_failed():
+ c.received(transact={"from": a0})
# Check if buyer can call receive
c.received(transact={"from": a1})
# Final check if everything worked. 1 value has been transferred
assert get_balance() == (init_bal_a0 + 1, init_bal_a1 - 1)
-def test_received_reentrancy(w3, get_contract, assert_tx_failed, get_balance, contract_code):
+def test_received_reentrancy(w3, get_contract, tx_failed, get_balance, contract_code):
buyer_contract_code = """
interface PurchaseContract:
diff --git a/tests/examples/storage/test_advanced_storage.py b/tests/functional/examples/storage/test_advanced_storage.py
similarity index 74%
rename from tests/examples/storage/test_advanced_storage.py
rename to tests/functional/examples/storage/test_advanced_storage.py
index 13ffce4f82..313d1a7e5c 100644
--- a/tests/examples/storage/test_advanced_storage.py
+++ b/tests/functional/examples/storage/test_advanced_storage.py
@@ -18,32 +18,30 @@ def test_initial_state(adv_storage_contract):
assert adv_storage_contract.storedData() == INITIAL_VALUE
-def test_failed_transactions(w3, adv_storage_contract, assert_tx_failed):
+def test_failed_transactions(w3, adv_storage_contract, tx_failed):
k1 = w3.eth.accounts[1]
# Try to set the storage to a negative amount
- assert_tx_failed(lambda: adv_storage_contract.set(-10, transact={"from": k1}))
+ with tx_failed():
+ adv_storage_contract.set(-10, transact={"from": k1})
# Lock the contract by storing more than 100. Then try to change the value
adv_storage_contract.set(150, transact={"from": k1})
- assert_tx_failed(lambda: adv_storage_contract.set(10, transact={"from": k1}))
+ with tx_failed():
+ adv_storage_contract.set(10, transact={"from": k1})
# Reset the contract and try to change the value
adv_storage_contract.reset(transact={"from": k1})
adv_storage_contract.set(10, transact={"from": k1})
assert adv_storage_contract.storedData() == 10
- # Assert a different exception (ValidationError for non matching argument type)
- assert_tx_failed(
- lambda: adv_storage_contract.set("foo", transact={"from": k1}), ValidationError
- )
+ # Assert a different exception (ValidationError for non-matching argument type)
+ with tx_failed(ValidationError):
+ adv_storage_contract.set("foo", transact={"from": k1})
# Assert a different exception that contains specific text
- assert_tx_failed(
- lambda: adv_storage_contract.set(1, 2, transact={"from": k1}),
- ValidationError,
- "invocation failed due to improper number of arguments",
- )
+ with tx_failed(ValidationError, "invocation failed due to improper number of arguments"):
+ adv_storage_contract.set(1, 2, transact={"from": k1})
def test_events(w3, adv_storage_contract, get_logs):
diff --git a/tests/examples/storage/test_storage.py b/tests/functional/examples/storage/test_storage.py
similarity index 100%
rename from tests/examples/storage/test_storage.py
rename to tests/functional/examples/storage/test_storage.py
diff --git a/tests/examples/tokens/test_erc1155.py b/tests/functional/examples/tokens/test_erc1155.py
similarity index 63%
rename from tests/examples/tokens/test_erc1155.py
rename to tests/functional/examples/tokens/test_erc1155.py
index abebd024b6..5dc314c037 100644
--- a/tests/examples/tokens/test_erc1155.py
+++ b/tests/functional/examples/tokens/test_erc1155.py
@@ -29,7 +29,7 @@
@pytest.fixture
-def erc1155(get_contract, w3, assert_tx_failed):
+def erc1155(get_contract, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
with open("examples/tokens/ERC1155ownable.vy") as f:
code = f.read()
@@ -41,18 +41,20 @@ def erc1155(get_contract, w3, assert_tx_failed):
assert c.balanceOf(a1, 1) == 1
assert c.balanceOf(a1, 2) == 1
assert c.balanceOf(a1, 3) == 1
- assert_tx_failed(
- lambda: c.mintBatch(ZERO_ADDRESS, mintBatch, minBatchSetOf10, transact={"from": owner})
- )
- assert_tx_failed(lambda: c.mintBatch(a1, [1, 2, 3], [1, 1], transact={"from": owner}))
+ with tx_failed():
+ c.mintBatch(ZERO_ADDRESS, mintBatch, minBatchSetOf10, transact={"from": owner})
+ with tx_failed():
+ c.mintBatch(a1, [1, 2, 3], [1, 1], transact={"from": owner})
c.mint(a1, 21, 1, transact={"from": owner})
c.mint(a1, 22, 1, transact={"from": owner})
c.mint(a1, 23, 1, transact={"from": owner})
c.mint(a1, 24, 1, transact={"from": owner})
- assert_tx_failed(lambda: c.mint(a1, 24, 1, transact={"from": a3}))
- assert_tx_failed(lambda: c.mint(ZERO_ADDRESS, 24, 1, transact={"from": owner}))
+ with tx_failed():
+ c.mint(a1, 24, 1, transact={"from": a3})
+ with tx_failed():
+ c.mint(ZERO_ADDRESS, 24, 1, transact={"from": owner})
assert c.balanceOf(a1, 21) == 1
assert c.balanceOf(a1, 22) == 1
@@ -80,69 +82,76 @@ def test_initial_state(erc1155):
assert erc1155.supportsInterface(ERC1155_INTERFACE_ID_METADATA)
-def test_pause(erc1155, w3, assert_tx_failed):
+def test_pause(erc1155, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
# check the pause status, pause, check, unpause, check, with owner and non-owner w3.eth.accounts
# this test will check all the function that should not work when paused.
assert not erc1155.paused()
# try to pause the contract from a non owner account
- assert_tx_failed(lambda: erc1155.pause(transact={"from": a1}))
+ with tx_failed():
+ erc1155.pause(transact={"from": a1})
# now pause the contract and check status
erc1155.pause(transact={"from": owner})
assert erc1155.paused()
# try pausing a paused contract
- assert_tx_failed(lambda: erc1155.pause())
+ with tx_failed():
+ erc1155.pause()
# try functions that should not work when paused
- assert_tx_failed(lambda: erc1155.setURI(NEW_CONTRACT_URI))
+ with tx_failed():
+ erc1155.setURI(NEW_CONTRACT_URI)
# test burn and burnbatch
- assert_tx_failed(lambda: erc1155.burn(21, 1))
- assert_tx_failed(lambda: erc1155.burnBatch([21, 22], [1, 1]))
+ with tx_failed():
+ erc1155.burn(21, 1)
+ with tx_failed():
+ erc1155.burnBatch([21, 22], [1, 1])
# check mint and mintbatch
- assert_tx_failed(lambda: erc1155.mint(a1, 21, 1, transact={"from": owner}))
- assert_tx_failed(
- lambda: erc1155.mintBatch(a1, mintBatch, minBatchSetOf10, transact={"from": owner})
- )
+ with tx_failed():
+ erc1155.mint(a1, 21, 1, transact={"from": owner})
+ with tx_failed():
+ erc1155.mintBatch(a1, mintBatch, minBatchSetOf10, transact={"from": owner})
# check safetransferfrom and safebatchtransferfrom
- assert_tx_failed(
- lambda: erc1155.safeTransferFrom(a1, a2, 21, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
- )
- assert_tx_failed(
- lambda: erc1155.safeBatchTransferFrom(
+ with tx_failed():
+ erc1155.safeTransferFrom(a1, a2, 21, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
+ with tx_failed():
+ erc1155.safeBatchTransferFrom(
a1, a2, [21, 22, 23], [1, 1, 1], DUMMY_BYTES32_DATA, transact={"from": a1}
)
- )
# check ownership functions
- assert_tx_failed(lambda: erc1155.transferOwnership(a1))
- assert_tx_failed(lambda: erc1155.renounceOwnership())
+ with tx_failed():
+ erc1155.transferOwnership(a1)
+ with tx_failed():
+ erc1155.renounceOwnership()
# check approval functions
- assert_tx_failed(lambda: erc1155.setApprovalForAll(owner, a5, True))
+ with tx_failed():
+ erc1155.setApprovalForAll(owner, a5, True)
# try and unpause as non-owner
- assert_tx_failed(lambda: erc1155.unpause(transact={"from": a1}))
+ with tx_failed():
+ erc1155.unpause(transact={"from": a1})
erc1155.unpause(transact={"from": owner})
assert not erc1155.paused()
# try un pausing an unpaused contract
- assert_tx_failed(lambda: erc1155.unpause())
+ with tx_failed():
+ erc1155.unpause()
-def test_contractURI(erc1155, w3, assert_tx_failed):
+def test_contractURI(erc1155, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
# change contract URI and restore.
assert erc1155.contractURI() == CONTRACT_METADATA_URI
- assert_tx_failed(
- lambda: erc1155.setContractURI(NEW_CONTRACT_METADATA_URI, transact={"from": a1})
- )
+ with tx_failed():
+ erc1155.setContractURI(NEW_CONTRACT_METADATA_URI, transact={"from": a1})
erc1155.setContractURI(NEW_CONTRACT_METADATA_URI, transact={"from": owner})
assert erc1155.contractURI() == NEW_CONTRACT_METADATA_URI
assert erc1155.contractURI() != CONTRACT_METADATA_URI
@@ -150,10 +159,11 @@ def test_contractURI(erc1155, w3, assert_tx_failed):
assert erc1155.contractURI() != NEW_CONTRACT_METADATA_URI
assert erc1155.contractURI() == CONTRACT_METADATA_URI
- assert_tx_failed(lambda: erc1155.setContractURI(CONTRACT_METADATA_URI))
+ with tx_failed():
+ erc1155.setContractURI(CONTRACT_METADATA_URI)
-def test_URI(erc1155, w3, assert_tx_failed):
+def test_URI(erc1155, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
# change contract URI and restore.
assert erc1155.uri(0) == CONTRACT_URI
@@ -164,7 +174,8 @@ def test_URI(erc1155, w3, assert_tx_failed):
assert erc1155.uri(0) != NEW_CONTRACT_URI
assert erc1155.uri(0) == CONTRACT_URI
- assert_tx_failed(lambda: erc1155.setURI(CONTRACT_URI))
+ with tx_failed():
+ erc1155.setURI(CONTRACT_URI)
# set contract to dynamic URI
erc1155.toggleDynUri(True, transact={"from": owner})
@@ -172,49 +183,41 @@ def test_URI(erc1155, w3, assert_tx_failed):
assert erc1155.uri(0) == CONTRACT_DYNURI + str(0) + ".json"
-def test_safeTransferFrom_balanceOf_single(erc1155, w3, assert_tx_failed):
+def test_safeTransferFrom_balanceOf_single(erc1155, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
assert erc1155.balanceOf(a1, 24) == 1
# transfer by non-owner
- assert_tx_failed(
- lambda: erc1155.safeTransferFrom(a1, a2, 24, 1, DUMMY_BYTES32_DATA, transact={"from": a2})
- )
+ with tx_failed():
+ erc1155.safeTransferFrom(a1, a2, 24, 1, DUMMY_BYTES32_DATA, transact={"from": a2})
# transfer to zero address
- assert_tx_failed(
- lambda: erc1155.safeTransferFrom(
- a1, ZERO_ADDRESS, 24, 1, DUMMY_BYTES32_DATA, transact={"from": a1}
- )
- )
+ with tx_failed():
+ erc1155.safeTransferFrom(a1, ZERO_ADDRESS, 24, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
# transfer to self
- assert_tx_failed(
- lambda: erc1155.safeTransferFrom(a1, a1, 24, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
- )
+ with tx_failed():
+ erc1155.safeTransferFrom(a1, a1, 24, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
# transfer more than owned
- assert_tx_failed(
- lambda: erc1155.safeTransferFrom(a1, a2, 24, 500, DUMMY_BYTES32_DATA, transact={"from": a1})
- )
+ with tx_failed():
+ erc1155.safeTransferFrom(a1, a2, 24, 500, DUMMY_BYTES32_DATA, transact={"from": a1})
# transfer item not owned / not existing
- assert_tx_failed(
- lambda: erc1155.safeTransferFrom(a1, a2, 500, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
- )
+ with tx_failed():
+ erc1155.safeTransferFrom(a1, a2, 500, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
erc1155.safeTransferFrom(a1, a2, 21, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
assert erc1155.balanceOf(a2, 21) == 1
# try to transfer item again
- assert_tx_failed(
- lambda: erc1155.safeTransferFrom(a1, a2, 21, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
- )
+ with tx_failed():
+ erc1155.safeTransferFrom(a1, a2, 21, 1, DUMMY_BYTES32_DATA, transact={"from": a1})
assert erc1155.balanceOf(a1, 21) == 0
# TODO: mint 20 NFTs [1:20] and check the balance for each
-def test_mintBatch_balanceOf(erc1155, w3, assert_tx_failed): # test_mint_batch
+def test_mintBatch_balanceOf(erc1155, w3, tx_failed): # test_mint_batch
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
# Use the mint three fixture to mint the tokens.
# this test checks the balances of this test
@@ -222,7 +225,7 @@ def test_mintBatch_balanceOf(erc1155, w3, assert_tx_failed): # test_mint_batch
assert erc1155.balanceOf(a1, i) == 1
-def test_safeBatchTransferFrom_balanceOf_batch(erc1155, w3, assert_tx_failed): # test_mint_batch
+def test_safeBatchTransferFrom_balanceOf_batch(erc1155, w3, tx_failed): # test_mint_batch
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
# check a1 balances for NFTs 21-24
@@ -231,67 +234,58 @@ def test_safeBatchTransferFrom_balanceOf_batch(erc1155, w3, assert_tx_failed):
assert erc1155.balanceOf(a1, 23) == 1
assert erc1155.balanceOf(a1, 23) == 1
- # try to transfer item from non item owner account
- assert_tx_failed(
- lambda: erc1155.safeBatchTransferFrom(
+ # try to transfer item from non-item owner account
+ with tx_failed():
+ erc1155.safeBatchTransferFrom(
a1, a2, [21, 22, 23], [1, 1, 1], DUMMY_BYTES32_DATA, transact={"from": a2}
)
- )
# try to transfer item to zero address
- assert_tx_failed(
- lambda: erc1155.safeBatchTransferFrom(
+ with tx_failed():
+ erc1155.safeBatchTransferFrom(
a1, ZERO_ADDRESS, [21, 22, 23], [1, 1, 1], DUMMY_BYTES32_DATA, transact={"from": a1}
)
- )
# try to transfer item to self
- assert_tx_failed(
- lambda: erc1155.safeBatchTransferFrom(
+ with tx_failed():
+ erc1155.safeBatchTransferFrom(
a1, a1, [21, 22, 23], [1, 1, 1], DUMMY_BYTES32_DATA, transact={"from": a1}
)
- )
# try to transfer more items than we own
- assert_tx_failed(
- lambda: erc1155.safeBatchTransferFrom(
+ with tx_failed():
+ erc1155.safeBatchTransferFrom(
a1, a2, [21, 22, 23], [1, 125, 1], DUMMY_BYTES32_DATA, transact={"from": a1}
)
- )
# mismatched item and amounts
- assert_tx_failed(
- lambda: erc1155.safeBatchTransferFrom(
+ with tx_failed():
+ erc1155.safeBatchTransferFrom(
a1, a2, [21, 22, 23], [1, 1], DUMMY_BYTES32_DATA, transact={"from": a1}
)
- )
# try to transfer nonexisting item
- assert_tx_failed(
- lambda: erc1155.safeBatchTransferFrom(
+ with tx_failed():
+ erc1155.safeBatchTransferFrom(
a1, a2, [21, 22, 500], [1, 1, 1], DUMMY_BYTES32_DATA, transact={"from": a1}
)
- )
assert erc1155.safeBatchTransferFrom(
a1, a2, [21, 22, 23], [1, 1, 1], DUMMY_BYTES32_DATA, transact={"from": a1}
)
# try to transfer again, our balances are zero now, should fail
- assert_tx_failed(
- lambda: erc1155.safeBatchTransferFrom(
+ with tx_failed():
+ erc1155.safeBatchTransferFrom(
a1, a2, [21, 22, 23], [1, 1, 1], DUMMY_BYTES32_DATA, transact={"from": a1}
)
- )
- assert_tx_failed(
- lambda: erc1155.balanceOfBatch([a2, a2, a2], [21, 22], transact={"from": owner})
- == [1, 1, 1]
- )
+ with tx_failed():
+ erc1155.balanceOfBatch([a2, a2, a2], [21, 22], transact={"from": owner})
assert erc1155.balanceOfBatch([a2, a2, a2], [21, 22, 23]) == [1, 1, 1]
assert erc1155.balanceOf(a1, 21) == 0
-def test_mint_one_burn_one(erc1155, w3, assert_tx_failed):
+def test_mint_one_burn_one(erc1155, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
# check the balance from an owner and non-owner account
@@ -301,20 +295,23 @@ def test_mint_one_burn_one(erc1155, w3, assert_tx_failed):
assert erc1155.balanceOf(owner, 25) == 1
# try and burn an item we don't control
- assert_tx_failed(lambda: erc1155.burn(25, 1, transact={"from": a3}))
+ with tx_failed():
+ erc1155.burn(25, 1, transact={"from": a3})
# burn an item that contains something we don't own
- assert_tx_failed(lambda: erc1155.burn(595, 1, transact={"from": a1}))
+ with tx_failed():
+ erc1155.burn(595, 1, transact={"from": a1})
# burn ah item passing a higher amount than we own
- assert_tx_failed(lambda: erc1155.burn(25, 500, transact={"from": a1}))
+ with tx_failed():
+ erc1155.burn(25, 500, transact={"from": a1})
erc1155.burn(25, 1, transact={"from": owner})
assert erc1155.balanceOf(owner, 25) == 0
-def test_mint_batch_burn_batch(erc1155, w3, assert_tx_failed):
+def test_mint_batch_burn_batch(erc1155, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
# mint NFTs 11-20
@@ -322,16 +319,20 @@ def test_mint_batch_burn_batch(erc1155, w3, assert_tx_failed):
assert erc1155.balanceOfBatch([a3, a3, a3], [11, 12, 13]) == [1, 1, 1]
# try and burn a batch we don't control
- assert_tx_failed(lambda: erc1155.burnBatch([11, 12], [1, 1]))
+ with tx_failed():
+ erc1155.burnBatch([11, 12], [1, 1])
# ids and amounts array length not matching
- assert_tx_failed(lambda: erc1155.burnBatch([1, 2, 3], [1, 1], transact={"from": a1}))
+ with tx_failed():
+ erc1155.burnBatch([1, 2, 3], [1, 1], transact={"from": a1})
# burn a batch that contains something we don't own
- assert_tx_failed(lambda: erc1155.burnBatch([2, 3, 595], [1, 1, 1], transact={"from": a1}))
+ with tx_failed():
+ erc1155.burnBatch([2, 3, 595], [1, 1, 1], transact={"from": a1})
# burn a batch passing a higher amount than we own
- assert_tx_failed(lambda: erc1155.burnBatch([1, 2, 3], [1, 500, 1], transact={"from": a1}))
+ with tx_failed():
+ erc1155.burnBatch([1, 2, 3], [1, 500, 1], transact={"from": a1})
# burn existing
erc1155.burnBatch([11, 12], [1, 1], transact={"from": a3})
@@ -339,18 +340,21 @@ def test_mint_batch_burn_batch(erc1155, w3, assert_tx_failed):
assert erc1155.balanceOfBatch([a3, a3, a3], [11, 12, 13]) == [0, 0, 1]
# burn again, should revert
- assert_tx_failed(lambda: erc1155.burnBatch([11, 12], [1, 1], transact={"from": a3}))
+ with tx_failed():
+ erc1155.burnBatch([11, 12], [1, 1], transact={"from": a3})
- assert lambda: erc1155.balanceOfBatch([a3, a3, a3], [1, 2, 3]) == [0, 0, 1]
+ assert erc1155.balanceOfBatch([a3, a3, a3], [1, 2, 3]) == [0, 0, 0]
-def test_approval_functions(erc1155, w3, assert_tx_failed): # test_mint_batch
+def test_approval_functions(erc1155, w3, tx_failed): # test_mint_batch
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
# self-approval by the owner
- assert_tx_failed(lambda: erc1155.setApprovalForAll(a5, a5, True, transact={"from": a5}))
+ with tx_failed():
+ erc1155.setApprovalForAll(a5, a5, True, transact={"from": a5})
# let's approve and operator for somebody else's account
- assert_tx_failed(lambda: erc1155.setApprovalForAll(owner, a5, True, transact={"from": a3}))
+ with tx_failed():
+ erc1155.setApprovalForAll(owner, a5, True, transact={"from": a3})
# set approval correctly
erc1155.setApprovalForAll(owner, a5, True)
@@ -362,7 +366,7 @@ def test_approval_functions(erc1155, w3, assert_tx_failed): # test_mint_batch
erc1155.setApprovalForAll(owner, a5, False)
-def test_max_batch_size_violation(erc1155, w3, assert_tx_failed):
+def test_max_batch_size_violation(erc1155, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
TOTAL_BAD_BATCH = 200
ids = []
@@ -371,27 +375,29 @@ def test_max_batch_size_violation(erc1155, w3, assert_tx_failed):
ids.append(i)
amounts.append(1)
- assert_tx_failed(lambda: erc1155.mintBatch(a1, ids, amounts, transact={"from": owner}))
+ with tx_failed():
+ erc1155.mintBatch(a1, ids, amounts, transact={"from": owner})
# Transferring back and forth
-def test_ownership_functions(erc1155, w3, assert_tx_failed, tester):
+def test_ownership_functions(erc1155, w3, tx_failed, tester):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
print(owner, a1, a2)
print("___owner___", erc1155.owner())
# change owner from account 0 to account 1 and back
assert erc1155.owner() == owner
- assert_tx_failed(lambda: erc1155.transferOwnership(a1, transact={"from": a2}))
+ with tx_failed():
+ erc1155.transferOwnership(a1, transact={"from": a2})
# try to transfer ownership to current owner
- assert_tx_failed(lambda: erc1155.transferOwnership(owner))
+ with tx_failed():
+ erc1155.transferOwnership(owner)
# try to transfer ownership to ZERO ADDRESS
- assert_tx_failed(
- lambda: erc1155.transferOwnership("0x0000000000000000000000000000000000000000")
- )
+ with tx_failed():
+ erc1155.transferOwnership("0x0000000000000000000000000000000000000000")
# Transfer ownership to account 1
erc1155.transferOwnership(a1, transact={"from": owner})
@@ -399,11 +405,12 @@ def test_ownership_functions(erc1155, w3, assert_tx_failed, tester):
assert erc1155.owner() == a1
-def test_renounce_ownership(erc1155, w3, assert_tx_failed):
+def test_renounce_ownership(erc1155, w3, tx_failed):
owner, a1, a2, a3, a4, a5 = w3.eth.accounts[0:6]
assert erc1155.owner() == owner
# try to transfer ownership from non-owner account
- assert_tx_failed(lambda: erc1155.renounceOwnership(transact={"from": a2}))
+ with tx_failed():
+ erc1155.renounceOwnership(transact={"from": a2})
erc1155.renounceOwnership(transact={"from": owner})
diff --git a/tests/examples/tokens/test_erc20.py b/tests/functional/examples/tokens/test_erc20.py
similarity index 82%
rename from tests/examples/tokens/test_erc20.py
rename to tests/functional/examples/tokens/test_erc20.py
index cba7769bae..ce507f75f8 100644
--- a/tests/examples/tokens/test_erc20.py
+++ b/tests/functional/examples/tokens/test_erc20.py
@@ -61,7 +61,7 @@ def test_initial_state(c, w3):
assert c.allowance(a2, a3) == 0
-def test_mint_and_burn(c, w3, assert_tx_failed):
+def test_mint_and_burn(c, w3, tx_failed):
minter, a1, a2 = w3.eth.accounts[0:3]
# Test scenario were mints 2 to a1, burns twice (check balance consistency)
@@ -70,23 +70,30 @@ def test_mint_and_burn(c, w3, assert_tx_failed):
assert c.balanceOf(a1) == 2
c.burn(2, transact={"from": a1})
assert c.balanceOf(a1) == 0
- assert_tx_failed(lambda: c.burn(2, transact={"from": a1}))
+ with tx_failed():
+ c.burn(2, transact={"from": a1})
assert c.balanceOf(a1) == 0
# Test scenario were mintes 0 to a2, burns (check balance consistency, false burn)
c.mint(a2, 0, transact={"from": minter})
assert c.balanceOf(a2) == 0
- assert_tx_failed(lambda: c.burn(2, transact={"from": a2}))
+ with tx_failed():
+ c.burn(2, transact={"from": a2})
# Check that a1 cannot burn after depleting their balance
- assert_tx_failed(lambda: c.burn(1, transact={"from": a1}))
+ with tx_failed():
+ c.burn(1, transact={"from": a1})
# Check that a1, a2 cannot mint
- assert_tx_failed(lambda: c.mint(a1, 1, transact={"from": a1}))
- assert_tx_failed(lambda: c.mint(a2, 1, transact={"from": a2}))
+ with tx_failed():
+ c.mint(a1, 1, transact={"from": a1})
+ with tx_failed():
+ c.mint(a2, 1, transact={"from": a2})
# Check that mint to ZERO_ADDRESS failed
- assert_tx_failed(lambda: c.mint(ZERO_ADDRESS, 1, transact={"from": a1}))
- assert_tx_failed(lambda: c.mint(ZERO_ADDRESS, 1, transact={"from": minter}))
+ with tx_failed():
+ c.mint(ZERO_ADDRESS, 1, transact={"from": a1})
+ with tx_failed():
+ c.mint(ZERO_ADDRESS, 1, transact={"from": minter})
-def test_totalSupply(c, w3, assert_tx_failed):
+def test_totalSupply(c, w3, tx_failed):
# Test total supply initially, after mint, between two burns, and after failed burn
minter, a1 = w3.eth.accounts[0:2]
assert c.totalSupply() == 0
@@ -96,40 +103,49 @@ def test_totalSupply(c, w3, assert_tx_failed):
assert c.totalSupply() == 1
c.burn(1, transact={"from": a1})
assert c.totalSupply() == 0
- assert_tx_failed(lambda: c.burn(1, transact={"from": a1}))
+ with tx_failed():
+ c.burn(1, transact={"from": a1})
assert c.totalSupply() == 0
# Test that 0-valued mint can't affect supply
c.mint(a1, 0, transact={"from": minter})
assert c.totalSupply() == 0
-def test_transfer(c, w3, assert_tx_failed):
+def test_transfer(c, w3, tx_failed):
minter, a1, a2 = w3.eth.accounts[0:3]
- assert_tx_failed(lambda: c.burn(1, transact={"from": a2}))
+ with tx_failed():
+ c.burn(1, transact={"from": a2})
c.mint(a1, 2, transact={"from": minter})
c.burn(1, transact={"from": a1})
c.transfer(a2, 1, transact={"from": a1})
- assert_tx_failed(lambda: c.burn(1, transact={"from": a1}))
+ with tx_failed():
+ c.burn(1, transact={"from": a1})
c.burn(1, transact={"from": a2})
- assert_tx_failed(lambda: c.burn(1, transact={"from": a2}))
+ with tx_failed():
+ c.burn(1, transact={"from": a2})
# Ensure transfer fails with insufficient balance
- assert_tx_failed(lambda: c.transfer(a1, 1, transact={"from": a2}))
+ with tx_failed():
+ c.transfer(a1, 1, transact={"from": a2})
# Ensure 0-transfer always succeeds
c.transfer(a1, 0, transact={"from": a2})
-def test_maxInts(c, w3, assert_tx_failed):
+def test_maxInts(c, w3, tx_failed):
minter, a1, a2 = w3.eth.accounts[0:3]
c.mint(a1, MAX_UINT256, transact={"from": minter})
assert c.balanceOf(a1) == MAX_UINT256
- assert_tx_failed(lambda: c.mint(a1, 1, transact={"from": a1}))
- assert_tx_failed(lambda: c.mint(a1, MAX_UINT256, transact={"from": a1}))
+ with tx_failed():
+ c.mint(a1, 1, transact={"from": a1})
+ with tx_failed():
+ c.mint(a1, MAX_UINT256, transact={"from": a1})
# Check that totalSupply cannot overflow, even when mint to other account
- assert_tx_failed(lambda: c.mint(a2, 1, transact={"from": minter}))
+ with tx_failed():
+ c.mint(a2, 1, transact={"from": minter})
# Check that corresponding mint is allowed after burn
c.burn(1, transact={"from": a1})
c.mint(a2, 1, transact={"from": minter})
- assert_tx_failed(lambda: c.mint(a2, 1, transact={"from": minter}))
+ with tx_failed():
+ c.mint(a2, 1, transact={"from": minter})
c.transfer(a1, 1, transact={"from": a2})
# Assert that after obtaining max number of tokens, a1 can transfer those but no more
assert c.balanceOf(a1) == MAX_UINT256
@@ -150,21 +166,24 @@ def test_maxInts(c, w3, assert_tx_failed):
assert c.balanceOf(a1) == 0
-def test_transferFrom_and_Allowance(c, w3, assert_tx_failed):
+def test_transferFrom_and_Allowance(c, w3, tx_failed):
minter, a1, a2, a3 = w3.eth.accounts[0:4]
- assert_tx_failed(lambda: c.burn(1, transact={"from": a2}))
+ with tx_failed():
+ c.burn(1, transact={"from": a2})
c.mint(a1, 1, transact={"from": minter})
c.mint(a2, 1, transact={"from": minter})
c.burn(1, transact={"from": a1})
# This should fail; no allowance or balance (0 always succeeds)
- assert_tx_failed(lambda: c.transferFrom(a1, a3, 1, transact={"from": a2}))
+ with tx_failed():
+ c.transferFrom(a1, a3, 1, transact={"from": a2})
c.transferFrom(a1, a3, 0, transact={"from": a2})
# Correct call to approval should update allowance (but not for reverse pair)
c.approve(a2, 1, transact={"from": a1})
assert c.allowance(a1, a2) == 1
assert c.allowance(a2, a1) == 0
# transferFrom should succeed when allowed, fail with wrong sender
- assert_tx_failed(lambda: c.transferFrom(a1, a3, 1, transact={"from": a3}))
+ with tx_failed():
+ c.transferFrom(a1, a3, 1, transact={"from": a3})
assert c.balanceOf(a2) == 1
c.approve(a1, 1, transact={"from": a2})
c.transferFrom(a2, a3, 1, transact={"from": a1})
@@ -173,7 +192,8 @@ def test_transferFrom_and_Allowance(c, w3, assert_tx_failed):
# transferFrom with no funds should fail despite approval
c.approve(a1, 1, transact={"from": a2})
assert c.allowance(a2, a1) == 1
- assert_tx_failed(lambda: c.transferFrom(a2, a3, 1, transact={"from": a1}))
+ with tx_failed():
+ c.transferFrom(a2, a3, 1, transact={"from": a1})
# 0-approve should not change balance or allow transferFrom to change balance
c.mint(a2, 1, transact={"from": minter})
assert c.allowance(a2, a1) == 1
@@ -181,7 +201,8 @@ def test_transferFrom_and_Allowance(c, w3, assert_tx_failed):
assert c.allowance(a2, a1) == 0
c.approve(a1, 0, transact={"from": a2})
assert c.allowance(a2, a1) == 0
- assert_tx_failed(lambda: c.transferFrom(a2, a3, 1, transact={"from": a1}))
+ with tx_failed():
+ c.transferFrom(a2, a3, 1, transact={"from": a1})
# Test that if non-zero approval exists, 0-approval is NOT required to proceed
# a non-conformant implementation is described in countermeasures at
# https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#heading=h.m9fhqynw2xvt
@@ -198,21 +219,24 @@ def test_transferFrom_and_Allowance(c, w3, assert_tx_failed):
assert c.allowance(a2, a1) == 5
-def test_burnFrom_and_Allowance(c, w3, assert_tx_failed):
+def test_burnFrom_and_Allowance(c, w3, tx_failed):
minter, a1, a2, a3 = w3.eth.accounts[0:4]
- assert_tx_failed(lambda: c.burn(1, transact={"from": a2}))
+ with tx_failed():
+ c.burn(1, transact={"from": a2})
c.mint(a1, 1, transact={"from": minter})
c.mint(a2, 1, transact={"from": minter})
c.burn(1, transact={"from": a1})
# This should fail; no allowance or balance (0 always succeeds)
- assert_tx_failed(lambda: c.burnFrom(a1, 1, transact={"from": a2}))
+ with tx_failed():
+ c.burnFrom(a1, 1, transact={"from": a2})
c.burnFrom(a1, 0, transact={"from": a2})
# Correct call to approval should update allowance (but not for reverse pair)
c.approve(a2, 1, transact={"from": a1})
assert c.allowance(a1, a2) == 1
assert c.allowance(a2, a1) == 0
# transferFrom should succeed when allowed, fail with wrong sender
- assert_tx_failed(lambda: c.burnFrom(a2, 1, transact={"from": a3}))
+ with tx_failed():
+ c.burnFrom(a2, 1, transact={"from": a3})
assert c.balanceOf(a2) == 1
c.approve(a1, 1, transact={"from": a2})
c.burnFrom(a2, 1, transact={"from": a1})
@@ -221,7 +245,8 @@ def test_burnFrom_and_Allowance(c, w3, assert_tx_failed):
# transferFrom with no funds should fail despite approval
c.approve(a1, 1, transact={"from": a2})
assert c.allowance(a2, a1) == 1
- assert_tx_failed(lambda: c.burnFrom(a2, 1, transact={"from": a1}))
+ with tx_failed():
+ c.burnFrom(a2, 1, transact={"from": a1})
# 0-approve should not change balance or allow transferFrom to change balance
c.mint(a2, 1, transact={"from": minter})
assert c.allowance(a2, a1) == 1
@@ -229,7 +254,8 @@ def test_burnFrom_and_Allowance(c, w3, assert_tx_failed):
assert c.allowance(a2, a1) == 0
c.approve(a1, 0, transact={"from": a2})
assert c.allowance(a2, a1) == 0
- assert_tx_failed(lambda: c.burnFrom(a2, 1, transact={"from": a1}))
+ with tx_failed():
+ c.burnFrom(a2, 1, transact={"from": a1})
# Test that if non-zero approval exists, 0-approval is NOT required to proceed
# a non-conformant implementation is described in countermeasures at
# https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#heading=h.m9fhqynw2xvt
@@ -245,7 +271,8 @@ def test_burnFrom_and_Allowance(c, w3, assert_tx_failed):
c.approve(a1, 5, transact={"from": a2})
assert c.allowance(a2, a1) == 5
# Check that burnFrom to ZERO_ADDRESS failed
- assert_tx_failed(lambda: c.burnFrom(ZERO_ADDRESS, 0, transact={"from": a1}))
+ with tx_failed():
+ c.burnFrom(ZERO_ADDRESS, 0, transact={"from": a1})
def test_raw_logs(c, w3, get_log_args):
@@ -307,33 +334,36 @@ def test_raw_logs(c, w3, get_log_args):
assert args.value == 0
-def test_bad_transfer(c_bad, w3, assert_tx_failed):
+def test_bad_transfer(c_bad, w3, tx_failed):
# Ensure transfer fails if it would otherwise overflow balance when totalSupply is corrupted
minter, a1, a2 = w3.eth.accounts[0:3]
c_bad.mint(a1, MAX_UINT256, transact={"from": minter})
c_bad.mint(a2, 1, transact={"from": minter})
- assert_tx_failed(lambda: c_bad.transfer(a1, 1, transact={"from": a2}))
+ with tx_failed():
+ c_bad.transfer(a1, 1, transact={"from": a2})
c_bad.transfer(a2, MAX_UINT256 - 1, transact={"from": a1})
assert c_bad.balanceOf(a1) == 1
assert c_bad.balanceOf(a2) == MAX_UINT256
-def test_bad_burn(c_bad, w3, assert_tx_failed):
+def test_bad_burn(c_bad, w3, tx_failed):
# Ensure burn fails if it would otherwise underflow balance when totalSupply is corrupted
minter, a1 = w3.eth.accounts[0:2]
assert c_bad.balanceOf(a1) == 0
c_bad.mint(a1, 2, transact={"from": minter})
assert c_bad.balanceOf(a1) == 2
- assert_tx_failed(lambda: c_bad.burn(3, transact={"from": a1}))
+ with tx_failed():
+ c_bad.burn(3, transact={"from": a1})
-def test_bad_transferFrom(c_bad, w3, assert_tx_failed):
+def test_bad_transferFrom(c_bad, w3, tx_failed):
# Ensure transferFrom fails if it would otherwise overflow balance when totalSupply is corrupted
minter, a1, a2 = w3.eth.accounts[0:3]
c_bad.mint(a1, MAX_UINT256, transact={"from": minter})
c_bad.mint(a2, 1, transact={"from": minter})
c_bad.approve(a1, 1, transact={"from": a2})
- assert_tx_failed(lambda: c_bad.transferFrom(a2, a1, 1, transact={"from": a1}))
+ with tx_failed():
+ c_bad.transferFrom(a2, a1, 1, transact={"from": a1})
c_bad.approve(a2, MAX_UINT256 - 1, transact={"from": a1})
assert c_bad.allowance(a1, a2) == MAX_UINT256 - 1
c_bad.transferFrom(a1, a2, MAX_UINT256 - 1, transact={"from": a2})
diff --git a/tests/examples/tokens/test_erc4626.py b/tests/functional/examples/tokens/test_erc4626.py
similarity index 100%
rename from tests/examples/tokens/test_erc4626.py
rename to tests/functional/examples/tokens/test_erc4626.py
diff --git a/tests/examples/tokens/test_erc721.py b/tests/functional/examples/tokens/test_erc721.py
similarity index 76%
rename from tests/examples/tokens/test_erc721.py
rename to tests/functional/examples/tokens/test_erc721.py
index ab3c6368c5..c881149baa 100644
--- a/tests/examples/tokens/test_erc721.py
+++ b/tests/functional/examples/tokens/test_erc721.py
@@ -40,16 +40,18 @@ def test_erc165(w3, c):
assert c.supportsInterface(ERC721_SIG)
-def test_balanceOf(c, w3, assert_tx_failed):
+def test_balanceOf(c, w3, tx_failed):
someone = w3.eth.accounts[1]
assert c.balanceOf(someone) == 3
- assert_tx_failed(lambda: c.balanceOf(ZERO_ADDRESS))
+ with tx_failed():
+ c.balanceOf(ZERO_ADDRESS)
-def test_ownerOf(c, w3, assert_tx_failed):
+def test_ownerOf(c, w3, tx_failed):
someone = w3.eth.accounts[1]
assert c.ownerOf(SOMEONE_TOKEN_IDS[0]) == someone
- assert_tx_failed(lambda: c.ownerOf(INVALID_TOKEN_ID))
+ with tx_failed():
+ c.ownerOf(INVALID_TOKEN_ID)
def test_getApproved(c, w3):
@@ -72,32 +74,24 @@ def test_isApprovedForAll(c, w3):
assert c.isApprovedForAll(someone, operator) == 1
-def test_transferFrom_by_owner(c, w3, assert_tx_failed, get_logs):
+def test_transferFrom_by_owner(c, w3, tx_failed, get_logs):
someone, operator = w3.eth.accounts[1:3]
# transfer from zero address
- assert_tx_failed(
- lambda: c.transferFrom(
- ZERO_ADDRESS, operator, SOMEONE_TOKEN_IDS[0], transact={"from": someone}
- )
- )
+ with tx_failed():
+ c.transferFrom(ZERO_ADDRESS, operator, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
# transfer to zero address
- assert_tx_failed(
- lambda: c.transferFrom(
- someone, ZERO_ADDRESS, SOMEONE_TOKEN_IDS[0], transact={"from": someone}
- )
- )
+ with tx_failed():
+ c.transferFrom(someone, ZERO_ADDRESS, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
# transfer token without ownership
- assert_tx_failed(
- lambda: c.transferFrom(someone, operator, OPERATOR_TOKEN_ID, transact={"from": someone})
- )
+ with tx_failed():
+ c.transferFrom(someone, operator, OPERATOR_TOKEN_ID, transact={"from": someone})
# transfer invalid token
- assert_tx_failed(
- lambda: c.transferFrom(someone, operator, INVALID_TOKEN_ID, transact={"from": someone})
- )
+ with tx_failed():
+ c.transferFrom(someone, operator, INVALID_TOKEN_ID, transact={"from": someone})
# transfer by owner
tx_hash = c.transferFrom(someone, operator, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
@@ -152,32 +146,24 @@ def test_transferFrom_by_operator(c, w3, get_logs):
assert c.balanceOf(operator) == 2
-def test_safeTransferFrom_by_owner(c, w3, assert_tx_failed, get_logs):
+def test_safeTransferFrom_by_owner(c, w3, tx_failed, get_logs):
someone, operator = w3.eth.accounts[1:3]
# transfer from zero address
- assert_tx_failed(
- lambda: c.safeTransferFrom(
- ZERO_ADDRESS, operator, SOMEONE_TOKEN_IDS[0], transact={"from": someone}
- )
- )
+ with tx_failed():
+ c.safeTransferFrom(ZERO_ADDRESS, operator, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
# transfer to zero address
- assert_tx_failed(
- lambda: c.safeTransferFrom(
- someone, ZERO_ADDRESS, SOMEONE_TOKEN_IDS[0], transact={"from": someone}
- )
- )
+ with tx_failed():
+ c.safeTransferFrom(someone, ZERO_ADDRESS, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
# transfer token without ownership
- assert_tx_failed(
- lambda: c.safeTransferFrom(someone, operator, OPERATOR_TOKEN_ID, transact={"from": someone})
- )
+ with tx_failed():
+ c.safeTransferFrom(someone, operator, OPERATOR_TOKEN_ID, transact={"from": someone})
# transfer invalid token
- assert_tx_failed(
- lambda: c.safeTransferFrom(someone, operator, INVALID_TOKEN_ID, transact={"from": someone})
- )
+ with tx_failed():
+ c.safeTransferFrom(someone, operator, INVALID_TOKEN_ID, transact={"from": someone})
# transfer by owner
tx_hash = c.safeTransferFrom(
@@ -238,15 +224,12 @@ def test_safeTransferFrom_by_operator(c, w3, get_logs):
assert c.balanceOf(operator) == 2
-def test_safeTransferFrom_to_contract(c, w3, assert_tx_failed, get_logs, get_contract):
+def test_safeTransferFrom_to_contract(c, w3, tx_failed, get_logs, get_contract):
someone = w3.eth.accounts[1]
# Can't transfer to a contract that doesn't implement the receiver code
- assert_tx_failed(
- lambda: c.safeTransferFrom(
- someone, c.address, SOMEONE_TOKEN_IDS[0], transact={"from": someone}
- )
- ) # noqa: E501
+ with tx_failed():
+ c.safeTransferFrom(someone, c.address, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
# Only to an address that implements that function
receiver = get_contract(
@@ -277,17 +260,20 @@ def onERC721Received(
assert c.balanceOf(receiver.address) == 1
-def test_approve(c, w3, assert_tx_failed, get_logs):
+def test_approve(c, w3, tx_failed, get_logs):
someone, operator = w3.eth.accounts[1:3]
# approve myself
- assert_tx_failed(lambda: c.approve(someone, SOMEONE_TOKEN_IDS[0], transact={"from": someone}))
+ with tx_failed():
+ c.approve(someone, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
# approve token without ownership
- assert_tx_failed(lambda: c.approve(operator, OPERATOR_TOKEN_ID, transact={"from": someone}))
+ with tx_failed():
+ c.approve(operator, OPERATOR_TOKEN_ID, transact={"from": someone})
# approve invalid token
- assert_tx_failed(lambda: c.approve(operator, INVALID_TOKEN_ID, transact={"from": someone}))
+ with tx_failed():
+ c.approve(operator, INVALID_TOKEN_ID, transact={"from": someone})
tx_hash = c.approve(operator, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
logs = get_logs(tx_hash, c, "Approval")
@@ -299,12 +285,13 @@ def test_approve(c, w3, assert_tx_failed, get_logs):
assert args.tokenId == SOMEONE_TOKEN_IDS[0]
-def test_setApprovalForAll(c, w3, assert_tx_failed, get_logs):
+def test_setApprovalForAll(c, w3, tx_failed, get_logs):
someone, operator = w3.eth.accounts[1:3]
approved = True
# setApprovalForAll myself
- assert_tx_failed(lambda: c.setApprovalForAll(someone, approved, transact={"from": someone}))
+ with tx_failed():
+ c.setApprovalForAll(someone, approved, transact={"from": someone})
tx_hash = c.setApprovalForAll(operator, approved, transact={"from": someone})
logs = get_logs(tx_hash, c, "ApprovalForAll")
@@ -316,14 +303,16 @@ def test_setApprovalForAll(c, w3, assert_tx_failed, get_logs):
assert args.approved == approved
-def test_mint(c, w3, assert_tx_failed, get_logs):
+def test_mint(c, w3, tx_failed, get_logs):
minter, someone = w3.eth.accounts[:2]
# mint by non-minter
- assert_tx_failed(lambda: c.mint(someone, SOMEONE_TOKEN_IDS[0], transact={"from": someone}))
+ with tx_failed():
+ c.mint(someone, SOMEONE_TOKEN_IDS[0], transact={"from": someone})
# mint to zero address
- assert_tx_failed(lambda: c.mint(ZERO_ADDRESS, SOMEONE_TOKEN_IDS[0], transact={"from": minter}))
+ with tx_failed():
+ c.mint(ZERO_ADDRESS, SOMEONE_TOKEN_IDS[0], transact={"from": minter})
# mint by minter
tx_hash = c.mint(someone, NEW_TOKEN_ID, transact={"from": minter})
@@ -338,11 +327,12 @@ def test_mint(c, w3, assert_tx_failed, get_logs):
assert c.balanceOf(someone) == 4
-def test_burn(c, w3, assert_tx_failed, get_logs):
+def test_burn(c, w3, tx_failed, get_logs):
someone, operator = w3.eth.accounts[1:3]
# burn token without ownership
- assert_tx_failed(lambda: c.burn(SOMEONE_TOKEN_IDS[0], transact={"from": operator}))
+ with tx_failed():
+ c.burn(SOMEONE_TOKEN_IDS[0], transact={"from": operator})
# burn token by owner
tx_hash = c.burn(SOMEONE_TOKEN_IDS[0], transact={"from": someone})
@@ -353,5 +343,6 @@ def test_burn(c, w3, assert_tx_failed, get_logs):
assert args.sender == someone
assert args.receiver == ZERO_ADDRESS
assert args.tokenId == SOMEONE_TOKEN_IDS[0]
- assert_tx_failed(lambda: c.ownerOf(SOMEONE_TOKEN_IDS[0]))
+ with tx_failed():
+ c.ownerOf(SOMEONE_TOKEN_IDS[0])
assert c.balanceOf(someone) == 2
diff --git a/tests/examples/voting/test_ballot.py b/tests/functional/examples/voting/test_ballot.py
similarity index 93%
rename from tests/examples/voting/test_ballot.py
rename to tests/functional/examples/voting/test_ballot.py
index 4207fe6e4e..9c3a09fc83 100644
--- a/tests/examples/voting/test_ballot.py
+++ b/tests/functional/examples/voting/test_ballot.py
@@ -33,7 +33,7 @@ def test_initial_state(w3, c):
assert c.voters(z0)[0] == 0 # Voter.weight
-def test_give_the_right_to_vote(w3, c, assert_tx_failed):
+def test_give_the_right_to_vote(w3, c, tx_failed):
a0, a1, a2, a3, a4, a5 = w3.eth.accounts[:6]
c.giveRightToVote(a1, transact={})
# Check voter given right has weight of 1
@@ -56,7 +56,8 @@ def test_give_the_right_to_vote(w3, c, assert_tx_failed):
# Check voter_acount is now 6
assert c.voterCount() == 6
# Check chairperson cannot give the right to vote twice to the same voter
- assert_tx_failed(lambda: c.giveRightToVote(a5, transact={}))
+ with tx_failed():
+ c.giveRightToVote(a5, transact={})
# Check voters weight didn't change
assert c.voters(a5)[0] == 1 # Voter.weight
@@ -127,7 +128,7 @@ def test_forward_weight(w3, c):
assert c.voters(a9)[0] == 10 # Voter.weight
-def test_block_short_cycle(w3, c, assert_tx_failed):
+def test_block_short_cycle(w3, c, tx_failed):
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = w3.eth.accounts[:10]
c.giveRightToVote(a0, transact={})
c.giveRightToVote(a1, transact={})
@@ -141,7 +142,8 @@ def test_block_short_cycle(w3, c, assert_tx_failed):
c.delegate(a3, transact={"from": a2})
c.delegate(a4, transact={"from": a3})
# would create a length 5 cycle:
- assert_tx_failed(lambda: c.delegate(a0, transact={"from": a4}))
+ with tx_failed():
+ c.delegate(a0, transact={"from": a4})
c.delegate(a5, transact={"from": a4})
# can't detect length 6 cycle, so this works:
@@ -150,7 +152,7 @@ def test_block_short_cycle(w3, c, assert_tx_failed):
# but this is something the frontend should prevent for user friendliness
-def test_delegate(w3, c, assert_tx_failed):
+def test_delegate(w3, c, tx_failed):
a0, a1, a2, a3, a4, a5, a6 = w3.eth.accounts[:7]
c.giveRightToVote(a0, transact={})
c.giveRightToVote(a1, transact={})
@@ -167,9 +169,11 @@ def test_delegate(w3, c, assert_tx_failed):
# Delegate's weight is 2
assert c.voters(a0)[0] == 2 # Voter.weight
# Voter cannot delegate twice
- assert_tx_failed(lambda: c.delegate(a2, transact={"from": a1}))
+ with tx_failed():
+ c.delegate(a2, transact={"from": a1})
# Voter cannot delegate to themselves
- assert_tx_failed(lambda: c.delegate(a2, transact={"from": a2}))
+ with tx_failed():
+ c.delegate(a2, transact={"from": a2})
# Voter CAN delegate to someone who hasn't been granted right to vote
# Exercise: prevent that
c.delegate(a6, transact={"from": a2})
@@ -180,7 +184,7 @@ def test_delegate(w3, c, assert_tx_failed):
assert c.voters(a0)[0] == 3 # Voter.weight
-def test_vote(w3, c, assert_tx_failed):
+def test_vote(w3, c, tx_failed):
a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = w3.eth.accounts[:10]
c.giveRightToVote(a0, transact={})
c.giveRightToVote(a1, transact={})
@@ -197,9 +201,11 @@ def test_vote(w3, c, assert_tx_failed):
# Vote count changes based on voters weight
assert c.proposals(0)[1] == 3 # Proposal.voteCount
# Voter cannot vote twice
- assert_tx_failed(lambda: c.vote(0))
+ with tx_failed():
+ c.vote(0)
# Voter cannot vote if they've delegated
- assert_tx_failed(lambda: c.vote(0, transact={"from": a1}))
+ with tx_failed():
+ c.vote(0, transact={"from": a1})
# Several voters can vote
c.vote(1, transact={"from": a4})
c.vote(1, transact={"from": a2})
@@ -207,7 +213,8 @@ def test_vote(w3, c, assert_tx_failed):
c.vote(1, transact={"from": a6})
assert c.proposals(1)[1] == 4 # Proposal.voteCount
# Can't vote on a non-proposal
- assert_tx_failed(lambda: c.vote(2, transact={"from": a7}))
+ with tx_failed():
+ c.vote(2, transact={"from": a7})
def test_winning_proposal(w3, c):
diff --git a/tests/examples/wallet/test_wallet.py b/tests/functional/examples/wallet/test_wallet.py
similarity index 87%
rename from tests/examples/wallet/test_wallet.py
rename to tests/functional/examples/wallet/test_wallet.py
index 71f1e5f331..b9db5acee3 100644
--- a/tests/examples/wallet/test_wallet.py
+++ b/tests/functional/examples/wallet/test_wallet.py
@@ -29,7 +29,7 @@ def _sign(seq, to, value, data, key):
return _sign
-def test_approve(w3, c, tester, assert_tx_failed, sign):
+def test_approve(w3, c, tester, tx_failed, sign):
a0, a1, a2, a3, a4, a5, a6 = w3.eth.accounts[:7]
k0, k1, k2, k3, k4, k5, k6, k7 = tester.backend.account_keys[:8]
@@ -45,24 +45,20 @@ def pack_and_sign(seq, *args):
c.approve(0, "0x" + to.hex(), value, data, sigs, transact={"value": value, "from": a1})
# Approve fails if only 2 signatures are given
sigs = pack_and_sign(1, k1, 0, k3, 0, 0)
- assert_tx_failed(
- lambda: c.approve(1, to_address, value, data, sigs, transact={"value": value, "from": a1})
- ) # noqa: E501
+ with tx_failed():
+ c.approve(1, to_address, value, data, sigs, transact={"value": value, "from": a1})
# Approve fails if an invalid signature is given
sigs = pack_and_sign(1, k1, 0, k7, 0, k5)
- assert_tx_failed(
- lambda: c.approve(1, to_address, value, data, sigs, transact={"value": value, "from": a1})
- ) # noqa: E501
+ with tx_failed():
+ c.approve(1, to_address, value, data, sigs, transact={"value": value, "from": a1})
# Approve fails if transaction number is incorrect (the first argument should be 1)
sigs = pack_and_sign(0, k1, 0, k3, 0, k5)
- assert_tx_failed(
- lambda: c.approve(0, to_address, value, data, sigs, transact={"value": value, "from": a1})
- ) # noqa: E501
+ with tx_failed():
+ c.approve(0, to_address, value, data, sigs, transact={"value": value, "from": a1})
# Approve fails if not enough value is sent
sigs = pack_and_sign(1, k1, 0, k3, 0, k5)
- assert_tx_failed(
- lambda: c.approve(1, to_address, value, data, sigs, transact={"value": 0, "from": a1})
- ) # noqa: E501
+ with tx_failed():
+ c.approve(1, to_address, value, data, sigs, transact={"value": 0, "from": a1})
sigs = pack_and_sign(1, k1, 0, k3, 0, k5)
# this call should succeed
diff --git a/tests/grammar/test_grammar.py b/tests/functional/grammar/test_grammar.py
similarity index 94%
rename from tests/grammar/test_grammar.py
rename to tests/functional/grammar/test_grammar.py
index d665ca2544..7dd8c35929 100644
--- a/tests/grammar/test_grammar.py
+++ b/tests/functional/grammar/test_grammar.py
@@ -4,7 +4,7 @@
import hypothesis
import hypothesis.strategies as st
import pytest
-from hypothesis import HealthCheck, assume, given
+from hypothesis import assume, given
from hypothesis.extra.lark import LarkStrategy
from vyper.ast import Module, parse_to_ast
@@ -92,7 +92,7 @@ def from_grammar() -> st.SearchStrategy[str]:
# Avoid examples with *only* single or double quote docstrings
-# because they trigger a trivial compiler bug
+# because they trigger a trivial parser bug
SINGLE_QUOTE_DOCSTRING = re.compile(r"^'''.*'''$")
DOUBLE_QUOTE_DOCSTRING = re.compile(r'^""".*"""$')
@@ -103,7 +103,7 @@ def has_no_docstrings(c):
@pytest.mark.fuzzing
@given(code=from_grammar().filter(lambda c: utf8_encodable(c)))
-@hypothesis.settings(deadline=400, max_examples=500, suppress_health_check=(HealthCheck.too_slow,))
+@hypothesis.settings(max_examples=500)
def test_grammar_bruteforce(code):
if utf8_encodable(code):
_, _, reformatted_code = pre_parse(code + "\n")
diff --git a/tests/parser/syntax/__init__.py b/tests/functional/syntax/__init__.py
similarity index 100%
rename from tests/parser/syntax/__init__.py
rename to tests/functional/syntax/__init__.py
diff --git a/tests/parser/exceptions/test_argument_exception.py b/tests/functional/syntax/exceptions/test_argument_exception.py
similarity index 93%
rename from tests/parser/exceptions/test_argument_exception.py
rename to tests/functional/syntax/exceptions/test_argument_exception.py
index fc06395015..0b7ec21bdb 100644
--- a/tests/parser/exceptions/test_argument_exception.py
+++ b/tests/functional/syntax/exceptions/test_argument_exception.py
@@ -1,13 +1,13 @@
import pytest
-from vyper import compiler
+from vyper import compile_code
from vyper.exceptions import ArgumentException
fail_list = [
"""
@external
def foo():
- x = as_wei_value(5, "vader")
+ x: uint256 = as_wei_value(5, "vader")
""",
"""
@external
@@ -95,4 +95,4 @@ def foo():
@pytest.mark.parametrize("bad_code", fail_list)
def test_function_declaration_exception(bad_code):
with pytest.raises(ArgumentException):
- compiler.compile_code(bad_code)
+ compile_code(bad_code)
diff --git a/tests/parser/exceptions/test_call_violation.py b/tests/functional/syntax/exceptions/test_call_violation.py
similarity index 100%
rename from tests/parser/exceptions/test_call_violation.py
rename to tests/functional/syntax/exceptions/test_call_violation.py
diff --git a/tests/parser/exceptions/test_constancy_exception.py b/tests/functional/syntax/exceptions/test_constancy_exception.py
similarity index 100%
rename from tests/parser/exceptions/test_constancy_exception.py
rename to tests/functional/syntax/exceptions/test_constancy_exception.py
diff --git a/tests/parser/exceptions/test_function_declaration_exception.py b/tests/functional/syntax/exceptions/test_function_declaration_exception.py
similarity index 100%
rename from tests/parser/exceptions/test_function_declaration_exception.py
rename to tests/functional/syntax/exceptions/test_function_declaration_exception.py
diff --git a/tests/parser/exceptions/test_instantiation_exception.py b/tests/functional/syntax/exceptions/test_instantiation_exception.py
similarity index 100%
rename from tests/parser/exceptions/test_instantiation_exception.py
rename to tests/functional/syntax/exceptions/test_instantiation_exception.py
diff --git a/tests/parser/exceptions/test_invalid_literal_exception.py b/tests/functional/syntax/exceptions/test_invalid_literal_exception.py
similarity index 88%
rename from tests/parser/exceptions/test_invalid_literal_exception.py
rename to tests/functional/syntax/exceptions/test_invalid_literal_exception.py
index 1f4f112252..a0cf10ad02 100644
--- a/tests/parser/exceptions/test_invalid_literal_exception.py
+++ b/tests/functional/syntax/exceptions/test_invalid_literal_exception.py
@@ -18,13 +18,6 @@ def foo():
""",
"""
@external
-def foo(x: int128):
- y: int128 = 7
- for i in range(x, x + y):
- pass
- """,
- """
-@external
def foo():
x: String[100] = "these bytes are nо gооd because the o's are from the Russian alphabet"
""",
diff --git a/tests/parser/exceptions/test_invalid_payable.py b/tests/functional/syntax/exceptions/test_invalid_payable.py
similarity index 100%
rename from tests/parser/exceptions/test_invalid_payable.py
rename to tests/functional/syntax/exceptions/test_invalid_payable.py
diff --git a/tests/parser/exceptions/test_invalid_reference.py b/tests/functional/syntax/exceptions/test_invalid_reference.py
similarity index 100%
rename from tests/parser/exceptions/test_invalid_reference.py
rename to tests/functional/syntax/exceptions/test_invalid_reference.py
diff --git a/tests/parser/exceptions/test_invalid_type_exception.py b/tests/functional/syntax/exceptions/test_invalid_type_exception.py
similarity index 100%
rename from tests/parser/exceptions/test_invalid_type_exception.py
rename to tests/functional/syntax/exceptions/test_invalid_type_exception.py
diff --git a/tests/parser/exceptions/test_namespace_collision.py b/tests/functional/syntax/exceptions/test_namespace_collision.py
similarity index 100%
rename from tests/parser/exceptions/test_namespace_collision.py
rename to tests/functional/syntax/exceptions/test_namespace_collision.py
diff --git a/tests/parser/exceptions/test_overflow_exception.py b/tests/functional/syntax/exceptions/test_overflow_exception.py
similarity index 100%
rename from tests/parser/exceptions/test_overflow_exception.py
rename to tests/functional/syntax/exceptions/test_overflow_exception.py
diff --git a/tests/parser/exceptions/test_structure_exception.py b/tests/functional/syntax/exceptions/test_structure_exception.py
similarity index 86%
rename from tests/parser/exceptions/test_structure_exception.py
rename to tests/functional/syntax/exceptions/test_structure_exception.py
index 08794b75f2..97ac2b139d 100644
--- a/tests/parser/exceptions/test_structure_exception.py
+++ b/tests/functional/syntax/exceptions/test_structure_exception.py
@@ -56,9 +56,26 @@ def double_nonreentrant():
""",
"""
@external
-@nonreentrant("B")
-@nonreentrant("C")
-def double_nonreentrant():
+@nonreentrant(" ")
+def invalid_nonreentrant_key():
+ pass
+ """,
+ """
+@external
+@nonreentrant("")
+def invalid_nonreentrant_key():
+ pass
+ """,
+ """
+@external
+@nonreentrant("123")
+def invalid_nonreentrant_key():
+ pass
+ """,
+ """
+@external
+@nonreentrant("!123abcd")
+def invalid_nonreentrant_key():
pass
""",
"""
diff --git a/tests/parser/exceptions/test_syntax_exception.py b/tests/functional/syntax/exceptions/test_syntax_exception.py
similarity index 100%
rename from tests/parser/exceptions/test_syntax_exception.py
rename to tests/functional/syntax/exceptions/test_syntax_exception.py
diff --git a/tests/parser/exceptions/test_type_mismatch_exception.py b/tests/functional/syntax/exceptions/test_type_mismatch_exception.py
similarity index 100%
rename from tests/parser/exceptions/test_type_mismatch_exception.py
rename to tests/functional/syntax/exceptions/test_type_mismatch_exception.py
diff --git a/tests/parser/exceptions/test_undeclared_definition.py b/tests/functional/syntax/exceptions/test_undeclared_definition.py
similarity index 100%
rename from tests/parser/exceptions/test_undeclared_definition.py
rename to tests/functional/syntax/exceptions/test_undeclared_definition.py
diff --git a/tests/parser/exceptions/test_variable_declaration_exception.py b/tests/functional/syntax/exceptions/test_variable_declaration_exception.py
similarity index 100%
rename from tests/parser/exceptions/test_variable_declaration_exception.py
rename to tests/functional/syntax/exceptions/test_variable_declaration_exception.py
diff --git a/tests/parser/exceptions/test_vyper_exception_pos.py b/tests/functional/syntax/exceptions/test_vyper_exception_pos.py
similarity index 100%
rename from tests/parser/exceptions/test_vyper_exception_pos.py
rename to tests/functional/syntax/exceptions/test_vyper_exception_pos.py
diff --git a/tests/parser/syntax/utils/test_event_names.py b/tests/functional/syntax/names/test_event_names.py
similarity index 100%
rename from tests/parser/syntax/utils/test_event_names.py
rename to tests/functional/syntax/names/test_event_names.py
diff --git a/tests/parser/syntax/utils/test_function_names.py b/tests/functional/syntax/names/test_function_names.py
similarity index 100%
rename from tests/parser/syntax/utils/test_function_names.py
rename to tests/functional/syntax/names/test_function_names.py
diff --git a/tests/parser/syntax/utils/test_variable_names.py b/tests/functional/syntax/names/test_variable_names.py
similarity index 100%
rename from tests/parser/syntax/utils/test_variable_names.py
rename to tests/functional/syntax/names/test_variable_names.py
diff --git a/tests/signatures/test_invalid_function_decorators.py b/tests/functional/syntax/signatures/test_invalid_function_decorators.py
similarity index 100%
rename from tests/signatures/test_invalid_function_decorators.py
rename to tests/functional/syntax/signatures/test_invalid_function_decorators.py
diff --git a/tests/signatures/test_method_id_conflicts.py b/tests/functional/syntax/signatures/test_method_id_conflicts.py
similarity index 100%
rename from tests/signatures/test_method_id_conflicts.py
rename to tests/functional/syntax/signatures/test_method_id_conflicts.py
diff --git a/tests/functional/syntax/test_abi_decode.py b/tests/functional/syntax/test_abi_decode.py
new file mode 100644
index 0000000000..a6665bb84c
--- /dev/null
+++ b/tests/functional/syntax/test_abi_decode.py
@@ -0,0 +1,45 @@
+import pytest
+
+from vyper import compiler
+from vyper.exceptions import TypeMismatch
+
+fail_list = [
+ (
+ """
+@external
+def foo(j: uint256) -> bool:
+ s: bool = _abi_decode(j, bool, unwrap_tuple= False)
+ return s
+ """,
+ TypeMismatch,
+ ),
+ (
+ """
+@external
+def bar(j: String[32]) -> bool:
+ s: bool = _abi_decode(j, bool, unwrap_tuple= False)
+ return s
+ """,
+ TypeMismatch,
+ ),
+]
+
+
+@pytest.mark.parametrize("bad_code,exc", fail_list)
+def test_abi_decode_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compiler.compile_code(bad_code)
+
+
+valid_list = [
+ """
+@external
+def foo(x: Bytes[32]) -> uint256:
+ return _abi_decode(x, uint256)
+ """
+]
+
+
+@pytest.mark.parametrize("good_code", valid_list)
+def test_abi_decode_success(good_code):
+ assert compiler.compile_code(good_code) is not None
diff --git a/tests/parser/syntax/test_abi_encode.py b/tests/functional/syntax/test_abi_encode.py
similarity index 100%
rename from tests/parser/syntax/test_abi_encode.py
rename to tests/functional/syntax/test_abi_encode.py
diff --git a/tests/functional/syntax/test_abs.py b/tests/functional/syntax/test_abs.py
new file mode 100644
index 0000000000..0841ff05d6
--- /dev/null
+++ b/tests/functional/syntax/test_abs.py
@@ -0,0 +1,40 @@
+import pytest
+
+from vyper import compile_code
+from vyper.exceptions import InvalidType
+
+fail_list = [
+ (
+ """
+@external
+def foo():
+ y: int256 = abs(
+ -57896044618658097711785492504343953926634992332820282019728792003956564819968
+ )
+ """,
+ InvalidType,
+ )
+]
+
+
+@pytest.mark.parametrize("bad_code,exc", fail_list)
+def test_abs_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compile_code(bad_code)
+
+
+valid_list = [
+ """
+FOO: constant(int256) = -3
+BAR: constant(int256) = abs(FOO)
+
+@external
+def foo():
+ a: int256 = BAR
+ """
+]
+
+
+@pytest.mark.parametrize("code", valid_list)
+def test_abs_pass(code):
+ assert compile_code(code) is not None
diff --git a/tests/parser/syntax/test_addmulmod.py b/tests/functional/syntax/test_addmulmod.py
similarity index 54%
rename from tests/parser/syntax/test_addmulmod.py
rename to tests/functional/syntax/test_addmulmod.py
index ddff4d3e01..17c7b3ab8c 100644
--- a/tests/parser/syntax/test_addmulmod.py
+++ b/tests/functional/syntax/test_addmulmod.py
@@ -1,5 +1,6 @@
import pytest
+from vyper import compile_code
from vyper.exceptions import InvalidType
fail_list = [
@@ -25,3 +26,24 @@ def foo() -> uint256:
@pytest.mark.parametrize("code,exc", fail_list)
def test_add_mod_fail(assert_compile_failed, get_contract, code, exc):
assert_compile_failed(lambda: get_contract(code), exc)
+
+
+valid_list = [
+ """
+FOO: constant(uint256) = 3
+BAR: constant(uint256) = 5
+BAZ: constant(uint256) = 19
+BAX: constant(uint256) = uint256_addmod(FOO, BAR, BAZ)
+ """,
+ """
+FOO: constant(uint256) = 3
+BAR: constant(uint256) = 5
+BAZ: constant(uint256) = 19
+BAX: constant(uint256) = uint256_mulmod(FOO, BAR, BAZ)
+ """,
+]
+
+
+@pytest.mark.parametrize("code", valid_list)
+def test_addmulmod_pass(code):
+ assert compile_code(code) is not None
diff --git a/tests/parser/syntax/test_address_code.py b/tests/functional/syntax/test_address_code.py
similarity index 99%
rename from tests/parser/syntax/test_address_code.py
rename to tests/functional/syntax/test_address_code.py
index 70ba5cbbf7..fa6ed20117 100644
--- a/tests/parser/syntax/test_address_code.py
+++ b/tests/functional/syntax/test_address_code.py
@@ -125,7 +125,6 @@ def test_address_code_compile_error(
):
with pytest.raises(error_type) as excinfo:
compiler.compile_code(bad_code)
- assert type(excinfo.value) == error_type
assert excinfo.value.message == error_message
diff --git a/tests/parser/syntax/test_ann_assign.py b/tests/functional/syntax/test_ann_assign.py
similarity index 100%
rename from tests/parser/syntax/test_ann_assign.py
rename to tests/functional/syntax/test_ann_assign.py
diff --git a/tests/parser/syntax/test_as_uint256.py b/tests/functional/syntax/test_as_uint256.py
similarity index 100%
rename from tests/parser/syntax/test_as_uint256.py
rename to tests/functional/syntax/test_as_uint256.py
diff --git a/tests/functional/syntax/test_as_wei_value.py b/tests/functional/syntax/test_as_wei_value.py
new file mode 100644
index 0000000000..056d0348e9
--- /dev/null
+++ b/tests/functional/syntax/test_as_wei_value.py
@@ -0,0 +1,131 @@
+import pytest
+
+from vyper import compile_code
+from vyper.exceptions import (
+ ArgumentException,
+ InvalidLiteral,
+ InvalidType,
+ OverflowException,
+ StructureException,
+ UndeclaredDefinition,
+)
+
+# CMC 2023-12-31 these tests could probably go in builtins/folding/
+
+fail_list = [
+ (
+ """
+@external
+def foo():
+ x: uint256 = as_wei_value(5, szabo)
+ """,
+ UndeclaredDefinition,
+ ),
+ (
+ """
+@external
+def foo():
+ x: uint256 = as_wei_value(5, "szaboo")
+ """,
+ ArgumentException,
+ ),
+ (
+ """
+@external
+def foo() -> int128:
+ x: int128 = 45
+ return x.balance
+ """,
+ StructureException,
+ ),
+ (
+ """
+@external
+def foo():
+ x: int128 = as_wei_value(0xf5, "szabo")
+ """,
+ InvalidType,
+ ),
+ (
+ """
+@external
+def foo() -> uint256:
+ return as_wei_value(
+ 115792089237316195423570985008687907853269984665640564039457584007913129639937,
+ 'milliether'
+ )
+ """,
+ OverflowException,
+ ),
+ (
+ """
+@external
+def foo():
+ x: uint256 = as_wei_value(-1, "szabo")
+ """,
+ InvalidLiteral,
+ ),
+ (
+ """
+FOO: constant(uint256) = as_wei_value(5, szabo)
+ """,
+ UndeclaredDefinition,
+ ),
+ (
+ """
+FOO: constant(uint256) = as_wei_value(5, "szaboo")
+ """,
+ ArgumentException,
+ ),
+ (
+ """
+FOO: constant(uint256) = as_wei_value(-1, "szabo")
+ """,
+ InvalidLiteral,
+ ),
+]
+
+
+@pytest.mark.parametrize("bad_code,exc", fail_list)
+def test_as_wei_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compile_code(bad_code)
+
+
+valid_list = [
+ """
+@external
+def foo():
+ x: uint256 = as_wei_value(5, "finney") + as_wei_value(2, "babbage") + as_wei_value(8, "shannon") # noqa: E501
+ """,
+ """
+@external
+def foo():
+ z: int128 = 2 + 3
+ x: uint256 = as_wei_value(2 + 3, "finney")
+ """,
+ """
+@external
+def foo():
+ x: uint256 = as_wei_value(5.182, "babbage")
+ """,
+ """
+@external
+def foo() -> uint256:
+ x: address = 0x1234567890123456789012345678901234567890
+ return x.balance
+ """,
+ """
+y: constant(String[5]) = "szabo"
+x: constant(uint256) = as_wei_value(5, y)
+
+@external
+def foo():
+ a: uint256 = x
+ """,
+]
+
+
+@pytest.mark.parametrize("good_code", valid_list)
+def test_as_wei_success(good_code, get_contract_with_gas_estimation):
+ assert get_contract_with_gas_estimation(good_code) is not None
diff --git a/tests/parser/syntax/test_block.py b/tests/functional/syntax/test_block.py
similarity index 100%
rename from tests/parser/syntax/test_block.py
rename to tests/functional/syntax/test_block.py
diff --git a/tests/parser/syntax/test_blockscope.py b/tests/functional/syntax/test_blockscope.py
similarity index 100%
rename from tests/parser/syntax/test_blockscope.py
rename to tests/functional/syntax/test_blockscope.py
diff --git a/tests/parser/syntax/test_bool.py b/tests/functional/syntax/test_bool.py
similarity index 98%
rename from tests/parser/syntax/test_bool.py
rename to tests/functional/syntax/test_bool.py
index 09f799d91c..48ed37321a 100644
--- a/tests/parser/syntax/test_bool.py
+++ b/tests/functional/syntax/test_bool.py
@@ -52,7 +52,7 @@ def foo() -> bool:
"""
@external
def foo() -> bool:
- a: address = ZERO_ADDRESS
+ a: address = empty(address)
return a == 1
""",
(
@@ -137,7 +137,7 @@ def foo() -> bool:
"""
@external
def foo2(a: address) -> bool:
- return a != ZERO_ADDRESS
+ return a != empty(address)
""",
]
diff --git a/tests/parser/syntax/test_bool_ops.py b/tests/functional/syntax/test_bool_ops.py
similarity index 100%
rename from tests/parser/syntax/test_bool_ops.py
rename to tests/functional/syntax/test_bool_ops.py
diff --git a/tests/parser/syntax/test_bytes.py b/tests/functional/syntax/test_bytes.py
similarity index 100%
rename from tests/parser/syntax/test_bytes.py
rename to tests/functional/syntax/test_bytes.py
diff --git a/tests/functional/syntax/test_ceil.py b/tests/functional/syntax/test_ceil.py
new file mode 100644
index 0000000000..41f4175d01
--- /dev/null
+++ b/tests/functional/syntax/test_ceil.py
@@ -0,0 +1,19 @@
+import pytest
+
+from vyper import compile_code
+
+valid_list = [
+ """
+BAR: constant(decimal) = 2.5
+FOO: constant(int256) = ceil(BAR)
+
+@external
+def foo():
+ a: int256 = FOO
+ """
+]
+
+
+@pytest.mark.parametrize("code", valid_list)
+def test_ceil_good(code):
+ assert compile_code(code) is not None
diff --git a/tests/parser/syntax/test_chainid.py b/tests/functional/syntax/test_chainid.py
similarity index 100%
rename from tests/parser/syntax/test_chainid.py
rename to tests/functional/syntax/test_chainid.py
diff --git a/tests/parser/syntax/test_code_size.py b/tests/functional/syntax/test_code_size.py
similarity index 100%
rename from tests/parser/syntax/test_code_size.py
rename to tests/functional/syntax/test_code_size.py
diff --git a/tests/parser/syntax/test_codehash.py b/tests/functional/syntax/test_codehash.py
similarity index 92%
rename from tests/parser/syntax/test_codehash.py
rename to tests/functional/syntax/test_codehash.py
index 5074d14636..c2d9a2e274 100644
--- a/tests/parser/syntax/test_codehash.py
+++ b/tests/functional/syntax/test_codehash.py
@@ -33,7 +33,7 @@ def foo4() -> bytes32:
return self.a.codehash
"""
settings = Settings(evm_version=evm_version, optimize=optimize)
- compiled = compile_code(code, ["bytecode_runtime"], settings=settings)
+ compiled = compile_code(code, output_formats=["bytecode_runtime"], settings=settings)
bytecode = bytes.fromhex(compiled["bytecode_runtime"][2:])
hash_ = keccak256(bytecode)
diff --git a/tests/parser/syntax/test_concat.py b/tests/functional/syntax/test_concat.py
similarity index 100%
rename from tests/parser/syntax/test_concat.py
rename to tests/functional/syntax/test_concat.py
diff --git a/tests/parser/syntax/test_conditionals.py b/tests/functional/syntax/test_conditionals.py
similarity index 100%
rename from tests/parser/syntax/test_conditionals.py
rename to tests/functional/syntax/test_conditionals.py
diff --git a/tests/parser/syntax/test_constants.py b/tests/functional/syntax/test_constants.py
similarity index 100%
rename from tests/parser/syntax/test_constants.py
rename to tests/functional/syntax/test_constants.py
diff --git a/tests/parser/syntax/test_create_with_code_of.py b/tests/functional/syntax/test_create_with_code_of.py
similarity index 100%
rename from tests/parser/syntax/test_create_with_code_of.py
rename to tests/functional/syntax/test_create_with_code_of.py
diff --git a/tests/parser/syntax/test_dynamic_array.py b/tests/functional/syntax/test_dynamic_array.py
similarity index 66%
rename from tests/parser/syntax/test_dynamic_array.py
rename to tests/functional/syntax/test_dynamic_array.py
index 0c23bf67da..f566a80625 100644
--- a/tests/parser/syntax/test_dynamic_array.py
+++ b/tests/functional/syntax/test_dynamic_array.py
@@ -1,6 +1,6 @@
import pytest
-from vyper import compiler
+from vyper import compile_code
from vyper.exceptions import StructureException
fail_list = [
@@ -24,22 +24,31 @@ def foo():
""",
StructureException,
),
+ (
+ """
+@external
+def foo():
+ a: DynArray[uint256, FOO] = [1, 2, 3]
+ """,
+ StructureException,
+ ),
]
@pytest.mark.parametrize("bad_code,exc", fail_list)
-def test_block_fail(assert_compile_failed, get_contract, bad_code, exc):
- assert_compile_failed(lambda: get_contract(bad_code), exc)
+def test_block_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compile_code(bad_code)
valid_list = [
"""
-enum Foo:
+flag Foo:
FE
FI
bar: DynArray[Foo, 10]
- """, # dynamic arrays of enums are allowed, but not static arrays
+ """, # dynamic arrays of flags are allowed, but not static arrays
"""
bar: DynArray[Bytes[30], 10]
""", # dynamic arrays of bytestrings are allowed, but not static arrays
@@ -48,4 +57,4 @@ def test_block_fail(assert_compile_failed, get_contract, bad_code, exc):
@pytest.mark.parametrize("good_code", valid_list)
def test_dynarray_pass(good_code):
- assert compiler.compile_code(good_code) is not None
+ assert compile_code(good_code) is not None
diff --git a/tests/functional/syntax/test_epsilon.py b/tests/functional/syntax/test_epsilon.py
new file mode 100644
index 0000000000..0e80d2b4bf
--- /dev/null
+++ b/tests/functional/syntax/test_epsilon.py
@@ -0,0 +1,20 @@
+import pytest
+
+from vyper import compile_code
+from vyper.exceptions import InvalidType
+
+# CMC 2023-12-31 this could probably go in builtins/folding/
+fail_list = [
+ (
+ """
+FOO: constant(address) = epsilon(address)
+ """,
+ InvalidType,
+ )
+]
+
+
+@pytest.mark.parametrize("bad_code,exc", fail_list)
+def test_block_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compile_code(bad_code)
diff --git a/tests/parser/syntax/test_extract32.py b/tests/functional/syntax/test_extract32.py
similarity index 100%
rename from tests/parser/syntax/test_extract32.py
rename to tests/functional/syntax/test_extract32.py
diff --git a/tests/parser/syntax/test_enum.py b/tests/functional/syntax/test_flag.py
similarity index 74%
rename from tests/parser/syntax/test_enum.py
rename to tests/functional/syntax/test_flag.py
index 9bb74fb675..22309502b7 100644
--- a/tests/parser/syntax/test_enum.py
+++ b/tests/functional/syntax/test_flag.py
@@ -2,7 +2,7 @@
from vyper import compiler
from vyper.exceptions import (
- EnumDeclarationException,
+ FlagDeclarationException,
InvalidOperation,
NamespaceCollision,
StructureException,
@@ -16,7 +16,7 @@
event Action:
pass
-enum Action:
+flag Action:
BUY
SELL
""",
@@ -24,23 +24,23 @@
),
(
"""
-enum Action:
+flag Action:
pass
""",
- EnumDeclarationException,
+ FlagDeclarationException,
),
(
"""
-enum Action:
+flag Action:
BUY
BUY
""",
- EnumDeclarationException,
+ FlagDeclarationException,
),
- ("enum Foo:\n" + "\n".join([f" member{i}" for i in range(257)]), EnumDeclarationException),
+ ("flag Foo:\n" + "\n".join([f" member{i}" for i in range(257)]), FlagDeclarationException),
(
"""
-enum Roles:
+flag Roles:
USER
STAFF
ADMIN
@@ -53,20 +53,20 @@ def foo(x: Roles) -> bool:
),
(
"""
-enum Roles:
+flag Roles:
USER
STAFF
ADMIN
@external
def foo(x: Roles) -> Roles:
- return x.USER # can't dereference on enum instance
+ return x.USER # can't dereference on flag instance
""",
StructureException,
),
(
"""
-enum Roles:
+flag Roles:
USER
STAFF
ADMIN
@@ -79,28 +79,28 @@ def foo(x: Roles) -> bool:
),
(
"""
-enum Functions:
+flag Functions:
def foo():nonpayable
""",
- EnumDeclarationException,
+ FlagDeclarationException,
),
(
"""
-enum Numbers:
+flag Numbers:
a:constant(uint256) = a
""",
- EnumDeclarationException,
+ FlagDeclarationException,
),
(
"""
-enum Numbers:
+flag Numbers:
12
""",
- EnumDeclarationException,
+ FlagDeclarationException,
),
(
"""
-enum Roles:
+flag Roles:
ADMIN
USER
@@ -112,9 +112,9 @@ def foo() -> Roles:
),
(
"""
-enum A:
+flag A:
a
-enum B:
+flag B:
a
b
@@ -135,12 +135,12 @@ def test_fail_cases(bad_code):
valid_list = [
"""
-enum Action:
+flag Action:
BUY
SELL
""",
"""
-enum Action:
+flag Action:
BUY
SELL
@external
@@ -148,7 +148,7 @@ def run() -> Action:
return Action.BUY
""",
"""
-enum Action:
+flag Action:
BUY
SELL
@@ -163,16 +163,16 @@ def run() -> Order:
amount: 10**18
})
""",
- "enum Foo:\n" + "\n".join([f" member{i}" for i in range(256)]),
+ "flag Foo:\n" + "\n".join([f" member{i}" for i in range(256)]),
"""
a: constant(uint256) = 1
-enum A:
+flag A:
a
""",
]
@pytest.mark.parametrize("good_code", valid_list)
-def test_enum_success(good_code):
+def test_flag_success(good_code):
assert compiler.compile_code(good_code) is not None
diff --git a/tests/functional/syntax/test_floor.py b/tests/functional/syntax/test_floor.py
new file mode 100644
index 0000000000..5c30aecbe1
--- /dev/null
+++ b/tests/functional/syntax/test_floor.py
@@ -0,0 +1,19 @@
+import pytest
+
+from vyper import compile_code
+
+valid_list = [
+ """
+BAR: constant(decimal) = 2.5
+FOO: constant(int256) = floor(BAR)
+
+@external
+def foo():
+ a: int256 = FOO
+ """
+]
+
+
+@pytest.mark.parametrize("code", valid_list)
+def test_floor_good(code):
+ assert compile_code(code) is not None
diff --git a/tests/functional/syntax/test_for_range.py b/tests/functional/syntax/test_for_range.py
new file mode 100644
index 0000000000..a9c3ad5cab
--- /dev/null
+++ b/tests/functional/syntax/test_for_range.py
@@ -0,0 +1,311 @@
+import re
+
+import pytest
+
+from vyper import compiler
+from vyper.exceptions import (
+ ArgumentException,
+ StateAccessViolation,
+ StructureException,
+ TypeMismatch,
+)
+
+fail_list = [
+ (
+ """
+@external
+def foo():
+ for a[1] in range(10):
+ pass
+ """,
+ StructureException,
+ "Invalid syntax for loop iterator",
+ "a[1]",
+ ),
+ (
+ """
+@external
+def bar():
+ for i in range(1,2,bound=0):
+ pass
+ """,
+ StructureException,
+ "Bound must be at least 1",
+ "0",
+ ),
+ (
+ """
+@external
+def foo():
+ x: uint256 = 100
+ for _ in range(10, bound=x):
+ pass
+ """,
+ StateAccessViolation,
+ "Bound must be a literal",
+ "x",
+ ),
+ (
+ """
+@external
+def foo():
+ for _ in range(10, 20, bound=5):
+ pass
+ """,
+ StructureException,
+ "Please remove the `bound=` kwarg when using range with constants",
+ "5",
+ ),
+ (
+ """
+@external
+def foo():
+ for _ in range(10, 20, bound=0):
+ pass
+ """,
+ StructureException,
+ "Bound must be at least 1",
+ "0",
+ ),
+ (
+ """
+@external
+def bar():
+ x:uint256 = 1
+ for i in range(x,x+1,bound=2,extra=3):
+ pass
+ """,
+ ArgumentException,
+ "Invalid keyword argument 'extra'",
+ "extra=3",
+ ),
+ (
+ """
+@external
+def bar():
+ for i in range(0):
+ pass
+ """,
+ StructureException,
+ "End must be greater than start",
+ "0",
+ ),
+ (
+ """
+@external
+def bar():
+ x:uint256 = 1
+ for i in range(x):
+ pass
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "x",
+ ),
+ (
+ """
+@external
+def bar():
+ x:uint256 = 1
+ for i in range(0, x):
+ pass
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "x",
+ ),
+ (
+ """
+@external
+def repeat(n: uint256) -> uint256:
+ for i in range(0, n * 10):
+ pass
+ return n
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "n * 10",
+ ),
+ (
+ """
+@external
+def bar():
+ x:uint256 = 1
+ for i in range(0, x + 1):
+ pass
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "x + 1",
+ ),
+ (
+ """
+@external
+def bar():
+ for i in range(2, 1):
+ pass
+ """,
+ StructureException,
+ "End must be greater than start",
+ "1",
+ ),
+ (
+ """
+@external
+def bar():
+ x:uint256 = 1
+ for i in range(x, x):
+ pass
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "x",
+ ),
+ (
+ """
+@external
+def foo():
+ x: int128 = 5
+ for i in range(x, x + 10):
+ pass
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "x",
+ ),
+ (
+ """
+@external
+def repeat(n: uint256) -> uint256:
+ for i in range(n, 6):
+ pass
+ return x
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "n",
+ ),
+ (
+ """
+@external
+def foo(x: int128):
+ y: int128 = 7
+ for i in range(x, x + y):
+ pass
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "x",
+ ),
+ (
+ """
+@external
+def bar(x: uint256):
+ for i in range(3, x):
+ pass
+ """,
+ StateAccessViolation,
+ "Value must be a literal integer, unless a bound is specified",
+ "x",
+ ),
+ (
+ """
+FOO: constant(int128) = 3
+BAR: constant(uint256) = 7
+
+@external
+def foo():
+ for i in range(FOO, BAR):
+ pass
+ """,
+ TypeMismatch,
+ "Iterator values are of different types",
+ "range(FOO, BAR)",
+ ),
+ (
+ """
+FOO: constant(int128) = -1
+
+@external
+def foo():
+ for i in range(10, bound=FOO):
+ pass
+ """,
+ StructureException,
+ "Bound must be at least 1",
+ "-1",
+ ),
+]
+
+for_code_regex = re.compile(r"for .+ in (.*):")
+fail_test_names = [
+ (
+ f"{i:02d}: {for_code_regex.search(code).group(1)}" # type: ignore[union-attr]
+ f" raises {type(err).__name__}"
+ )
+ for i, (code, err, msg, src) in enumerate(fail_list)
+]
+
+
+@pytest.mark.parametrize("bad_code,error_type,message,source_code", fail_list, ids=fail_test_names)
+def test_range_fail(bad_code, error_type, message, source_code):
+ with pytest.raises(error_type) as exc_info:
+ compiler.compile_code(bad_code)
+ assert message == exc_info.value.message
+ assert source_code == exc_info.value.args[1].node_source_code
+
+
+valid_list = [
+ """
+@external
+def foo():
+ for i in range(10):
+ pass
+ """,
+ """
+@external
+def foo():
+ for i in range(10, 20):
+ pass
+ """,
+ """
+@external
+def foo():
+ x: int128 = 5
+ for i in range(1, x, bound=4):
+ pass
+ """,
+ """
+@external
+def foo():
+ x: int128 = 5
+ for i in range(x, bound=4):
+ pass
+ """,
+ """
+@external
+def foo():
+ x: int128 = 5
+ for i in range(0, x, bound=4):
+ pass
+ """,
+ """
+interface Foo:
+ def kick(): nonpayable
+foos: Foo[3]
+@external
+def kick_foos():
+ for foo in self.foos:
+ foo.kick()
+ """,
+]
+
+valid_test_names = [
+ f"{i} {for_code_regex.search(code).group(1)}" # type: ignore[union-attr]
+ for i, code in enumerate(valid_list)
+]
+
+
+@pytest.mark.parametrize("good_code", valid_list, ids=valid_test_names)
+def test_range_success(good_code):
+ assert compiler.compile_code(good_code) is not None
diff --git a/tests/parser/syntax/test_functions_call.py b/tests/functional/syntax/test_functions_call.py
similarity index 100%
rename from tests/parser/syntax/test_functions_call.py
rename to tests/functional/syntax/test_functions_call.py
diff --git a/tests/parser/syntax/test_immutables.py b/tests/functional/syntax/test_immutables.py
similarity index 98%
rename from tests/parser/syntax/test_immutables.py
rename to tests/functional/syntax/test_immutables.py
index ab38f6b56d..1027d9fe66 100644
--- a/tests/parser/syntax/test_immutables.py
+++ b/tests/functional/syntax/test_immutables.py
@@ -63,7 +63,7 @@ def __init__(_value: uint256):
@pytest.mark.parametrize("bad_code", fail_list)
def test_compilation_fails_with_exception(bad_code):
- with pytest.raises(Exception):
+ with pytest.raises(VyperException):
compile_code(bad_code)
diff --git a/tests/parser/syntax/test_interfaces.py b/tests/functional/syntax/test_interfaces.py
similarity index 94%
rename from tests/parser/syntax/test_interfaces.py
rename to tests/functional/syntax/test_interfaces.py
index 5afb34e6bd..a672ed7b88 100644
--- a/tests/parser/syntax/test_interfaces.py
+++ b/tests/functional/syntax/test_interfaces.py
@@ -47,7 +47,7 @@ def test():
@external
def test():
- a: address(ERC20) = ZERO_ADDRESS
+ a: address(ERC20) = empty(address)
""",
InvalidType,
),
@@ -306,7 +306,7 @@ def some_func(): nonpayable
@external
def __init__():
- self.my_interface[self.idx] = MyInterface(ZERO_ADDRESS)
+ self.my_interface[self.idx] = MyInterface(empty(address))
""",
"""
interface MyInterface:
@@ -374,18 +374,15 @@ def test_interfaces_success(good_code):
assert compiler.compile_code(good_code) is not None
-def test_imports_and_implements_within_interface():
+def test_imports_and_implements_within_interface(make_input_bundle):
interface_code = """
-from vyper.interfaces import ERC20
-import foo.bar as Baz
-
-implements: Baz
-
@external
def foobar():
- pass
+ ...
"""
+ input_bundle = make_input_bundle({"foo.vyi": interface_code})
+
code = """
import foo as Foo
@@ -396,9 +393,4 @@ def foobar():
pass
"""
- assert (
- compiler.compile_code(
- code, interface_codes={"Foo": {"type": "vyper", "code": interface_code}}
- )
- is not None
- )
+ assert compiler.compile_code(code, input_bundle=input_bundle) is not None
diff --git a/tests/parser/syntax/test_invalids.py b/tests/functional/syntax/test_invalids.py
similarity index 100%
rename from tests/parser/syntax/test_invalids.py
rename to tests/functional/syntax/test_invalids.py
diff --git a/tests/parser/syntax/test_keccak256.py b/tests/functional/syntax/test_keccak256.py
similarity index 100%
rename from tests/parser/syntax/test_keccak256.py
rename to tests/functional/syntax/test_keccak256.py
diff --git a/tests/parser/syntax/test_len.py b/tests/functional/syntax/test_len.py
similarity index 62%
rename from tests/parser/syntax/test_len.py
rename to tests/functional/syntax/test_len.py
index bbde7e4897..b8cc61df1d 100644
--- a/tests/parser/syntax/test_len.py
+++ b/tests/functional/syntax/test_len.py
@@ -1,7 +1,6 @@
import pytest
-from pytest import raises
-from vyper import compiler
+from vyper import compile_code
from vyper.exceptions import TypeMismatch
fail_list = [
@@ -21,11 +20,11 @@ def foo(inp: int128) -> uint256:
@pytest.mark.parametrize("bad_code", fail_list)
def test_block_fail(bad_code):
if isinstance(bad_code, tuple):
- with raises(bad_code[1]):
- compiler.compile_code(bad_code[0])
+ with pytest.raises(bad_code[1]):
+ compile_code(bad_code[0])
else:
- with raises(TypeMismatch):
- compiler.compile_code(bad_code)
+ with pytest.raises(TypeMismatch):
+ compile_code(bad_code)
valid_list = [
@@ -39,9 +38,18 @@ def foo(inp: Bytes[10]) -> uint256:
def foo(inp: String[10]) -> uint256:
return len(inp)
""",
+ """
+BAR: constant(String[5]) = "vyper"
+FOO: constant(uint256) = len(BAR)
+
+@external
+def foo() -> uint256:
+ a: uint256 = FOO
+ return a
+ """,
]
@pytest.mark.parametrize("good_code", valid_list)
def test_list_success(good_code):
- assert compiler.compile_code(good_code) is not None
+ assert compile_code(good_code) is not None
diff --git a/tests/parser/syntax/test_list.py b/tests/functional/syntax/test_list.py
similarity index 98%
rename from tests/parser/syntax/test_list.py
rename to tests/functional/syntax/test_list.py
index 3f81b911c8..db41de5526 100644
--- a/tests/parser/syntax/test_list.py
+++ b/tests/functional/syntax/test_list.py
@@ -305,8 +305,9 @@ def foo():
"""
@external
def foo():
+ x: DynArray[uint256, 3] = [1, 2, 3]
for i in [[], []]:
- pass
+ x = i
""",
]
diff --git a/tests/parser/syntax/test_logging.py b/tests/functional/syntax/test_logging.py
similarity index 100%
rename from tests/parser/syntax/test_logging.py
rename to tests/functional/syntax/test_logging.py
diff --git a/tests/functional/syntax/test_method_id.py b/tests/functional/syntax/test_method_id.py
new file mode 100644
index 0000000000..849c1b0d55
--- /dev/null
+++ b/tests/functional/syntax/test_method_id.py
@@ -0,0 +1,50 @@
+import pytest
+
+from vyper import compile_code
+from vyper.exceptions import InvalidLiteral, InvalidType
+
+fail_list = [
+ (
+ """
+@external
+def foo():
+ a: Bytes[4] = method_id("bar ()")
+ """,
+ InvalidLiteral,
+ ),
+ (
+ """
+FOO: constant(Bytes[4]) = method_id(1)
+ """,
+ InvalidType,
+ ),
+ (
+ """
+FOO: constant(Bytes[4]) = method_id("bar ()")
+ """,
+ InvalidLiteral,
+ ),
+]
+
+
+@pytest.mark.parametrize("bad_code,exc", fail_list)
+def test_method_id_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compile_code(bad_code)
+
+
+valid_list = [
+ """
+FOO: constant(String[5]) = "foo()"
+BAR: constant(Bytes[4]) = method_id(FOO)
+
+@external
+def foo(a: Bytes[4] = BAR):
+ pass
+ """
+]
+
+
+@pytest.mark.parametrize("code", valid_list)
+def test_method_id_pass(code):
+ assert compile_code(code) is not None
diff --git a/tests/functional/syntax/test_minmax.py b/tests/functional/syntax/test_minmax.py
new file mode 100644
index 0000000000..78ee74635c
--- /dev/null
+++ b/tests/functional/syntax/test_minmax.py
@@ -0,0 +1,64 @@
+import pytest
+
+from vyper import compile_code
+from vyper.exceptions import InvalidType, OverflowException, TypeMismatch
+
+fail_list = [
+ (
+ """
+@external
+def foo():
+ y: int128 = min(7, 0x1234567890123456789012345678901234567890)
+ """,
+ InvalidType,
+ ),
+ (
+ """
+@external
+def foo():
+ a: int16 = min(min_value(int16), max_value(int8))
+ """,
+ TypeMismatch,
+ ),
+ (
+ """
+@external
+def foo():
+ a: decimal = min(1.0, 18707220957835557353007165858768422651595.9365500928)
+ """,
+ OverflowException,
+ ),
+]
+
+
+@pytest.mark.parametrize("bad_code,exc", fail_list)
+def test_block_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compile_code(bad_code)
+
+
+valid_list = [
+ """
+FOO: constant(uint256) = 123
+BAR: constant(uint256) = 456
+BAZ: constant(uint256) = min(FOO, BAR)
+
+@external
+def foo():
+ a: uint256 = BAZ
+ """,
+ """
+FOO: constant(uint256) = 123
+BAR: constant(uint256) = 456
+BAZ: constant(uint256) = max(FOO, BAR)
+
+@external
+def foo():
+ a: uint256 = BAZ
+ """,
+]
+
+
+@pytest.mark.parametrize("good_code", valid_list)
+def test_block_success(good_code):
+ assert compile_code(good_code) is not None
diff --git a/tests/functional/syntax/test_minmax_value.py b/tests/functional/syntax/test_minmax_value.py
new file mode 100644
index 0000000000..8cc3370b42
--- /dev/null
+++ b/tests/functional/syntax/test_minmax_value.py
@@ -0,0 +1,39 @@
+import pytest
+
+from vyper import compile_code
+from vyper.exceptions import InvalidType
+
+fail_list = [
+ (
+ """
+@external
+def foo():
+ a: address = min_value(address)
+ """,
+ InvalidType,
+ ),
+ (
+ """
+@external
+def foo():
+ a: address = max_value(address)
+ """,
+ InvalidType,
+ ),
+ (
+ """
+FOO: constant(address) = min_value(address)
+
+@external
+def foo():
+ a: address = FOO
+ """,
+ InvalidType,
+ ),
+]
+
+
+@pytest.mark.parametrize("bad_code,exc", fail_list)
+def test_block_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compile_code(bad_code)
diff --git a/tests/parser/syntax/test_msg_data.py b/tests/functional/syntax/test_msg_data.py
similarity index 100%
rename from tests/parser/syntax/test_msg_data.py
rename to tests/functional/syntax/test_msg_data.py
diff --git a/tests/parser/syntax/test_nested_list.py b/tests/functional/syntax/test_nested_list.py
similarity index 100%
rename from tests/parser/syntax/test_nested_list.py
rename to tests/functional/syntax/test_nested_list.py
diff --git a/tests/parser/syntax/test_no_none.py b/tests/functional/syntax/test_no_none.py
similarity index 79%
rename from tests/parser/syntax/test_no_none.py
rename to tests/functional/syntax/test_no_none.py
index 7030a56b18..085ce395ab 100644
--- a/tests/parser/syntax/test_no_none.py
+++ b/tests/functional/syntax/test_no_none.py
@@ -30,13 +30,13 @@ def foo():
"""
@external
def foo():
- bar: bytes32 = EMPTY_BYTES32
+ bar: bytes32 = empty(bytes32)
bar = None
""",
"""
@external
def foo():
- bar: address = ZERO_ADDRESS
+ bar: address = empty(address)
bar = None
""",
"""
@@ -72,7 +72,9 @@ def foo():
]
for contract in contracts:
- assert_compile_failed(lambda: get_contract_with_gas_estimation(contract), InvalidLiteral)
+ assert_compile_failed(
+ lambda c=contract: get_contract_with_gas_estimation(c), InvalidLiteral
+ )
def test_no_is_none(assert_compile_failed, get_contract_with_gas_estimation):
@@ -104,19 +106,21 @@ def foo():
"""
@external
def foo():
- bar: bytes32 = EMPTY_BYTES32
+ bar: bytes32 = empty(bytes32)
assert bar is None
""",
"""
@external
def foo():
- bar: address = ZERO_ADDRESS
+ bar: address = empty(address)
assert bar is None
""",
]
for contract in contracts:
- assert_compile_failed(lambda: get_contract_with_gas_estimation(contract), SyntaxException)
+ assert_compile_failed(
+ lambda c=contract: get_contract_with_gas_estimation(c), SyntaxException
+ )
def test_no_eq_none(assert_compile_failed, get_contract_with_gas_estimation):
@@ -148,19 +152,21 @@ def foo():
"""
@external
def foo():
- bar: bytes32 = EMPTY_BYTES32
+ bar: bytes32 = empty(bytes32)
assert bar == None
""",
"""
@external
def foo():
- bar: address = ZERO_ADDRESS
+ bar: address = empty(address)
assert bar == None
""",
]
for contract in contracts:
- assert_compile_failed(lambda: get_contract_with_gas_estimation(contract), InvalidLiteral)
+ assert_compile_failed(
+ lambda c=contract: get_contract_with_gas_estimation(c), InvalidLiteral
+ )
def test_struct_none(assert_compile_failed, get_contract_with_gas_estimation):
@@ -195,4 +201,6 @@ def foo():
]
for contract in contracts:
- assert_compile_failed(lambda: get_contract_with_gas_estimation(contract), InvalidLiteral)
+ assert_compile_failed(
+ lambda c=contract: get_contract_with_gas_estimation(c), InvalidLiteral
+ )
diff --git a/tests/functional/syntax/test_powmod.py b/tests/functional/syntax/test_powmod.py
new file mode 100644
index 0000000000..12ea23152c
--- /dev/null
+++ b/tests/functional/syntax/test_powmod.py
@@ -0,0 +1,39 @@
+import pytest
+
+from vyper import compile_code
+from vyper.exceptions import InvalidType
+
+fail_list = [
+ (
+ """
+@external
+def foo():
+ a: uint256 = pow_mod256(-1, -1)
+ """,
+ InvalidType,
+ )
+]
+
+
+@pytest.mark.parametrize("bad_code,exc", fail_list)
+def test_powmod_fail(bad_code, exc):
+ with pytest.raises(exc):
+ compile_code(bad_code)
+
+
+valid_list = [
+ """
+FOO: constant(uint256) = 3
+BAR: constant(uint256) = 5
+BAZ: constant(uint256) = pow_mod256(FOO, BAR)
+
+@external
+def foo():
+ a: uint256 = BAZ
+ """
+]
+
+
+@pytest.mark.parametrize("code", valid_list)
+def test_powmod_pass(code):
+ assert compile_code(code) is not None
diff --git a/tests/parser/syntax/test_print.py b/tests/functional/syntax/test_print.py
similarity index 100%
rename from tests/parser/syntax/test_print.py
rename to tests/functional/syntax/test_print.py
diff --git a/tests/parser/syntax/test_public.py b/tests/functional/syntax/test_public.py
similarity index 93%
rename from tests/parser/syntax/test_public.py
rename to tests/functional/syntax/test_public.py
index 68575ebd41..71bff753f4 100644
--- a/tests/parser/syntax/test_public.py
+++ b/tests/functional/syntax/test_public.py
@@ -30,9 +30,9 @@ def foo() -> int128:
x: public(HashMap[uint256, Foo])
""",
- # expansion of public user-defined enum
+ # expansion of public user-defined flag
"""
-enum Foo:
+flag Foo:
BAR
x: public(HashMap[uint256, Foo])
diff --git a/tests/parser/syntax/test_raw_call.py b/tests/functional/syntax/test_raw_call.py
similarity index 81%
rename from tests/parser/syntax/test_raw_call.py
rename to tests/functional/syntax/test_raw_call.py
index b1286e7a8e..c0b38d1d1e 100644
--- a/tests/parser/syntax/test_raw_call.py
+++ b/tests/functional/syntax/test_raw_call.py
@@ -1,6 +1,6 @@
import pytest
-from vyper import compiler
+from vyper import compile_code
from vyper.exceptions import ArgumentException, InvalidType, SyntaxException, TypeMismatch
fail_list = [
@@ -39,7 +39,7 @@ def foo():
@pytest.mark.parametrize("bad_code,exc", fail_list)
def test_raw_call_fail(bad_code, exc):
with pytest.raises(exc):
- compiler.compile_code(bad_code)
+ compile_code(bad_code)
valid_list = [
@@ -90,9 +90,23 @@ def foo():
value=self.balance - self.balances[0]
)
""",
+ # test constants
+ """
+OUTSIZE: constant(uint256) = 4
+REVERT_ON_FAILURE: constant(bool) = True
+@external
+def foo():
+ x: Bytes[9] = raw_call(
+ 0x1234567890123456789012345678901234567890,
+ b"cow",
+ max_outsize=OUTSIZE,
+ gas=595757,
+ revert_on_failure=REVERT_ON_FAILURE
+ )
+ """,
]
@pytest.mark.parametrize("good_code", valid_list)
def test_raw_call_success(good_code):
- assert compiler.compile_code(good_code) is not None
+ assert compile_code(good_code) is not None
diff --git a/tests/parser/syntax/test_return_tuple.py b/tests/functional/syntax/test_return_tuple.py
similarity index 100%
rename from tests/parser/syntax/test_return_tuple.py
rename to tests/functional/syntax/test_return_tuple.py
diff --git a/tests/parser/syntax/test_self_balance.py b/tests/functional/syntax/test_self_balance.py
similarity index 88%
rename from tests/parser/syntax/test_self_balance.py
rename to tests/functional/syntax/test_self_balance.py
index 63db58e347..d22d8a2750 100644
--- a/tests/parser/syntax/test_self_balance.py
+++ b/tests/functional/syntax/test_self_balance.py
@@ -20,7 +20,7 @@ def __default__():
pass
"""
settings = Settings(evm_version=evm_version)
- opcodes = compiler.compile_code(code, ["opcodes"], settings=settings)["opcodes"]
+ opcodes = compiler.compile_code(code, output_formats=["opcodes"], settings=settings)["opcodes"]
if EVM_VERSIONS[evm_version] >= EVM_VERSIONS["istanbul"]:
assert "SELFBALANCE" in opcodes
else:
diff --git a/tests/parser/syntax/test_selfdestruct.py b/tests/functional/syntax/test_selfdestruct.py
similarity index 100%
rename from tests/parser/syntax/test_selfdestruct.py
rename to tests/functional/syntax/test_selfdestruct.py
diff --git a/tests/parser/syntax/test_send.py b/tests/functional/syntax/test_send.py
similarity index 100%
rename from tests/parser/syntax/test_send.py
rename to tests/functional/syntax/test_send.py
diff --git a/tests/parser/syntax/test_slice.py b/tests/functional/syntax/test_slice.py
similarity index 100%
rename from tests/parser/syntax/test_slice.py
rename to tests/functional/syntax/test_slice.py
diff --git a/tests/parser/syntax/test_string.py b/tests/functional/syntax/test_string.py
similarity index 100%
rename from tests/parser/syntax/test_string.py
rename to tests/functional/syntax/test_string.py
diff --git a/tests/parser/syntax/test_structs.py b/tests/functional/syntax/test_structs.py
similarity index 100%
rename from tests/parser/syntax/test_structs.py
rename to tests/functional/syntax/test_structs.py
diff --git a/tests/parser/syntax/test_ternary.py b/tests/functional/syntax/test_ternary.py
similarity index 97%
rename from tests/parser/syntax/test_ternary.py
rename to tests/functional/syntax/test_ternary.py
index 325be3e43b..6a2bb9c072 100644
--- a/tests/parser/syntax/test_ternary.py
+++ b/tests/functional/syntax/test_ternary.py
@@ -1,6 +1,6 @@
import pytest
-from vyper.compiler import compile_code
+from vyper import compile_code
from vyper.exceptions import InvalidType, TypeMismatch
good_list = [
@@ -82,7 +82,7 @@ def foo() -> uint256:
def foo() -> uint256:
return 1 if TEST else 2
""",
- InvalidType,
+ TypeMismatch,
),
( # bad test type: variable
"""
diff --git a/tests/parser/syntax/test_tuple_assign.py b/tests/functional/syntax/test_tuple_assign.py
similarity index 98%
rename from tests/parser/syntax/test_tuple_assign.py
rename to tests/functional/syntax/test_tuple_assign.py
index 115499ce8b..49b63ee614 100644
--- a/tests/parser/syntax/test_tuple_assign.py
+++ b/tests/functional/syntax/test_tuple_assign.py
@@ -41,7 +41,7 @@ def out_literals() -> (int128, int128, Bytes[10]):
@external
def test() -> (int128, address, Bytes[10]):
a: int128 = 0
- b: address = ZERO_ADDRESS
+ b: address = empty(address)
a, b = self.out_literals() # tuple count mismatch
return
""",
diff --git a/tests/functional/syntax/test_uint2str.py b/tests/functional/syntax/test_uint2str.py
new file mode 100644
index 0000000000..9e6dde30cc
--- /dev/null
+++ b/tests/functional/syntax/test_uint2str.py
@@ -0,0 +1,19 @@
+import pytest
+
+from vyper import compile_code
+
+valid_list = [
+ """
+FOO: constant(uint256) = 3
+BAR: constant(String[78]) = uint2str(FOO)
+
+@external
+def foo():
+ a: String[78] = BAR
+ """
+]
+
+
+@pytest.mark.parametrize("code", valid_list)
+def test_addmulmod_pass(code):
+ assert compile_code(code) is not None
diff --git a/tests/functional/syntax/test_unary.py b/tests/functional/syntax/test_unary.py
new file mode 100644
index 0000000000..5942ee15db
--- /dev/null
+++ b/tests/functional/syntax/test_unary.py
@@ -0,0 +1,21 @@
+import pytest
+
+from vyper import compile_code
+from vyper.exceptions import InvalidType
+
+fail_list = [
+ (
+ """
+@external
+def foo() -> int128:
+ return -2**127
+ """,
+ InvalidType,
+ )
+]
+
+
+@pytest.mark.parametrize("code,exc", fail_list)
+def test_unary_fail(code, exc):
+ with pytest.raises(exc):
+ compile_code(code)
diff --git a/tests/parser/syntax/test_unbalanced_return.py b/tests/functional/syntax/test_unbalanced_return.py
similarity index 97%
rename from tests/parser/syntax/test_unbalanced_return.py
rename to tests/functional/syntax/test_unbalanced_return.py
index 5337b4b677..d1d9732777 100644
--- a/tests/parser/syntax/test_unbalanced_return.py
+++ b/tests/functional/syntax/test_unbalanced_return.py
@@ -56,7 +56,7 @@ def valid_address(sender: address) -> bool:
"""
@internal
def valid_address(sender: address) -> bool:
- if sender == ZERO_ADDRESS:
+ if sender == empty(address):
selfdestruct(sender)
_sender: address = sender
else:
@@ -144,7 +144,7 @@ def test() -> int128:
"""
@external
def test() -> int128:
- x: bytes32 = EMPTY_BYTES32
+ x: bytes32 = empty(bytes32)
if False:
if False:
return 0
diff --git a/tests/parser/features/test_reverting.py b/tests/parser/features/test_reverting.py
deleted file mode 100644
index 2cdc727015..0000000000
--- a/tests/parser/features/test_reverting.py
+++ /dev/null
@@ -1,59 +0,0 @@
-import pytest
-from eth.codecs import abi
-from eth_tester.exceptions import TransactionFailed
-
-from vyper.utils import method_id
-
-pytestmark = pytest.mark.usefixtures("memory_mocker")
-
-
-def test_revert_reason(w3, assert_tx_failed, get_contract_with_gas_estimation):
- reverty_code = """
-@external
-def foo():
- data: Bytes[4] = method_id("NoFives()")
- raw_revert(data)
- """
-
- revert_bytes = method_id("NoFives()")
-
- assert_tx_failed(
- lambda: get_contract_with_gas_estimation(reverty_code).foo(transact={}),
- TransactionFailed,
- exc_text=f"execution reverted: {revert_bytes}",
- )
-
-
-def test_revert_reason_typed(w3, assert_tx_failed, get_contract_with_gas_estimation):
- reverty_code = """
-@external
-def foo():
- val: uint256 = 5
- data: Bytes[100] = _abi_encode(val, method_id=method_id("NoFives(uint256)"))
- raw_revert(data)
- """
-
- revert_bytes = method_id("NoFives(uint256)") + abi.encode("(uint256)", (5,))
-
- assert_tx_failed(
- lambda: get_contract_with_gas_estimation(reverty_code).foo(transact={}),
- TransactionFailed,
- exc_text=f"execution reverted: {revert_bytes}",
- )
-
-
-def test_revert_reason_typed_no_variable(w3, assert_tx_failed, get_contract_with_gas_estimation):
- reverty_code = """
-@external
-def foo():
- val: uint256 = 5
- raw_revert(_abi_encode(val, method_id=method_id("NoFives(uint256)")))
- """
-
- revert_bytes = method_id("NoFives(uint256)") + abi.encode("(uint256)", (5,))
-
- assert_tx_failed(
- lambda: get_contract_with_gas_estimation(reverty_code).foo(transact={}),
- TransactionFailed,
- exc_text=f"execution reverted: {revert_bytes}",
- )
diff --git a/tests/parser/functions/test_as_wei_value.py b/tests/parser/functions/test_as_wei_value.py
deleted file mode 100644
index bab0aed616..0000000000
--- a/tests/parser/functions/test_as_wei_value.py
+++ /dev/null
@@ -1,31 +0,0 @@
-def test_ext_call(w3, side_effects_contract, assert_side_effects_invoked, get_contract):
- code = """
-@external
-def foo(a: Foo) -> uint256:
- return as_wei_value(a.foo(7), "ether")
-
-interface Foo:
- def foo(x: uint8) -> uint8: nonpayable
- """
-
- c1 = side_effects_contract("uint8")
- c2 = get_contract(code)
-
- assert c2.foo(c1.address) == w3.to_wei(7, "ether")
- assert_side_effects_invoked(c1, lambda: c2.foo(c1.address, transact={}))
-
-
-def test_internal_call(w3, get_contract_with_gas_estimation):
- code = """
-@external
-def foo() -> uint256:
- return as_wei_value(self.bar(), "ether")
-
-@internal
-def bar() -> uint8:
- return 7
- """
-
- c = get_contract_with_gas_estimation(code)
-
- assert c.foo() == w3.to_wei(7, "ether")
diff --git a/tests/parser/syntax/test_as_wei_value.py b/tests/parser/syntax/test_as_wei_value.py
deleted file mode 100644
index a5232a5c9a..0000000000
--- a/tests/parser/syntax/test_as_wei_value.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import pytest
-
-from vyper.exceptions import ArgumentException, InvalidType, StructureException
-
-fail_list = [
- (
- """
-@external
-def foo():
- x: int128 = as_wei_value(5, szabo)
- """,
- ArgumentException,
- ),
- (
- """
-@external
-def foo() -> int128:
- x: int128 = 45
- return x.balance
- """,
- StructureException,
- ),
- (
- """
-@external
-def foo():
- x: int128 = as_wei_value(0xf5, "szabo")
- """,
- InvalidType,
- ),
-]
-
-
-@pytest.mark.parametrize("bad_code,exc", fail_list)
-def test_as_wei_fail(get_contract_with_gas_estimation, bad_code, exc, assert_compile_failed):
- assert_compile_failed(lambda: get_contract_with_gas_estimation(bad_code), exc)
-
-
-valid_list = [
- """
-@external
-def foo():
- x: uint256 = as_wei_value(5, "finney") + as_wei_value(2, "babbage") + as_wei_value(8, "shannon") # noqa: E501
- """,
- """
-@external
-def foo():
- z: int128 = 2 + 3
- x: uint256 = as_wei_value(2 + 3, "finney")
- """,
- """
-@external
-def foo():
- x: uint256 = as_wei_value(5.182, "babbage")
- """,
- """
-@external
-def foo() -> uint256:
- x: address = 0x1234567890123456789012345678901234567890
- return x.balance
- """,
-]
-
-
-@pytest.mark.parametrize("good_code", valid_list)
-def test_as_wei_success(good_code, get_contract_with_gas_estimation):
- assert get_contract_with_gas_estimation(good_code) is not None
diff --git a/tests/parser/syntax/test_for_range.py b/tests/parser/syntax/test_for_range.py
deleted file mode 100644
index e6f35c1d2d..0000000000
--- a/tests/parser/syntax/test_for_range.py
+++ /dev/null
@@ -1,78 +0,0 @@
-import pytest
-
-from vyper import compiler
-from vyper.exceptions import StructureException
-
-fail_list = [
- (
- """
-@external
-def foo():
- for a[1] in range(10):
- pass
- """,
- StructureException,
- ),
- (
- """
-@external
-def bar():
- for i in range(1,2,bound=2):
- pass
- """,
- StructureException,
- ),
- (
- """
-@external
-def bar():
- x:uint256 = 1
- for i in range(x,x+1,bound=2):
- pass
- """,
- StructureException,
- ),
-]
-
-
-@pytest.mark.parametrize("bad_code", fail_list)
-def test_range_fail(bad_code):
- with pytest.raises(bad_code[1]):
- compiler.compile_code(bad_code[0])
-
-
-valid_list = [
- """
-@external
-def foo():
- for i in range(10):
- pass
- """,
- """
-@external
-def foo():
- for i in range(10, 20):
- pass
- """,
- """
-@external
-def foo():
- x: int128 = 5
- for i in range(x, x + 10):
- pass
- """,
- """
-interface Foo:
- def kick(): nonpayable
-foos: Foo[3]
-@external
-def kick_foos():
- for foo in self.foos:
- foo.kick()
- """,
-]
-
-
-@pytest.mark.parametrize("good_code", valid_list)
-def test_range_success(good_code):
- assert compiler.compile_code(good_code) is not None
diff --git a/tests/parser/syntax/test_minmax.py b/tests/parser/syntax/test_minmax.py
deleted file mode 100644
index 2ad3d363f1..0000000000
--- a/tests/parser/syntax/test_minmax.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import pytest
-
-from vyper.exceptions import InvalidType, TypeMismatch
-
-fail_list = [
- (
- """
-@external
-def foo():
- y: int128 = min(7, 0x1234567890123456789012345678901234567890)
- """,
- InvalidType,
- ),
- (
- """
-@external
-def foo():
- a: int16 = min(min_value(int16), max_value(int8))
- """,
- TypeMismatch,
- ),
-]
-
-
-@pytest.mark.parametrize("bad_code,exc", fail_list)
-def test_block_fail(assert_compile_failed, get_contract_with_gas_estimation, bad_code, exc):
- assert_compile_failed(lambda: get_contract_with_gas_estimation(bad_code), exc)
diff --git a/tests/parser/syntax/test_minmax_value.py b/tests/parser/syntax/test_minmax_value.py
deleted file mode 100644
index e154cad23f..0000000000
--- a/tests/parser/syntax/test_minmax_value.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import pytest
-
-from vyper.exceptions import InvalidType
-
-fail_list = [
- """
-@external
-def foo():
- a: address = min_value(address)
- """,
- """
-@external
-def foo():
- a: address = max_value(address)
- """,
-]
-
-
-@pytest.mark.parametrize("bad_code", fail_list)
-def test_block_fail(assert_compile_failed, get_contract_with_gas_estimation, bad_code):
- assert_compile_failed(lambda: get_contract_with_gas_estimation(bad_code), InvalidType)
diff --git a/tests/parser/test_selector_table.py b/tests/parser/test_selector_table.py
deleted file mode 100644
index 01a83698b7..0000000000
--- a/tests/parser/test_selector_table.py
+++ /dev/null
@@ -1,198 +0,0 @@
-import hypothesis.strategies as st
-import pytest
-from hypothesis import given, settings
-
-import vyper.utils as utils
-from vyper.codegen.jumptable_utils import (
- generate_dense_jumptable_info,
- generate_sparse_jumptable_buckets,
-)
-from vyper.compiler.settings import OptimizationLevel
-
-
-@given(
- n_methods=st.integers(min_value=1, max_value=100),
- seed=st.integers(min_value=0, max_value=2**64 - 1),
-)
-@pytest.mark.fuzzing
-@settings(max_examples=10, deadline=None)
-def test_sparse_jumptable_probe_depth(n_methods, seed):
- sigs = [f"foo{i + seed}()" for i in range(n_methods)]
- _, buckets = generate_sparse_jumptable_buckets(sigs)
- bucket_sizes = [len(bucket) for bucket in buckets.values()]
-
- # generally bucket sizes should be bounded at around 4, but
- # just test that they don't get really out of hand
- assert max(bucket_sizes) <= 8
-
- # generally mean bucket size should be around 1.6, here just
- # test they don't get really out of hand
- assert sum(bucket_sizes) / len(bucket_sizes) <= 4
-
-
-@given(
- n_methods=st.integers(min_value=4, max_value=100),
- seed=st.integers(min_value=0, max_value=2**64 - 1),
-)
-@pytest.mark.fuzzing
-@settings(max_examples=10, deadline=None)
-def test_dense_jumptable_bucket_size(n_methods, seed):
- sigs = [f"foo{i + seed}()" for i in range(n_methods)]
- n = len(sigs)
- buckets = generate_dense_jumptable_info(sigs)
- n_buckets = len(buckets)
-
- # generally should be around 14 buckets per 100 methods, here
- # we test they don't get really out of hand
- assert n_buckets / n < 0.4 or n < 10
-
-
-@pytest.mark.parametrize("opt_level", list(OptimizationLevel))
-# dense selector table packing boundaries at 256 and 65336
-@pytest.mark.parametrize("max_calldata_bytes", [255, 256, 65336])
-@settings(max_examples=5, deadline=None)
-@given(
- seed=st.integers(min_value=0, max_value=2**64 - 1),
- max_default_args=st.integers(min_value=0, max_value=4),
- default_fn_mutability=st.sampled_from(["", "@pure", "@view", "@nonpayable", "@payable"]),
-)
-@pytest.mark.fuzzing
-def test_selector_table_fuzz(
- max_calldata_bytes,
- seed,
- max_default_args,
- opt_level,
- default_fn_mutability,
- w3,
- get_contract,
- assert_tx_failed,
- get_logs,
-):
- def abi_sig(calldata_words, i, n_default_args):
- args = [] if not calldata_words else [f"uint256[{calldata_words}]"]
- args.extend(["uint256"] * n_default_args)
- argstr = ",".join(args)
- return f"foo{seed + i}({argstr})"
-
- def generate_func_def(mutability, calldata_words, i, n_default_args):
- arglist = [] if not calldata_words else [f"x: uint256[{calldata_words}]"]
- for j in range(n_default_args):
- arglist.append(f"x{j}: uint256 = 0")
- args = ", ".join(arglist)
- _log_return = f"log _Return({i})" if mutability == "@payable" else ""
-
- return f"""
-@external
-{mutability}
-def foo{seed + i}({args}) -> uint256:
- {_log_return}
- return {i}
- """
-
- @given(
- methods=st.lists(
- st.tuples(
- st.sampled_from(["@pure", "@view", "@nonpayable", "@payable"]),
- st.integers(min_value=0, max_value=max_calldata_bytes // 32),
- # n bytes to strip from calldata
- st.integers(min_value=1, max_value=4),
- # n default args
- st.integers(min_value=0, max_value=max_default_args),
- ),
- min_size=1,
- max_size=100,
- )
- )
- @settings(max_examples=25)
- def _test(methods):
- func_defs = "\n".join(
- generate_func_def(m, s, i, d) for i, (m, s, _, d) in enumerate(methods)
- )
-
- if default_fn_mutability == "":
- default_fn_code = ""
- elif default_fn_mutability in ("@nonpayable", "@payable"):
- default_fn_code = f"""
-@external
-{default_fn_mutability}
-def __default__():
- log CalledDefault()
- """
- else:
- # can't log from pure/view functions, just test that it returns
- default_fn_code = """
-@external
-def __default__():
- pass
- """
-
- code = f"""
-event CalledDefault:
- pass
-
-event _Return:
- val: uint256
-
-{func_defs}
-
-{default_fn_code}
- """
-
- c = get_contract(code, override_opt_level=opt_level)
-
- for i, (mutability, n_calldata_words, n_strip_bytes, n_default_args) in enumerate(methods):
- funcname = f"foo{seed + i}"
- func = getattr(c, funcname)
-
- for j in range(n_default_args + 1):
- args = [[1] * n_calldata_words] if n_calldata_words else []
- args.extend([1] * j)
-
- # check the function returns as expected
- assert func(*args) == i
-
- method_id = utils.method_id(abi_sig(n_calldata_words, i, j))
-
- argsdata = b"\x00" * (n_calldata_words * 32 + j * 32)
-
- # do payable check
- if mutability == "@payable":
- tx = func(*args, transact={"value": 1})
- (event,) = get_logs(tx, c, "_Return")
- assert event.args.val == i
- else:
- hexstr = (method_id + argsdata).hex()
- txdata = {"to": c.address, "data": hexstr, "value": 1}
- assert_tx_failed(lambda: w3.eth.send_transaction(txdata))
-
- # now do calldatasize check
- # strip some bytes
- calldata = (method_id + argsdata)[:-n_strip_bytes]
- hexstr = calldata.hex()
- tx_params = {"to": c.address, "data": hexstr}
- if n_calldata_words == 0 and j == 0:
- # no args, hit default function
- if default_fn_mutability == "":
- assert_tx_failed(lambda: w3.eth.send_transaction(tx_params))
- elif default_fn_mutability == "@payable":
- # we should be able to send eth to it
- tx_params["value"] = 1
- tx = w3.eth.send_transaction(tx_params)
- logs = get_logs(tx, c, "CalledDefault")
- assert len(logs) == 1
- else:
- tx = w3.eth.send_transaction(tx_params)
-
- # note: can't emit logs from view/pure functions,
- # so the logging is not tested.
- if default_fn_mutability == "@nonpayable":
- logs = get_logs(tx, c, "CalledDefault")
- assert len(logs) == 1
-
- # check default function reverts
- tx_params["value"] = 1
- assert_tx_failed(lambda: w3.eth.send_transaction(tx_params))
- else:
- assert_tx_failed(lambda: w3.eth.send_transaction(tx_params))
-
- _test()
diff --git a/vyper/builtins/interfaces/__init__.py b/tests/unit/__init__.py
similarity index 100%
rename from vyper/builtins/interfaces/__init__.py
rename to tests/unit/__init__.py
diff --git a/tests/unit/abi_types/test_invalid_abi_types.py b/tests/unit/abi_types/test_invalid_abi_types.py
new file mode 100644
index 0000000000..1a8a7db884
--- /dev/null
+++ b/tests/unit/abi_types/test_invalid_abi_types.py
@@ -0,0 +1,26 @@
+import pytest
+
+from vyper.abi_types import (
+ ABI_Bytes,
+ ABI_BytesM,
+ ABI_DynamicArray,
+ ABI_FixedMxN,
+ ABI_GIntM,
+ ABI_String,
+)
+from vyper.exceptions import InvalidABIType
+
+cases_invalid_types = [
+ (ABI_GIntM, ((0, False), (7, False), (300, True), (300, False))),
+ (ABI_FixedMxN, ((0, 0, False), (8, 0, False), (256, 81, True), (300, 80, False))),
+ (ABI_BytesM, ((0,), (33,), (-10,))),
+ (ABI_Bytes, ((-1,), (-69,))),
+ (ABI_DynamicArray, ((ABI_GIntM(256, False), -1), (ABI_String(256), -10))),
+]
+
+
+@pytest.mark.parametrize("typ,params_variants", cases_invalid_types)
+def test_invalid_abi_types(assert_compile_failed, typ, params_variants):
+ # double parametrization cannot work because the 2nd dimension is variable
+ for params in params_variants:
+ assert_compile_failed(lambda p=params: typ(*p), InvalidABIType)
diff --git a/tests/ast/nodes/test_binary.py b/tests/unit/ast/nodes/test_binary.py
similarity index 100%
rename from tests/ast/nodes/test_binary.py
rename to tests/unit/ast/nodes/test_binary.py
diff --git a/tests/ast/nodes/test_compare_nodes.py b/tests/unit/ast/nodes/test_compare_nodes.py
similarity index 100%
rename from tests/ast/nodes/test_compare_nodes.py
rename to tests/unit/ast/nodes/test_compare_nodes.py
diff --git a/tests/ast/nodes/test_evaluate_binop_decimal.py b/tests/unit/ast/nodes/test_fold_binop_decimal.py
similarity index 82%
rename from tests/ast/nodes/test_evaluate_binop_decimal.py
rename to tests/unit/ast/nodes/test_fold_binop_decimal.py
index c6c69626b8..e426a11de9 100644
--- a/tests/ast/nodes/test_evaluate_binop_decimal.py
+++ b/tests/unit/ast/nodes/test_fold_binop_decimal.py
@@ -13,14 +13,14 @@
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st_decimals, right=st_decimals)
@example(left=Decimal("0.9999999999"), right=Decimal("0.0000000001"))
@example(left=Decimal("0.0000000001"), right=Decimal("0.9999999999"))
@example(left=Decimal("0.9999999999"), right=Decimal("0.9999999999"))
@example(left=Decimal("0.0000000001"), right=Decimal("0.0000000001"))
@pytest.mark.parametrize("op", "+-*/%")
-def test_binop_decimal(get_contract, assert_tx_failed, op, left, right):
+def test_binop_decimal(get_contract, tx_failed, op, left, right):
source = f"""
@external
def foo(a: decimal, b: decimal) -> decimal:
@@ -31,7 +31,7 @@ def foo(a: decimal, b: decimal) -> decimal:
vyper_ast = vy_ast.parse_to_ast(f"{left} {op} {right}")
old_node = vyper_ast.body[0].value
try:
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
is_valid = True
except ZeroDivisionException:
is_valid = False
@@ -39,7 +39,8 @@ def foo(a: decimal, b: decimal) -> decimal:
if is_valid:
assert contract.foo(left, right) == new_node.value
else:
- assert_tx_failed(lambda: contract.foo(left, right))
+ with tx_failed():
+ contract.foo(left, right)
def test_binop_pow():
@@ -48,16 +49,16 @@ def test_binop_pow():
old_node = vyper_ast.body[0].value
with pytest.raises(TypeMismatch):
- old_node.evaluate()
+ old_node.get_folded_value()
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(
values=st.lists(st_decimals, min_size=2, max_size=10),
ops=st.lists(st.sampled_from("+-*/%"), min_size=11, max_size=11),
)
-def test_nested(get_contract, assert_tx_failed, values, ops):
+def test_nested(get_contract, tx_failed, values, ops):
variables = "abcdefghij"
input_value = ",".join(f"{i}: decimal" for i in variables[: len(values)])
return_value = " ".join(f"{a} {b}" for a, b in zip(variables[: len(values)], ops))
@@ -73,8 +74,8 @@ def foo({input_value}) -> decimal:
literal_op = literal_op.rsplit(maxsplit=1)[0]
vyper_ast = vy_ast.parse_to_ast(literal_op)
try:
- vy_ast.folding.replace_literal_ops(vyper_ast)
- expected = vyper_ast.body[0].value.value
+ new_node = vyper_ast.body[0].value.get_folded_value()
+ expected = new_node.value
is_valid = -(2**127) <= expected < 2**127
except (OverflowException, ZeroDivisionException):
# for overflow or division/modulus by 0, expect the contract call to revert
@@ -83,4 +84,5 @@ def foo({input_value}) -> decimal:
if is_valid:
assert contract.foo(*values) == expected
else:
- assert_tx_failed(lambda: contract.foo(*values))
+ with tx_failed():
+ contract.foo(*values)
diff --git a/tests/ast/nodes/test_evaluate_binop_int.py b/tests/unit/ast/nodes/test_fold_binop_int.py
similarity index 79%
rename from tests/ast/nodes/test_evaluate_binop_int.py
rename to tests/unit/ast/nodes/test_fold_binop_int.py
index d632a95461..904b36c167 100644
--- a/tests/ast/nodes/test_evaluate_binop_int.py
+++ b/tests/unit/ast/nodes/test_fold_binop_int.py
@@ -9,14 +9,14 @@
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st_int32, right=st_int32)
@example(left=1, right=1)
@example(left=1, right=-1)
@example(left=-1, right=1)
@example(left=-1, right=-1)
@pytest.mark.parametrize("op", "+-*/%")
-def test_binop_int128(get_contract, assert_tx_failed, op, left, right):
+def test_binop_int128(get_contract, tx_failed, op, left, right):
source = f"""
@external
def foo(a: int128, b: int128) -> int128:
@@ -27,7 +27,7 @@ def foo(a: int128, b: int128) -> int128:
vyper_ast = vy_ast.parse_to_ast(f"{left} {op} {right}")
old_node = vyper_ast.body[0].value
try:
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
is_valid = True
except ZeroDivisionException:
is_valid = False
@@ -35,17 +35,18 @@ def foo(a: int128, b: int128) -> int128:
if is_valid:
assert contract.foo(left, right) == new_node.value
else:
- assert_tx_failed(lambda: contract.foo(left, right))
+ with tx_failed():
+ contract.foo(left, right)
st_uint64 = st.integers(min_value=0, max_value=2**64)
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st_uint64, right=st_uint64)
@pytest.mark.parametrize("op", "+-*/%")
-def test_binop_uint256(get_contract, assert_tx_failed, op, left, right):
+def test_binop_uint256(get_contract, tx_failed, op, left, right):
source = f"""
@external
def foo(a: uint256, b: uint256) -> uint256:
@@ -56,7 +57,7 @@ def foo(a: uint256, b: uint256) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"{left} {op} {right}")
old_node = vyper_ast.body[0].value
try:
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
is_valid = new_node.value >= 0
except ZeroDivisionException:
is_valid = False
@@ -64,12 +65,13 @@ def foo(a: uint256, b: uint256) -> uint256:
if is_valid:
assert contract.foo(left, right) == new_node.value
else:
- assert_tx_failed(lambda: contract.foo(left, right))
+ with tx_failed():
+ contract.foo(left, right)
@pytest.mark.xfail(reason="need to implement safe exponentiation logic")
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st.integers(min_value=2, max_value=245), right=st.integers(min_value=0, max_value=16))
@example(left=0, right=0)
@example(left=0, right=1)
@@ -83,18 +85,18 @@ def foo(a: uint256, b: uint256) -> uint256:
vyper_ast = vy_ast.parse_to_ast(f"{left} ** {right}")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
assert contract.foo(left, right) == new_node.value
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(
values=st.lists(st.integers(min_value=-256, max_value=256), min_size=2, max_size=10),
ops=st.lists(st.sampled_from("+-*/%"), min_size=11, max_size=11),
)
-def test_binop_nested(get_contract, assert_tx_failed, values, ops):
+def test_binop_nested(get_contract, tx_failed, values, ops):
variables = "abcdefghij"
input_value = ",".join(f"{i}: int128" for i in variables[: len(values)])
return_value = " ".join(f"{a} {b}" for a, b in zip(variables[: len(values)], ops))
@@ -113,8 +115,8 @@ def foo({input_value}) -> int128:
vyper_ast = vy_ast.parse_to_ast(literal_op)
try:
- vy_ast.folding.replace_literal_ops(vyper_ast)
- expected = vyper_ast.body[0].value.value
+ new_node = vyper_ast.body[0].value.get_folded_value()
+ expected = new_node.value
is_valid = True
except ZeroDivisionException:
is_valid = False
@@ -122,4 +124,5 @@ def foo({input_value}) -> int128:
if is_valid:
assert contract.foo(*values) == expected
else:
- assert_tx_failed(lambda: contract.foo(*values))
+ with tx_failed():
+ contract.foo(*values)
diff --git a/tests/ast/nodes/test_evaluate_boolop.py b/tests/unit/ast/nodes/test_fold_boolop.py
similarity index 88%
rename from tests/ast/nodes/test_evaluate_boolop.py
rename to tests/unit/ast/nodes/test_fold_boolop.py
index 6bd9ecc6cb..3c42da0d26 100644
--- a/tests/ast/nodes/test_evaluate_boolop.py
+++ b/tests/unit/ast/nodes/test_fold_boolop.py
@@ -8,7 +8,7 @@
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(values=st.lists(st.booleans(), min_size=2, max_size=10))
@pytest.mark.parametrize("comparator", ["and", "or"])
def test_boolop_simple(get_contract, values, comparator):
@@ -26,13 +26,13 @@ def foo({input_value}) -> bool:
vyper_ast = vy_ast.parse_to_ast(literal_op)
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
assert contract.foo(*values) == new_node.value
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(
values=st.lists(st.booleans(), min_size=2, max_size=10),
comparators=st.lists(st.sampled_from(["and", "or"]), min_size=11, max_size=11),
@@ -53,7 +53,7 @@ def foo({input_value}) -> bool:
literal_op = literal_op.rsplit(maxsplit=1)[0]
vyper_ast = vy_ast.parse_to_ast(literal_op)
- vy_ast.folding.replace_literal_ops(vyper_ast)
- expected = vyper_ast.body[0].value.value
+ new_node = vyper_ast.body[0].value.get_folded_value()
+ expected = new_node.value
assert contract.foo(*values) == expected
diff --git a/tests/ast/nodes/test_evaluate_compare.py b/tests/unit/ast/nodes/test_fold_compare.py
similarity index 90%
rename from tests/ast/nodes/test_evaluate_compare.py
rename to tests/unit/ast/nodes/test_fold_compare.py
index 9ff5cea338..2b7c0f09d7 100644
--- a/tests/ast/nodes/test_evaluate_compare.py
+++ b/tests/unit/ast/nodes/test_fold_compare.py
@@ -8,7 +8,7 @@
# TODO expand to all signed types
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st.integers(), right=st.integers())
@pytest.mark.parametrize("op", ["==", "!=", "<", "<=", ">=", ">"])
def test_compare_eq_signed(get_contract, op, left, right):
@@ -21,14 +21,14 @@ def foo(a: int128, b: int128) -> bool:
vyper_ast = vy_ast.parse_to_ast(f"{left} {op} {right}")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
assert contract.foo(left, right) == new_node.value
# TODO expand to all unsigned types
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(left=st.integers(min_value=0), right=st.integers(min_value=0))
@pytest.mark.parametrize("op", ["==", "!=", "<", "<=", ">=", ">"])
def test_compare_eq_unsigned(get_contract, op, left, right):
@@ -41,13 +41,13 @@ def foo(a: uint128, b: uint128) -> bool:
vyper_ast = vy_ast.parse_to_ast(f"{left} {op} {right}")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
assert contract.foo(left, right) == new_node.value
@pytest.mark.fuzzing
-@settings(max_examples=20, deadline=1000)
+@settings(max_examples=20)
@given(left=st.integers(), right=st.lists(st.integers(), min_size=1, max_size=16))
def test_compare_in(left, right, get_contract):
source = f"""
@@ -65,7 +65,7 @@ def bar(a: int128) -> bool:
vyper_ast = vy_ast.parse_to_ast(f"{left} in {right}")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
# check runtime == fully folded
assert contract.foo(left, right) == new_node.value
@@ -76,7 +76,7 @@ def bar(a: int128) -> bool:
@pytest.mark.fuzzing
-@settings(max_examples=20, deadline=1000)
+@settings(max_examples=20)
@given(left=st.integers(), right=st.lists(st.integers(), min_size=1, max_size=16))
def test_compare_not_in(left, right, get_contract):
source = f"""
@@ -94,7 +94,7 @@ def bar(a: int128) -> bool:
vyper_ast = vy_ast.parse_to_ast(f"{left} not in {right}")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
# check runtime == fully folded
assert contract.foo(left, right) == new_node.value
@@ -109,4 +109,4 @@ def test_compare_type_mismatch(op):
vyper_ast = vy_ast.parse_to_ast(f"1 {op} 1.0")
old_node = vyper_ast.body[0].value
with pytest.raises(UnfoldableNode):
- old_node.evaluate()
+ old_node.get_folded_value()
diff --git a/tests/ast/nodes/test_evaluate_subscript.py b/tests/unit/ast/nodes/test_fold_subscript.py
similarity index 88%
rename from tests/ast/nodes/test_evaluate_subscript.py
rename to tests/unit/ast/nodes/test_fold_subscript.py
index 3c0fa5d16d..1884abf73b 100644
--- a/tests/ast/nodes/test_evaluate_subscript.py
+++ b/tests/unit/ast/nodes/test_fold_subscript.py
@@ -6,7 +6,7 @@
@pytest.mark.fuzzing
-@settings(max_examples=50, deadline=1000)
+@settings(max_examples=50)
@given(
idx=st.integers(min_value=0, max_value=9),
array=st.lists(st.integers(), min_size=10, max_size=10),
@@ -21,6 +21,6 @@ def foo(array: int128[10], idx: uint256) -> int128:
vyper_ast = vy_ast.parse_to_ast(f"{array}[{idx}]")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
assert contract.foo(array, idx) == new_node.value
diff --git a/tests/ast/nodes/test_evaluate_unaryop.py b/tests/unit/ast/nodes/test_fold_unaryop.py
similarity index 86%
rename from tests/ast/nodes/test_evaluate_unaryop.py
rename to tests/unit/ast/nodes/test_fold_unaryop.py
index 63d7a0b7ff..ff48adfe71 100644
--- a/tests/ast/nodes/test_evaluate_unaryop.py
+++ b/tests/unit/ast/nodes/test_fold_unaryop.py
@@ -14,7 +14,7 @@ def foo(a: bool) -> bool:
vyper_ast = vy_ast.parse_to_ast(f"not {bool_cond}")
old_node = vyper_ast.body[0].value
- new_node = old_node.evaluate()
+ new_node = old_node.get_folded_value()
assert contract.foo(bool_cond) == new_node.value
@@ -31,7 +31,7 @@ def foo(a: bool) -> bool:
literal_op = f"{'not ' * count}{bool_cond}"
vyper_ast = vy_ast.parse_to_ast(literal_op)
- vy_ast.folding.replace_literal_ops(vyper_ast)
- expected = vyper_ast.body[0].value.value
+ new_node = vyper_ast.body[0].value.get_folded_value()
+ expected = new_node.value
assert contract.foo(bool_cond) == expected
diff --git a/tests/ast/nodes/test_from_node.py b/tests/unit/ast/nodes/test_from_node.py
similarity index 100%
rename from tests/ast/nodes/test_from_node.py
rename to tests/unit/ast/nodes/test_from_node.py
diff --git a/tests/ast/nodes/test_get_children.py b/tests/unit/ast/nodes/test_get_children.py
similarity index 100%
rename from tests/ast/nodes/test_get_children.py
rename to tests/unit/ast/nodes/test_get_children.py
diff --git a/tests/ast/nodes/test_get_descendants.py b/tests/unit/ast/nodes/test_get_descendants.py
similarity index 100%
rename from tests/ast/nodes/test_get_descendants.py
rename to tests/unit/ast/nodes/test_get_descendants.py
diff --git a/tests/ast/nodes/test_hex.py b/tests/unit/ast/nodes/test_hex.py
similarity index 88%
rename from tests/ast/nodes/test_hex.py
rename to tests/unit/ast/nodes/test_hex.py
index 47483c493c..d413340083 100644
--- a/tests/ast/nodes/test_hex.py
+++ b/tests/unit/ast/nodes/test_hex.py
@@ -37,9 +37,9 @@ def foo():
@pytest.mark.parametrize("code", code_invalid_checksum)
-def test_invalid_checksum(code):
+def test_invalid_checksum(code, dummy_input_bundle):
vyper_module = vy_ast.parse_to_ast(code)
with pytest.raises(InvalidLiteral):
vy_ast.validation.validate_literal_nodes(vyper_module)
- semantics.validate_semantics(vyper_module, {})
+ semantics.validate_semantics(vyper_module, dummy_input_bundle)
diff --git a/tests/parser/parser_utils/test_annotate_and_optimize_ast.py b/tests/unit/ast/test_annotate_and_optimize_ast.py
similarity index 94%
rename from tests/parser/parser_utils/test_annotate_and_optimize_ast.py
rename to tests/unit/ast/test_annotate_and_optimize_ast.py
index 68a07178bb..16ce6fe631 100644
--- a/tests/parser/parser_utils/test_annotate_and_optimize_ast.py
+++ b/tests/unit/ast/test_annotate_and_optimize_ast.py
@@ -1,7 +1,6 @@
import ast as python_ast
-from vyper.ast.annotation import annotate_python_ast
-from vyper.ast.pre_parser import pre_parse
+from vyper.ast.parse import annotate_python_ast, pre_parse
class AssertionVisitor(python_ast.NodeVisitor):
diff --git a/tests/parser/ast_utils/test_ast_dict.py b/tests/unit/ast/test_ast_dict.py
similarity index 85%
rename from tests/parser/ast_utils/test_ast_dict.py
rename to tests/unit/ast/test_ast_dict.py
index f483d0cbe8..20390f3d5e 100644
--- a/tests/parser/ast_utils/test_ast_dict.py
+++ b/tests/unit/ast/test_ast_dict.py
@@ -1,7 +1,8 @@
import json
from vyper import compiler
-from vyper.ast.utils import ast_to_dict, dict_to_ast, parse_to_ast
+from vyper.ast.parse import parse_to_ast
+from vyper.ast.utils import ast_to_dict, dict_to_ast
def get_node_ids(ast_struct, ids=None):
@@ -19,7 +20,7 @@ def get_node_ids(ast_struct, ids=None):
elif v is None or isinstance(v, (str, int)):
continue
else:
- raise Exception("Unknown ast_struct provided.")
+ raise Exception(f"Unknown ast_struct provided. {k}, {v}")
return ids
@@ -30,7 +31,7 @@ def test() -> int128:
a: uint256 = 100
return 123
"""
- dict_out = compiler.compile_code(code, ["ast_dict"])
+ dict_out = compiler.compile_code(code, output_formats=["ast_dict"])
node_ids = get_node_ids(dict_out)
assert len(node_ids) == len(set(node_ids))
@@ -40,8 +41,8 @@ def test_basic_ast():
code = """
a: int128
"""
- dict_out = compiler.compile_code(code, ["ast_dict"])
- assert dict_out["ast_dict"]["ast"]["body"][0] == {
+ dict_out = compiler.compile_code(code, output_formats=["annotated_ast_dict"], source_id=0)
+ assert dict_out["annotated_ast_dict"]["ast"]["body"][0] == {
"annotation": {
"ast_type": "Name",
"col_offset": 3,
@@ -68,12 +69,14 @@ def test_basic_ast():
"lineno": 2,
"node_id": 2,
"src": "1:1:0",
+ "type": "int128",
},
"value": None,
"is_constant": False,
"is_immutable": False,
"is_public": False,
"is_transient": False,
+ "type": "int128",
}
@@ -89,7 +92,7 @@ def foo() -> uint256: view
def foo() -> uint256:
return 1
"""
- dict_out = compiler.compile_code(code, ["ast_dict"])
+ dict_out = compiler.compile_code(code, output_formats=["ast_dict"], source_id=0)
assert dict_out["ast_dict"]["ast"]["body"][1] == {
"col_offset": 0,
"annotation": {
diff --git a/tests/ast/test_metadata_journal.py b/tests/unit/ast/test_metadata_journal.py
similarity index 100%
rename from tests/ast/test_metadata_journal.py
rename to tests/unit/ast/test_metadata_journal.py
diff --git a/tests/ast/test_natspec.py b/tests/unit/ast/test_natspec.py
similarity index 99%
rename from tests/ast/test_natspec.py
rename to tests/unit/ast/test_natspec.py
index c2133468aa..22167f8694 100644
--- a/tests/ast/test_natspec.py
+++ b/tests/unit/ast/test_natspec.py
@@ -60,7 +60,7 @@ def doesEat(food: String[30], qty: uint256) -> bool:
def parse_natspec(code):
- vyper_ast = CompilerData(code).vyper_module_folded
+ vyper_ast = CompilerData(code).annotated_vyper_module
return vy_ast.parse_natspec(vyper_ast)
diff --git a/tests/parser/ast_utils/test_ast.py b/tests/unit/ast/test_parser.py
similarity index 92%
rename from tests/parser/ast_utils/test_ast.py
rename to tests/unit/ast/test_parser.py
index c47bf40bfa..e0bfcbc2ef 100644
--- a/tests/parser/ast_utils/test_ast.py
+++ b/tests/unit/ast/test_parser.py
@@ -1,4 +1,4 @@
-from vyper.ast.utils import parse_to_ast
+from vyper.ast.parse import parse_to_ast
def test_ast_equal():
diff --git a/tests/ast/test_pre_parser.py b/tests/unit/ast/test_pre_parser.py
similarity index 65%
rename from tests/ast/test_pre_parser.py
rename to tests/unit/ast/test_pre_parser.py
index 5427532c16..682c13ca84 100644
--- a/tests/ast/test_pre_parser.py
+++ b/tests/unit/ast/test_pre_parser.py
@@ -1,8 +1,9 @@
import pytest
from vyper.ast.pre_parser import pre_parse, validate_version_pragma
+from vyper.compiler.phases import CompilerData
from vyper.compiler.settings import OptimizationLevel, Settings
-from vyper.exceptions import VersionException
+from vyper.exceptions import StructureException, VersionException
SRC_LINE = (1, 0) # Dummy source line
COMPILER_VERSION = "0.1.1"
@@ -96,43 +97,50 @@ def test_prerelease_invalid_version_pragma(file_version, mock_version):
"""
""",
Settings(),
+ Settings(optimize=OptimizationLevel.GAS),
),
(
"""
#pragma optimize codesize
""",
Settings(optimize=OptimizationLevel.CODESIZE),
+ None,
),
(
"""
#pragma optimize none
""",
Settings(optimize=OptimizationLevel.NONE),
+ None,
),
(
"""
#pragma optimize gas
""",
Settings(optimize=OptimizationLevel.GAS),
+ None,
),
(
"""
#pragma version 0.3.10
""",
Settings(compiler_version="0.3.10"),
+ Settings(optimize=OptimizationLevel.GAS),
),
(
"""
#pragma evm-version shanghai
""",
Settings(evm_version="shanghai"),
+ Settings(evm_version="shanghai", optimize=OptimizationLevel.GAS),
),
(
"""
#pragma optimize codesize
#pragma evm-version shanghai
""",
- Settings(evm_version="shanghai", optimize=OptimizationLevel.GAS),
+ Settings(evm_version="shanghai", optimize=OptimizationLevel.CODESIZE),
+ None,
),
(
"""
@@ -140,6 +148,7 @@ def test_prerelease_invalid_version_pragma(file_version, mock_version):
#pragma evm-version shanghai
""",
Settings(evm_version="shanghai", compiler_version="0.3.10"),
+ Settings(evm_version="shanghai", optimize=OptimizationLevel.GAS),
),
(
"""
@@ -147,6 +156,7 @@ def test_prerelease_invalid_version_pragma(file_version, mock_version):
#pragma optimize gas
""",
Settings(compiler_version="0.3.10", optimize=OptimizationLevel.GAS),
+ Settings(optimize=OptimizationLevel.GAS),
),
(
"""
@@ -155,11 +165,62 @@ def test_prerelease_invalid_version_pragma(file_version, mock_version):
#pragma optimize gas
""",
Settings(compiler_version="0.3.10", optimize=OptimizationLevel.GAS, evm_version="shanghai"),
+ Settings(optimize=OptimizationLevel.GAS, evm_version="shanghai"),
),
]
-@pytest.mark.parametrize("code, expected_pragmas", pragma_examples)
-def parse_pragmas(code, expected_pragmas):
- pragmas, _, _ = pre_parse(code)
- assert pragmas == expected_pragmas
+@pytest.mark.parametrize("code, pre_parse_settings, compiler_data_settings", pragma_examples)
+def test_parse_pragmas(code, pre_parse_settings, compiler_data_settings, mock_version):
+ mock_version("0.3.10")
+ settings, _, _ = pre_parse(code)
+
+ assert settings == pre_parse_settings
+
+ compiler_data = CompilerData(code)
+
+ # check what happens after CompilerData constructor
+ if compiler_data_settings is None:
+ # None is sentinel here meaning that nothing changed
+ compiler_data_settings = pre_parse_settings
+
+ # cannot be set via pragma, don't check
+ compiler_data_settings.experimental_codegen = False
+
+ assert compiler_data.settings == compiler_data_settings
+
+
+invalid_pragmas = [
+ # evm-versionnn
+ """
+# pragma evm-versionnn cancun
+ """,
+ # bad fork name
+ """
+# pragma evm-version cancunn
+ """,
+ # oppptimize
+ """
+# pragma oppptimize codesize
+ """,
+ # ggas
+ """
+# pragma optimize ggas
+ """,
+ # double specified
+ """
+# pragma optimize gas
+# pragma optimize codesize
+ """,
+ # double specified
+ """
+# pragma evm-version cancun
+# pragma evm-version shanghai
+ """,
+]
+
+
+@pytest.mark.parametrize("code", invalid_pragmas)
+def test_invalid_pragma(code):
+ with pytest.raises(StructureException):
+ pre_parse(code)
diff --git a/tests/test_utils.py b/tests/unit/ast/test_source_annotation.py
similarity index 100%
rename from tests/test_utils.py
rename to tests/unit/ast/test_source_annotation.py
diff --git a/tests/cli/outputs/test_storage_layout.py b/tests/unit/cli/storage_layout/test_storage_layout.py
similarity index 100%
rename from tests/cli/outputs/test_storage_layout.py
rename to tests/unit/cli/storage_layout/test_storage_layout.py
diff --git a/tests/cli/outputs/test_storage_layout_overrides.py b/tests/unit/cli/storage_layout/test_storage_layout_overrides.py
similarity index 98%
rename from tests/cli/outputs/test_storage_layout_overrides.py
rename to tests/unit/cli/storage_layout/test_storage_layout_overrides.py
index 94e0faeb37..f4c11b7ae6 100644
--- a/tests/cli/outputs/test_storage_layout_overrides.py
+++ b/tests/unit/cli/storage_layout/test_storage_layout_overrides.py
@@ -103,7 +103,7 @@ def test_overflow():
storage_layout_override = {"x": {"slot": 2**256 - 1, "type": "uint256[2]"}}
with pytest.raises(
- StorageLayoutException, match=f"Invalid storage slot for var x, out of bounds: {2**256}\n"
+ StorageLayoutException, match=f"Invalid storage slot for var x, out of bounds: {2**256}"
):
compile_code(
code, output_formats=["layout"], storage_layout_override=storage_layout_override
diff --git a/tests/unit/cli/vyper_compile/test_compile_files.py b/tests/unit/cli/vyper_compile/test_compile_files.py
new file mode 100644
index 0000000000..2a65d66835
--- /dev/null
+++ b/tests/unit/cli/vyper_compile/test_compile_files.py
@@ -0,0 +1,259 @@
+from pathlib import Path
+
+import pytest
+
+from tests.utils import working_directory
+from vyper.cli.vyper_compile import compile_files
+
+
+def test_combined_json_keys(tmp_path, make_file):
+ make_file("bar.vy", "")
+
+ combined_keys = {
+ "bytecode",
+ "bytecode_runtime",
+ "blueprint_bytecode",
+ "abi",
+ "source_map",
+ "layout",
+ "method_identifiers",
+ "userdoc",
+ "devdoc",
+ }
+ compile_data = compile_files(["bar.vy"], ["combined_json"], paths=[tmp_path])
+
+ assert set(compile_data.keys()) == {Path("bar.vy"), "version"}
+ assert set(compile_data[Path("bar.vy")].keys()) == combined_keys
+
+
+def test_invalid_root_path():
+ with pytest.raises(FileNotFoundError):
+ compile_files([], [], paths=["path/that/does/not/exist"])
+
+
+CONTRACT_CODE = """
+{import_stmt}
+
+@external
+def foo() -> {alias}.FooStruct:
+ return {alias}.FooStruct({{foo_: 13}})
+
+@external
+def bar(a: address) -> {alias}.FooStruct:
+ return {alias}(a).bar()
+"""
+
+INTERFACE_CODE = """
+struct FooStruct:
+ foo_: uint256
+
+@external
+def foo() -> FooStruct:
+ ...
+
+@external
+def bar() -> FooStruct:
+ ...
+"""
+
+
+SAME_FOLDER_IMPORT_STMT = [
+ ("import IFoo as IFoo", "IFoo"),
+ ("import contracts.IFoo as IFoo", "IFoo"),
+ ("from . import IFoo", "IFoo"),
+ ("from contracts import IFoo", "IFoo"),
+ ("from ..contracts import IFoo", "IFoo"),
+ ("from . import IFoo as FooBar", "FooBar"),
+ ("from contracts import IFoo as FooBar", "FooBar"),
+ ("from ..contracts import IFoo as FooBar", "FooBar"),
+]
+
+
+@pytest.mark.parametrize("import_stmt,alias", SAME_FOLDER_IMPORT_STMT)
+def test_import_same_folder(import_stmt, alias, tmp_path, make_file):
+ foo = "contracts/foo.vy"
+ make_file("contracts/foo.vy", CONTRACT_CODE.format(import_stmt=import_stmt, alias=alias))
+ make_file("contracts/IFoo.vyi", INTERFACE_CODE)
+
+ assert compile_files([foo], ["combined_json"], paths=[tmp_path])
+
+
+SUBFOLDER_IMPORT_STMT = [
+ ("import other.IFoo as IFoo", "IFoo"),
+ ("import contracts.other.IFoo as IFoo", "IFoo"),
+ ("from other import IFoo", "IFoo"),
+ ("from contracts.other import IFoo", "IFoo"),
+ ("from .other import IFoo", "IFoo"),
+ ("from ..contracts.other import IFoo", "IFoo"),
+ ("from other import IFoo as FooBar", "FooBar"),
+ ("from contracts.other import IFoo as FooBar", "FooBar"),
+ ("from .other import IFoo as FooBar", "FooBar"),
+ ("from ..contracts.other import IFoo as FooBar", "FooBar"),
+]
+
+
+@pytest.mark.parametrize("import_stmt, alias", SUBFOLDER_IMPORT_STMT)
+def test_import_subfolder(import_stmt, alias, tmp_path, make_file):
+ foo = make_file(
+ "contracts/foo.vy", (CONTRACT_CODE.format(import_stmt=import_stmt, alias=alias))
+ )
+ make_file("contracts/other/IFoo.vyi", INTERFACE_CODE)
+
+ assert compile_files([foo], ["combined_json"], paths=[tmp_path])
+
+
+OTHER_FOLDER_IMPORT_STMT = [
+ ("import interfaces.IFoo as IFoo", "IFoo"),
+ ("from interfaces import IFoo", "IFoo"),
+ ("from ..interfaces import IFoo", "IFoo"),
+ ("from interfaces import IFoo as FooBar", "FooBar"),
+ ("from ..interfaces import IFoo as FooBar", "FooBar"),
+]
+
+
+@pytest.mark.parametrize("import_stmt, alias", OTHER_FOLDER_IMPORT_STMT)
+def test_import_other_folder(import_stmt, alias, tmp_path, make_file):
+ foo = make_file("contracts/foo.vy", CONTRACT_CODE.format(import_stmt=import_stmt, alias=alias))
+ make_file("interfaces/IFoo.vyi", INTERFACE_CODE)
+
+ assert compile_files([foo], ["combined_json"], paths=[tmp_path])
+
+
+def test_import_parent_folder(tmp_path, make_file):
+ foo = make_file(
+ "contracts/baz/foo.vy",
+ CONTRACT_CODE.format(import_stmt="from ... import IFoo", alias="IFoo"),
+ )
+ make_file("IFoo.vyi", INTERFACE_CODE)
+
+ assert compile_files([foo], ["combined_json"], paths=[tmp_path])
+
+ # perform relative import outside of base folder
+ compile_files([foo], ["combined_json"], paths=[tmp_path / "contracts"])
+
+
+def test_import_search_paths(tmp_path, make_file):
+ with working_directory(tmp_path):
+ contract_code = CONTRACT_CODE.format(import_stmt="from utils import IFoo", alias="IFoo")
+ contract_filename = "dir1/baz/foo.vy"
+ interface_filename = "dir2/utils/IFoo.vyi"
+ make_file(interface_filename, INTERFACE_CODE)
+ make_file(contract_filename, contract_code)
+
+ assert compile_files([contract_filename], ["combined_json"], paths=["dir2"])
+
+
+META_IMPORT_STMT = [
+ "import ISelf as ISelf",
+ "import contracts.ISelf as ISelf",
+ "from . import ISelf",
+ "from contracts import ISelf",
+]
+
+
+@pytest.mark.parametrize("import_stmt", META_IMPORT_STMT)
+def test_import_self_interface(import_stmt, tmp_path, make_file):
+ interface_code = """
+struct FooStruct:
+ foo_: uint256
+
+@external
+def know_thyself(a: address) -> FooStruct:
+ ...
+
+@external
+def be_known() -> FooStruct:
+ ...
+ """
+ code = f"""
+{import_stmt}
+
+@external
+def know_thyself(a: address) -> ISelf.FooStruct:
+ return ISelf(a).be_known()
+
+@external
+def be_known() -> ISelf.FooStruct:
+ return ISelf.FooStruct({{foo_: 42}})
+ """
+ make_file("contracts/ISelf.vyi", interface_code)
+ meta = make_file("contracts/Self.vy", code)
+
+ assert compile_files([meta], ["combined_json"], paths=[tmp_path])
+
+
+# implement IFoo in another contract for fun
+@pytest.mark.parametrize("import_stmt_foo,alias", SAME_FOLDER_IMPORT_STMT)
+def test_another_interface_implementation(import_stmt_foo, alias, tmp_path, make_file):
+ baz_code = f"""
+{import_stmt_foo}
+
+@external
+def foo(a: address) -> {alias}.FooStruct:
+ return {alias}(a).foo()
+
+@external
+def bar(_foo: address) -> {alias}.FooStruct:
+ return {alias}(_foo).bar()
+ """
+ make_file("contracts/IFoo.vyi", INTERFACE_CODE)
+ baz = make_file("contracts/Baz.vy", baz_code)
+
+ assert compile_files([baz], ["combined_json"], paths=[tmp_path])
+
+
+def test_local_namespace(make_file, tmp_path):
+ # interface code namespaces should be isolated
+ # all of these contract should be able to compile together
+ codes = [
+ "import foo as FooBar",
+ "import bar as FooBar",
+ "import foo as BarFoo",
+ "import bar as BarFoo",
+ ]
+ struct_def = """
+struct FooStruct:
+ foo_: uint256
+
+ """
+
+ paths = []
+ for i, code in enumerate(codes):
+ code += struct_def
+ filename = f"code{i}.vy"
+ make_file(filename, code)
+ paths.append(filename)
+
+ for file_name in ("foo.vyi", "bar.vyi"):
+ make_file(file_name, INTERFACE_CODE)
+
+ assert compile_files(paths, ["combined_json"], paths=[tmp_path])
+
+
+def test_compile_outside_root_path(tmp_path, make_file):
+ # absolute paths relative to "."
+ make_file("ifoo.vyi", INTERFACE_CODE)
+ foo = make_file("foo.vy", CONTRACT_CODE.format(import_stmt="import ifoo as IFoo", alias="IFoo"))
+
+ assert compile_files([foo], ["combined_json"], paths=None)
+
+
+def test_import_library(tmp_path, make_file):
+ library_source = """
+@internal
+def foo() -> uint256:
+ return block.number + 1
+ """
+
+ contract_source = """
+import lib
+
+@external
+def foo() -> uint256:
+ return lib.foo()
+ """
+
+ make_file("lib.vy", library_source)
+ contract_file = make_file("contract.vy", contract_source)
+
+ assert compile_files([contract_file], ["combined_json"], paths=[tmp_path]) is not None
diff --git a/tests/cli/vyper_compile/test_parse_args.py b/tests/unit/cli/vyper_compile/test_parse_args.py
similarity index 99%
rename from tests/cli/vyper_compile/test_parse_args.py
rename to tests/unit/cli/vyper_compile/test_parse_args.py
index a676a7836b..0e8c4e9605 100644
--- a/tests/cli/vyper_compile/test_parse_args.py
+++ b/tests/unit/cli/vyper_compile/test_parse_args.py
@@ -21,7 +21,9 @@ def foo() -> bool:
bar_path = chdir_path.joinpath("bar.vy")
with bar_path.open("w") as fp:
fp.write(code)
+
_parse_args([str(bar_path)]) # absolute path
os.chdir(chdir_path.parent)
+
_parse_args([str(bar_path)]) # absolute path, subfolder of cwd
_parse_args([str(bar_path.relative_to(chdir_path.parent))]) # relative path
diff --git a/tests/unit/cli/vyper_json/test_compile_json.py b/tests/unit/cli/vyper_json/test_compile_json.py
new file mode 100644
index 0000000000..a50946ba21
--- /dev/null
+++ b/tests/unit/cli/vyper_json/test_compile_json.py
@@ -0,0 +1,273 @@
+import json
+from pathlib import PurePath
+
+import pytest
+
+import vyper
+from vyper.cli.vyper_json import (
+ compile_from_input_dict,
+ compile_json,
+ exc_handler_to_dict,
+ get_inputs,
+)
+from vyper.compiler import OUTPUT_FORMATS, compile_code, compile_from_file_input
+from vyper.compiler.input_bundle import JSONInputBundle
+from vyper.exceptions import InvalidType, JSONError, SyntaxException
+
+FOO_CODE = """
+import contracts.ibar as IBar
+
+import contracts.library as library
+
+@external
+def foo(a: address) -> bool:
+ return IBar(a).bar(1)
+
+@external
+def baz() -> uint256:
+ return self.balance + library.foo()
+"""
+
+BAR_CODE = """
+import contracts.ibar as IBar
+
+implements: IBar
+
+@external
+def bar(a: uint256) -> bool:
+ return True
+"""
+
+BAR_VYI = """
+@external
+def bar(a: uint256) -> bool:
+ ...
+"""
+
+LIBRARY_CODE = """
+@internal
+def foo() -> uint256:
+ return block.number + 1
+"""
+
+BAD_SYNTAX_CODE = """
+def bar()>:
+"""
+
+BAD_COMPILER_CODE = """
+@external
+def oopsie(a: uint256) -> bool:
+ return 42
+"""
+
+BAR_ABI = [
+ {
+ "name": "bar",
+ "outputs": [{"type": "bool", "name": "out"}],
+ "inputs": [{"type": "uint256", "name": "a"}],
+ "stateMutability": "nonpayable",
+ "type": "function",
+ }
+]
+
+
+@pytest.fixture(scope="function")
+def input_json():
+ return {
+ "language": "Vyper",
+ "sources": {
+ "contracts/foo.vy": {"content": FOO_CODE},
+ "contracts/library.vy": {"content": LIBRARY_CODE},
+ "contracts/bar.vy": {"content": BAR_CODE},
+ },
+ "interfaces": {"contracts/ibar.json": {"abi": BAR_ABI}},
+ "settings": {"outputSelection": {"*": ["*"]}},
+ }
+
+
+@pytest.fixture(scope="function")
+def input_bundle(input_json):
+ # CMC 2023-12-11 maybe input_json -> JSONInputBundle should be a helper
+ # function in `vyper_json.py`.
+ sources = get_inputs(input_json)
+ return JSONInputBundle(sources, search_paths=[PurePath(".")])
+
+
+# test string and dict inputs both work
+def test_string_input(input_json):
+ assert compile_json(input_json) == compile_json(json.dumps(input_json))
+
+
+def test_bad_json():
+ with pytest.raises(JSONError):
+ compile_json("this probably isn't valid JSON, is it")
+
+
+def test_keyerror_becomes_jsonerror(input_json):
+ del input_json["sources"]
+ with pytest.raises(KeyError):
+ compile_from_input_dict(input_json)
+ with pytest.raises(JSONError):
+ compile_json(input_json)
+
+
+def test_compile_json(input_json, input_bundle):
+ foo_input = input_bundle.load_file("contracts/foo.vy")
+ foo = compile_from_file_input(
+ foo_input, output_formats=OUTPUT_FORMATS, input_bundle=input_bundle
+ )
+
+ library_input = input_bundle.load_file("contracts/library.vy")
+ library = compile_from_file_input(
+ library_input, output_formats=OUTPUT_FORMATS, input_bundle=input_bundle
+ )
+
+ bar_input = input_bundle.load_file("contracts/bar.vy")
+ bar = compile_from_file_input(
+ bar_input, output_formats=OUTPUT_FORMATS, input_bundle=input_bundle
+ )
+
+ compile_code_results = {
+ "contracts/bar.vy": bar,
+ "contracts/library.vy": library,
+ "contracts/foo.vy": foo,
+ }
+
+ output_json = compile_json(input_json)
+ assert list(output_json["contracts"].keys()) == [
+ "contracts/foo.vy",
+ "contracts/library.vy",
+ "contracts/bar.vy",
+ ]
+
+ assert sorted(output_json.keys()) == ["compiler", "contracts", "sources"]
+ assert output_json["compiler"] == f"vyper-{vyper.__version__}"
+
+ for source_id, contract_name in [(0, "foo"), (2, "library"), (3, "bar")]:
+ path = f"contracts/{contract_name}.vy"
+ data = compile_code_results[path]
+ assert output_json["sources"][path] == {"id": source_id, "ast": data["ast_dict"]["ast"]}
+ assert output_json["contracts"][path][contract_name] == {
+ "abi": data["abi"],
+ "devdoc": data["devdoc"],
+ "interface": data["interface"],
+ "ir": data["ir_dict"],
+ "userdoc": data["userdoc"],
+ "metadata": data["metadata"],
+ "evm": {
+ "bytecode": {"object": data["bytecode"], "opcodes": data["opcodes"]},
+ "deployedBytecode": {
+ "object": data["bytecode_runtime"],
+ "opcodes": data["opcodes_runtime"],
+ "sourceMap": data["source_map"]["pc_pos_map_compressed"],
+ "sourceMapFull": data["source_map_full"],
+ },
+ "methodIdentifiers": data["method_identifiers"],
+ },
+ }
+
+
+def test_compilation_targets(input_json):
+ output_json = compile_json(input_json)
+ assert list(output_json["contracts"].keys()) == [
+ "contracts/foo.vy",
+ "contracts/library.vy",
+ "contracts/bar.vy",
+ ]
+
+ # omit library.vy
+ input_json["settings"]["outputSelection"] = {"contracts/foo.vy": "*", "contracts/bar.vy": "*"}
+ output_json = compile_json(input_json)
+
+ assert list(output_json["contracts"].keys()) == ["contracts/foo.vy", "contracts/bar.vy"]
+
+
+def test_different_outputs(input_bundle, input_json):
+ input_json["settings"]["outputSelection"] = {
+ "contracts/bar.vy": "*",
+ "contracts/foo.vy": ["evm.methodIdentifiers"],
+ }
+ output_json = compile_json(input_json)
+ assert list(output_json["contracts"].keys()) == ["contracts/bar.vy", "contracts/foo.vy"]
+
+ assert sorted(output_json.keys()) == ["compiler", "contracts", "sources"]
+ assert output_json["compiler"] == f"vyper-{vyper.__version__}"
+
+ contracts = output_json["contracts"]
+
+ foo = contracts["contracts/foo.vy"]["foo"]
+ bar = contracts["contracts/bar.vy"]["bar"]
+ assert sorted(bar.keys()) == ["abi", "devdoc", "evm", "interface", "ir", "metadata", "userdoc"]
+
+ assert sorted(foo.keys()) == ["evm"]
+
+ # check method_identifiers
+ method_identifiers = compile_code(
+ FOO_CODE,
+ contract_path="contracts/foo.vy",
+ output_formats=["method_identifiers"],
+ input_bundle=input_bundle,
+ )["method_identifiers"]
+ assert foo["evm"]["methodIdentifiers"] == method_identifiers
+
+
+def test_root_folder_not_exists(input_json):
+ with pytest.raises(FileNotFoundError):
+ compile_json(input_json, root_folder="/path/that/does/not/exist")
+
+
+def test_wrong_language():
+ with pytest.raises(JSONError):
+ compile_json({"language": "Solidity"})
+
+
+def test_exc_handler_raises_syntax(input_json):
+ input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE}
+ with pytest.raises(SyntaxException):
+ compile_json(input_json)
+
+
+def test_exc_handler_to_dict_syntax(input_json):
+ input_json["sources"]["badcode.vy"] = {"content": BAD_SYNTAX_CODE}
+ result = compile_json(input_json, exc_handler_to_dict)
+ assert "errors" in result
+ assert len(result["errors"]) == 1
+ error = result["errors"][0]
+ assert error["component"] == "compiler", error
+ assert error["type"] == "SyntaxException"
+
+
+def test_exc_handler_raises_compiler(input_json):
+ input_json["sources"]["badcode.vy"] = {"content": BAD_COMPILER_CODE}
+ with pytest.raises(InvalidType):
+ compile_json(input_json)
+
+
+def test_exc_handler_to_dict_compiler(input_json):
+ input_json["sources"]["badcode.vy"] = {"content": BAD_COMPILER_CODE}
+ result = compile_json(input_json, exc_handler_to_dict)
+ assert sorted(result.keys()) == ["compiler", "errors"]
+ assert result["compiler"] == f"vyper-{vyper.__version__}"
+ assert len(result["errors"]) == 1
+ error = result["errors"][0]
+ assert error["component"] == "compiler"
+ assert error["type"] == "InvalidType"
+
+
+def test_source_ids_increment(input_json):
+ input_json["settings"]["outputSelection"] = {"*": ["evm.deployedBytecode.sourceMap"]}
+ result = compile_json(input_json)
+
+ def get(filename, contractname):
+ return result["contracts"][filename][contractname]["evm"]["deployedBytecode"]["sourceMap"]
+
+ assert get("contracts/foo.vy", "foo").startswith("-1:-1:0")
+ assert get("contracts/library.vy", "library").startswith("-1:-1:2")
+ assert get("contracts/bar.vy", "bar").startswith("-1:-1:3")
+
+
+def test_relative_import_paths(input_json):
+ input_json["sources"]["contracts/potato/baz/baz.vy"] = {"content": "from ... import foo"}
+ input_json["sources"]["contracts/potato/baz/potato.vy"] = {"content": "from . import baz"}
+ input_json["sources"]["contracts/potato/footato.vy"] = {"content": "from baz import baz"}
+ compile_from_input_dict(input_json)
diff --git a/tests/unit/cli/vyper_json/test_get_inputs.py b/tests/unit/cli/vyper_json/test_get_inputs.py
new file mode 100644
index 0000000000..c91cc750f2
--- /dev/null
+++ b/tests/unit/cli/vyper_json/test_get_inputs.py
@@ -0,0 +1,139 @@
+from pathlib import PurePath
+
+import pytest
+
+from vyper.cli.vyper_json import get_inputs
+from vyper.exceptions import JSONError
+from vyper.utils import keccak256
+
+FOO_CODE = """
+import contracts.bar as Bar
+
+@external
+def foo(a: address) -> bool:
+ return Bar(a).bar(1)
+"""
+
+BAR_CODE = """
+@external
+def bar(a: uint256) -> bool:
+ return True
+"""
+
+
+def test_no_sources():
+ with pytest.raises(KeyError):
+ get_inputs({})
+
+
+def test_contracts_urls():
+ with pytest.raises(JSONError):
+ get_inputs({"sources": {"foo.vy": {"urls": ["https://foo.code.com/"]}}})
+
+
+def test_contracts_no_content_key():
+ with pytest.raises(JSONError):
+ get_inputs({"sources": {"foo.vy": FOO_CODE}})
+
+
+def test_contracts_keccak():
+ hash_ = keccak256(FOO_CODE.encode()).hex()
+
+ input_json = {"sources": {"foo.vy": {"content": FOO_CODE, "keccak256": hash_}}}
+ get_inputs(input_json)
+
+ input_json["sources"]["foo.vy"]["keccak256"] = "0x" + hash_
+ get_inputs(input_json)
+
+ input_json["sources"]["foo.vy"]["keccak256"] = "0x1234567890"
+ with pytest.raises(JSONError):
+ get_inputs(input_json)
+
+
+def test_contracts_outside_pwd():
+ input_json = {"sources": {"../foo.vy": {"content": FOO_CODE}}}
+ get_inputs(input_json)
+
+
+def test_contract_collision():
+ # ./foo.vy and foo.vy will resolve to the same path
+ input_json = {"sources": {"./foo.vy": {"content": FOO_CODE}, "foo.vy": {"content": FOO_CODE}}}
+ with pytest.raises(JSONError):
+ get_inputs(input_json)
+
+
+def test_contracts_return_value():
+ input_json = {
+ "sources": {"foo.vy": {"content": FOO_CODE}, "contracts/bar.vy": {"content": BAR_CODE}}
+ }
+ result = get_inputs(input_json)
+ assert result == {
+ PurePath("foo.vy"): {"content": FOO_CODE},
+ PurePath("contracts/bar.vy"): {"content": BAR_CODE},
+ }
+
+
+BAR_ABI = [
+ {
+ "name": "bar",
+ "outputs": [{"type": "bool", "name": "out"}],
+ "inputs": [{"type": "uint256", "name": "a"}],
+ "stateMutability": "nonpayable",
+ "type": "function",
+ }
+]
+
+
+# tests to get interfaces from input dicts
+
+
+def test_interface_collision():
+ input_json = {
+ "sources": {"foo.vy": {"content": FOO_CODE}},
+ "interfaces": {"bar.json": {"abi": BAR_ABI}, "bar.vy": {"content": BAR_CODE}},
+ }
+ with pytest.raises(JSONError):
+ get_inputs(input_json)
+
+
+def test_json_no_abi():
+ input_json = {
+ "sources": {"foo.vy": {"content": FOO_CODE}},
+ "interfaces": {"bar.json": {"content": BAR_ABI}},
+ }
+ with pytest.raises(JSONError):
+ get_inputs(input_json)
+
+
+def test_vy_no_content():
+ input_json = {
+ "sources": {"foo.vy": {"content": FOO_CODE}},
+ "interfaces": {"bar.vy": {"abi": BAR_CODE}},
+ }
+ with pytest.raises(JSONError):
+ get_inputs(input_json)
+
+
+def test_interfaces_output():
+ input_json = {
+ "sources": {"foo.vy": {"content": FOO_CODE}},
+ "interfaces": {
+ "bar.json": {"abi": BAR_ABI},
+ "interface.folder/bar2.vy": {"content": BAR_CODE},
+ },
+ }
+ result = get_inputs(input_json)
+ assert result == {
+ PurePath("foo.vy"): {"content": FOO_CODE},
+ PurePath("bar.json"): {"abi": BAR_ABI},
+ PurePath("interface.folder/bar2.vy"): {"content": BAR_CODE},
+ }
+
+
+# EIP-2678 -- not currently supported
+@pytest.mark.xfail
+def test_manifest_output():
+ input_json = {"interfaces": {"bar.json": {"contractTypes": {"Bar": {"abi": BAR_ABI}}}}}
+ result = get_inputs(input_json)
+ assert isinstance(result, dict)
+ assert result == {"Bar": {"type": "json", "code": BAR_ABI}}
diff --git a/tests/cli/vyper_json/test_get_settings.py b/tests/unit/cli/vyper_json/test_get_settings.py
similarity index 97%
rename from tests/cli/vyper_json/test_get_settings.py
rename to tests/unit/cli/vyper_json/test_get_settings.py
index bbe5dab113..989d4565cd 100644
--- a/tests/cli/vyper_json/test_get_settings.py
+++ b/tests/unit/cli/vyper_json/test_get_settings.py
@@ -1,5 +1,3 @@
-#!/usr/bin/env python3
-
import pytest
from vyper.cli.vyper_json import get_evm_version
diff --git a/tests/unit/cli/vyper_json/test_output_selection.py b/tests/unit/cli/vyper_json/test_output_selection.py
new file mode 100644
index 0000000000..5383190a66
--- /dev/null
+++ b/tests/unit/cli/vyper_json/test_output_selection.py
@@ -0,0 +1,68 @@
+from pathlib import PurePath
+
+import pytest
+
+from vyper.cli.vyper_json import TRANSLATE_MAP, get_output_formats
+from vyper.exceptions import JSONError
+
+
+def test_no_outputs():
+ with pytest.raises(KeyError):
+ get_output_formats({})
+
+
+def test_invalid_output():
+ input_json = {
+ "sources": {"foo.vy": ""},
+ "settings": {"outputSelection": {"foo.vy": ["abi", "foobar"]}},
+ }
+ with pytest.raises(JSONError):
+ get_output_formats(input_json)
+
+
+def test_unknown_contract():
+ input_json = {"sources": {}, "settings": {"outputSelection": {"bar.vy": ["abi"]}}}
+ with pytest.raises(JSONError):
+ get_output_formats(input_json)
+
+
+@pytest.mark.parametrize("output", TRANSLATE_MAP.items())
+def test_translate_map(output):
+ input_json = {
+ "sources": {"foo.vy": ""},
+ "settings": {"outputSelection": {"foo.vy": [output[0]]}},
+ }
+ assert get_output_formats(input_json) == {PurePath("foo.vy"): [output[1]]}
+
+
+def test_star():
+ input_json = {
+ "sources": {"foo.vy": "", "bar.vy": ""},
+ "settings": {"outputSelection": {"*": ["*"]}},
+ }
+ expected = sorted(set(TRANSLATE_MAP.values()))
+ result = get_output_formats(input_json)
+ assert result == {PurePath("foo.vy"): expected, PurePath("bar.vy"): expected}
+
+
+def test_evm():
+ input_json = {
+ "sources": {"foo.vy": ""},
+ "settings": {"outputSelection": {"foo.vy": ["abi", "evm"]}},
+ }
+ expected = ["abi"] + sorted(v for k, v in TRANSLATE_MAP.items() if k.startswith("evm"))
+ result = get_output_formats(input_json)
+ assert result == {PurePath("foo.vy"): expected}
+
+
+def test_solc_style():
+ input_json = {
+ "sources": {"foo.vy": ""},
+ "settings": {"outputSelection": {"foo.vy": {"": ["abi"], "foo.vy": ["ir"]}}},
+ }
+ assert get_output_formats(input_json) == {PurePath("foo.vy"): ["abi", "ir_dict"]}
+
+
+def test_metadata():
+ input_json = {"sources": {"foo.vy": ""}, "settings": {"outputSelection": {"*": ["metadata"]}}}
+ assert get_output_formats(input_json) == {PurePath("foo.vy"): ["metadata"]}
diff --git a/tests/cli/vyper_json/test_parse_args_vyperjson.py b/tests/unit/cli/vyper_json/test_parse_args_vyperjson.py
similarity index 95%
rename from tests/cli/vyper_json/test_parse_args_vyperjson.py
rename to tests/unit/cli/vyper_json/test_parse_args_vyperjson.py
index 11e527843a..6b509dd3ef 100644
--- a/tests/cli/vyper_json/test_parse_args_vyperjson.py
+++ b/tests/unit/cli/vyper_json/test_parse_args_vyperjson.py
@@ -9,11 +9,11 @@
from vyper.exceptions import JSONError
FOO_CODE = """
-import contracts.bar as Bar
+import contracts.ibar as IBar
@external
def foo(a: address) -> bool:
- return Bar(a).bar(1)
+ return IBar(a).bar(1)
"""
BAR_CODE = """
@@ -29,7 +29,6 @@ def bar(a: uint256) -> bool:
"inputs": [{"type": "uint256", "name": "a"}],
"stateMutability": "nonpayable",
"type": "function",
- "gas": 313,
}
]
@@ -39,7 +38,7 @@ def bar(a: uint256) -> bool:
"contracts/foo.vy": {"content": FOO_CODE},
"contracts/bar.vy": {"content": BAR_CODE},
},
- "interfaces": {"contracts/bar.json": {"abi": BAR_ABI}},
+ "interfaces": {"contracts/ibar.json": {"abi": BAR_ABI}},
"settings": {"outputSelection": {"*": ["*"]}},
}
diff --git a/tests/compiler/__init__.py b/tests/unit/compiler/__init__.py
similarity index 100%
rename from tests/compiler/__init__.py
rename to tests/unit/compiler/__init__.py
diff --git a/tests/compiler/asm/test_asm_optimizer.py b/tests/unit/compiler/asm/test_asm_optimizer.py
similarity index 50%
rename from tests/compiler/asm/test_asm_optimizer.py
rename to tests/unit/compiler/asm/test_asm_optimizer.py
index 47b70a8c70..44b823757c 100644
--- a/tests/compiler/asm/test_asm_optimizer.py
+++ b/tests/unit/compiler/asm/test_asm_optimizer.py
@@ -1,5 +1,6 @@
import pytest
+from vyper.compiler import compile_code
from vyper.compiler.phases import CompilerData
from vyper.compiler.settings import OptimizationLevel, Settings
@@ -71,33 +72,61 @@ def __init__():
]
+# check dead code eliminator works on unreachable functions
@pytest.mark.parametrize("code", codes)
def test_dead_code_eliminator(code):
c = CompilerData(code, settings=Settings(optimize=OptimizationLevel.NONE))
- initcode_asm = [i for i in c.assembly if not isinstance(i, list)]
- runtime_asm = c.assembly_runtime
- ctor_only_label = "_sym_internal_ctor_only___"
- runtime_only_label = "_sym_internal_runtime_only___"
+ # get the labels
+ initcode_asm = [i for i in c.assembly if isinstance(i, str)]
+ runtime_asm = [i for i in c.assembly_runtime if isinstance(i, str)]
+
+ ctor_only = "ctor_only()"
+ runtime_only = "runtime_only()"
# qux reachable from unoptimized initcode, foo not reachable.
- assert ctor_only_label + "_deploy" in initcode_asm
- assert runtime_only_label + "_deploy" not in initcode_asm
+ assert any(ctor_only in instr for instr in initcode_asm)
+ assert all(runtime_only not in instr for instr in initcode_asm)
# all labels should be in unoptimized runtime asm
- for s in (ctor_only_label, runtime_only_label):
- assert s + "_runtime" in runtime_asm
+ for s in (ctor_only, runtime_only):
+ assert any(s in instr for instr in runtime_asm)
c = CompilerData(code, settings=Settings(optimize=OptimizationLevel.GAS))
- initcode_asm = [i for i in c.assembly if not isinstance(i, list)]
- runtime_asm = c.assembly_runtime
+ initcode_asm = [i for i in c.assembly if isinstance(i, str)]
+ runtime_asm = [i for i in c.assembly_runtime if isinstance(i, str)]
# ctor only label should not be in runtime code
- for instr in runtime_asm:
- if isinstance(instr, str):
- assert not instr.startswith(ctor_only_label), instr
+ assert all(ctor_only not in instr for instr in runtime_asm)
# runtime only label should not be in initcode asm
- for instr in initcode_asm:
- if isinstance(instr, str):
- assert not instr.startswith(runtime_only_label), instr
+ assert all(runtime_only not in instr for instr in initcode_asm)
+
+
+def test_library_code_eliminator(make_input_bundle):
+ library = """
+@internal
+def unused1():
+ pass
+
+@internal
+def unused2():
+ self.unused1()
+
+@internal
+def some_function():
+ pass
+ """
+ code = """
+import library
+
+@external
+def foo():
+ library.some_function()
+ """
+ input_bundle = make_input_bundle({"library.vy": library})
+ res = compile_code(code, input_bundle=input_bundle, output_formats=["asm"])
+ asm = res["asm"]
+ assert "some_function()" in asm
+ assert "unused1()" not in asm
+ assert "unused2()" not in asm
diff --git a/tests/unit/compiler/ir/__init__.py b/tests/unit/compiler/ir/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/tests/compiler/test_calldatacopy.py b/tests/unit/compiler/ir/test_calldatacopy.py
similarity index 100%
rename from tests/compiler/test_calldatacopy.py
rename to tests/unit/compiler/ir/test_calldatacopy.py
diff --git a/tests/compiler/ir/test_compile_ir.py b/tests/unit/compiler/ir/test_compile_ir.py
similarity index 100%
rename from tests/compiler/ir/test_compile_ir.py
rename to tests/unit/compiler/ir/test_compile_ir.py
diff --git a/tests/compiler/ir/test_optimize_ir.py b/tests/unit/compiler/ir/test_optimize_ir.py
similarity index 70%
rename from tests/compiler/ir/test_optimize_ir.py
rename to tests/unit/compiler/ir/test_optimize_ir.py
index b679e55453..cb46ba238d 100644
--- a/tests/compiler/ir/test_optimize_ir.py
+++ b/tests/unit/compiler/ir/test_optimize_ir.py
@@ -1,9 +1,13 @@
import pytest
from vyper.codegen.ir_node import IRnode
+from vyper.evm.opcodes import EVM_VERSIONS, anchor_evm_version
from vyper.exceptions import StaticAssertionException
from vyper.ir import optimizer
+POST_CANCUN = {k: v for k, v in EVM_VERSIONS.items() if v >= EVM_VERSIONS["cancun"]}
+
+
optimize_list = [
(["eq", 1, 2], [0]),
(["lt", 1, 2], [1]),
@@ -143,7 +147,9 @@
(["sub", "x", 0], ["x"]),
(["sub", "x", "x"], [0]),
(["sub", ["sload", 0], ["sload", 0]], None),
- (["sub", ["callvalue"], ["callvalue"]], None),
+ (["sub", ["callvalue"], ["callvalue"]], [0]),
+ (["sub", ["msize"], ["msize"]], None),
+ (["sub", ["gas"], ["gas"]], None),
(["sub", -1, ["sload", 0]], ["not", ["sload", 0]]),
(["mul", "x", 1], ["x"]),
(["div", "x", 1], ["x"]),
@@ -210,7 +216,9 @@
(["eq", -1, ["add", -(2**255), 2**255 - 1]], [1]), # test compile-time wrapping
(["eq", -2, ["add", 2**256 - 1, 2**256 - 1]], [1]), # test compile-time wrapping
(["eq", "x", "x"], [1]),
- (["eq", "callvalue", "callvalue"], None),
+ (["eq", "gas", "gas"], None),
+ (["eq", "msize", "msize"], None),
+ (["eq", "callvalue", "callvalue"], [1]),
(["ne", "x", "x"], [0]),
]
@@ -268,3 +276,106 @@ def test_operator_set_values():
assert optimizer.COMPARISON_OPS == {"lt", "gt", "le", "ge", "slt", "sgt", "sle", "sge"}
assert optimizer.STRICT_COMPARISON_OPS == {"lt", "gt", "slt", "sgt"}
assert optimizer.UNSTRICT_COMPARISON_OPS == {"le", "ge", "sle", "sge"}
+
+
+mload_merge_list = [
+ # copy "backward" with no overlap between src and dst buffers,
+ # OK to become mcopy
+ (
+ ["seq", ["mstore", 32, ["mload", 128]], ["mstore", 64, ["mload", 160]]],
+ ["mcopy", 32, 128, 64],
+ ),
+ # copy with overlap "backwards", OK to become mcopy
+ (["seq", ["mstore", 32, ["mload", 64]], ["mstore", 64, ["mload", 96]]], ["mcopy", 32, 64, 64]),
+ # "stationary" overlap (i.e. a no-op mcopy), OK to become mcopy
+ (["seq", ["mstore", 32, ["mload", 32]], ["mstore", 64, ["mload", 64]]], ["mcopy", 32, 32, 64]),
+ # copy "forward" with no overlap, OK to become mcopy
+ (["seq", ["mstore", 64, ["mload", 0]], ["mstore", 96, ["mload", 32]]], ["mcopy", 64, 0, 64]),
+ # copy "forwards" with overlap by one word, must NOT become mcopy
+ (["seq", ["mstore", 64, ["mload", 32]], ["mstore", 96, ["mload", 64]]], None),
+ # check "forward" overlap by one byte, must NOT become mcopy
+ (["seq", ["mstore", 64, ["mload", 1]], ["mstore", 96, ["mload", 33]]], None),
+ # check "forward" overlap by one byte again, must NOT become mcopy
+ (["seq", ["mstore", 63, ["mload", 0]], ["mstore", 95, ["mload", 32]]], None),
+ # copy 3 words with partial overlap "forwards", partially becomes mcopy
+ # (2 words are mcopied and 1 word is mload/mstored
+ (
+ [
+ "seq",
+ ["mstore", 96, ["mload", 32]],
+ ["mstore", 128, ["mload", 64]],
+ ["mstore", 160, ["mload", 96]],
+ ],
+ ["seq", ["mcopy", 96, 32, 64], ["mstore", 160, ["mload", 96]]],
+ ),
+ # copy 4 words with partial overlap "forwards", becomes 2 mcopies of 2 words each
+ (
+ [
+ "seq",
+ ["mstore", 96, ["mload", 32]],
+ ["mstore", 128, ["mload", 64]],
+ ["mstore", 160, ["mload", 96]],
+ ["mstore", 192, ["mload", 128]],
+ ],
+ ["seq", ["mcopy", 96, 32, 64], ["mcopy", 160, 96, 64]],
+ ),
+ # copy 4 words with 1 byte of overlap, must NOT become mcopy
+ (
+ [
+ "seq",
+ ["mstore", 96, ["mload", 33]],
+ ["mstore", 128, ["mload", 65]],
+ ["mstore", 160, ["mload", 97]],
+ ["mstore", 192, ["mload", 129]],
+ ],
+ None,
+ ),
+ # Ensure only sequential mstore + mload sequences are optimized
+ (
+ [
+ "seq",
+ ["mstore", 0, ["mload", 32]],
+ ["sstore", 0, ["calldataload", 4]],
+ ["mstore", 32, ["mload", 64]],
+ ],
+ None,
+ ),
+ # not-word aligned optimizations (not overlap)
+ (["seq", ["mstore", 0, ["mload", 1]], ["mstore", 32, ["mload", 33]]], ["mcopy", 0, 1, 64]),
+ # not-word aligned optimizations (overlap)
+ (["seq", ["mstore", 1, ["mload", 0]], ["mstore", 33, ["mload", 32]]], None),
+ # not-word aligned optimizations (overlap and not-overlap)
+ (
+ [
+ "seq",
+ ["mstore", 0, ["mload", 1]],
+ ["mstore", 32, ["mload", 33]],
+ ["mstore", 1, ["mload", 0]],
+ ["mstore", 33, ["mload", 32]],
+ ],
+ ["seq", ["mcopy", 0, 1, 64], ["mstore", 1, ["mload", 0]], ["mstore", 33, ["mload", 32]]],
+ ),
+ # overflow test
+ (
+ [
+ "seq",
+ ["mstore", 2**256 - 1 - 31 - 32, ["mload", 0]],
+ ["mstore", 2**256 - 1 - 31, ["mload", 32]],
+ ],
+ ["mcopy", 2**256 - 1 - 31 - 32, 0, 64],
+ ),
+]
+
+
+@pytest.mark.parametrize("ir", mload_merge_list)
+@pytest.mark.parametrize("evm_version", list(POST_CANCUN.keys()))
+def test_mload_merge(ir, evm_version):
+ with anchor_evm_version(evm_version):
+ optimized = optimizer.optimize(IRnode.from_list(ir[0]))
+ if ir[1] is None:
+ # no-op, assert optimizer does nothing
+ expected = IRnode.from_list(ir[0])
+ else:
+ expected = IRnode.from_list(ir[1])
+
+ assert optimized == expected
diff --git a/tests/compiler/ir/test_repeat.py b/tests/unit/compiler/ir/test_repeat.py
similarity index 100%
rename from tests/compiler/ir/test_repeat.py
rename to tests/unit/compiler/ir/test_repeat.py
diff --git a/tests/compiler/ir/test_with.py b/tests/unit/compiler/ir/test_with.py
similarity index 100%
rename from tests/compiler/ir/test_with.py
rename to tests/unit/compiler/ir/test_with.py
diff --git a/tests/compiler/test_bytecode_runtime.py b/tests/unit/compiler/test_bytecode_runtime.py
similarity index 86%
rename from tests/compiler/test_bytecode_runtime.py
rename to tests/unit/compiler/test_bytecode_runtime.py
index 9519b03772..613ee4d2b8 100644
--- a/tests/compiler/test_bytecode_runtime.py
+++ b/tests/unit/compiler/test_bytecode_runtime.py
@@ -48,14 +48,14 @@ def _parse_cbor_metadata(initcode):
def test_bytecode_runtime():
- out = vyper.compile_code(simple_contract_code, ["bytecode_runtime", "bytecode"])
+ out = vyper.compile_code(simple_contract_code, output_formats=["bytecode_runtime", "bytecode"])
assert len(out["bytecode"]) > len(out["bytecode_runtime"])
assert out["bytecode_runtime"].removeprefix("0x") in out["bytecode"].removeprefix("0x")
def test_bytecode_signature():
- out = vyper.compile_code(simple_contract_code, ["bytecode_runtime", "bytecode"])
+ out = vyper.compile_code(simple_contract_code, output_formats=["bytecode_runtime", "bytecode"])
runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x"))
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))
@@ -72,7 +72,9 @@ def test_bytecode_signature():
def test_bytecode_signature_dense_jumptable():
settings = Settings(optimize=OptimizationLevel.CODESIZE)
- out = vyper.compile_code(many_functions, ["bytecode_runtime", "bytecode"], settings=settings)
+ out = vyper.compile_code(
+ many_functions, output_formats=["bytecode_runtime", "bytecode"], settings=settings
+ )
runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x"))
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))
@@ -89,7 +91,9 @@ def test_bytecode_signature_dense_jumptable():
def test_bytecode_signature_sparse_jumptable():
settings = Settings(optimize=OptimizationLevel.GAS)
- out = vyper.compile_code(many_functions, ["bytecode_runtime", "bytecode"], settings=settings)
+ out = vyper.compile_code(
+ many_functions, output_formats=["bytecode_runtime", "bytecode"], settings=settings
+ )
runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x"))
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))
@@ -104,7 +108,7 @@ def test_bytecode_signature_sparse_jumptable():
def test_bytecode_signature_immutables():
- out = vyper.compile_code(has_immutables, ["bytecode_runtime", "bytecode"])
+ out = vyper.compile_code(has_immutables, output_formats=["bytecode_runtime", "bytecode"])
runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x"))
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))
diff --git a/tests/compiler/test_compile_code.py b/tests/unit/compiler/test_compile_code.py
similarity index 99%
rename from tests/compiler/test_compile_code.py
rename to tests/unit/compiler/test_compile_code.py
index cdbf9d1f52..7af133e362 100644
--- a/tests/compiler/test_compile_code.py
+++ b/tests/unit/compiler/test_compile_code.py
@@ -11,4 +11,4 @@ def a() -> bool:
return True
"""
with pytest.warns(vyper.warnings.ContractSizeLimitWarning):
- vyper.compile_code(code, ["bytecode_runtime"])
+ vyper.compile_code(code, output_formats=["bytecode_runtime"])
diff --git a/tests/compiler/test_default_settings.py b/tests/unit/compiler/test_default_settings.py
similarity index 100%
rename from tests/compiler/test_default_settings.py
rename to tests/unit/compiler/test_default_settings.py
diff --git a/tests/unit/compiler/test_input_bundle.py b/tests/unit/compiler/test_input_bundle.py
new file mode 100644
index 0000000000..621b529722
--- /dev/null
+++ b/tests/unit/compiler/test_input_bundle.py
@@ -0,0 +1,244 @@
+import json
+from pathlib import Path, PurePath
+
+import pytest
+
+from tests.utils import working_directory
+from vyper.compiler.input_bundle import ABIInput, FileInput, FilesystemInputBundle, JSONInputBundle
+
+
+# FilesystemInputBundle which uses same search path as make_file
+@pytest.fixture
+def input_bundle(tmp_path):
+ return FilesystemInputBundle([tmp_path])
+
+
+def test_load_file(make_file, input_bundle):
+ filepath = make_file("foo.vy", "contents")
+
+ file = input_bundle.load_file(Path("foo.vy"))
+
+ assert isinstance(file, FileInput)
+ assert file == FileInput(0, Path("foo.vy"), filepath, "contents")
+
+
+def test_search_path_context_manager(make_file, tmp_path):
+ ib = FilesystemInputBundle([])
+
+ filepath = make_file("foo.vy", "contents")
+
+ with pytest.raises(FileNotFoundError):
+ # no search path given
+ ib.load_file(Path("foo.vy"))
+
+ with ib.search_path(tmp_path):
+ file = ib.load_file(Path("foo.vy"))
+
+ assert isinstance(file, FileInput)
+ assert file == FileInput(0, Path("foo.vy"), filepath, "contents")
+
+
+def test_search_path_precedence(make_file, tmp_path, tmp_path_factory, input_bundle):
+ # test search path precedence.
+ # most recent search path is the highest precedence
+ tmpdir = tmp_path_factory.mktemp("some_directory")
+ tmpdir2 = tmp_path_factory.mktemp("some_other_directory")
+
+ filepaths = []
+ for i, directory in enumerate([tmp_path, tmpdir, tmpdir2]):
+ path = directory / "foo.vy"
+ with path.open("w") as f:
+ f.write(f"contents {i}")
+ filepaths.append(path)
+
+ ib = FilesystemInputBundle([tmp_path, tmpdir, tmpdir2])
+
+ file = ib.load_file("foo.vy")
+
+ assert isinstance(file, FileInput)
+ assert file == FileInput(0, "foo.vy", filepaths[2], "contents 2")
+
+ with ib.search_path(tmpdir):
+ file = ib.load_file("foo.vy")
+
+ assert isinstance(file, FileInput)
+ assert file == FileInput(1, "foo.vy", filepaths[1], "contents 1")
+
+
+# special rules for handling json files
+def test_load_abi(make_file, input_bundle, tmp_path):
+ contents = json.dumps("some string")
+
+ path = make_file("foo.json", contents)
+
+ file = input_bundle.load_file("foo.json")
+ assert isinstance(file, ABIInput)
+ assert file == ABIInput(0, "foo.json", path, "some string")
+
+ # suffix doesn't matter
+ path = make_file("foo.txt", contents)
+ file = input_bundle.load_file("foo.txt")
+ assert isinstance(file, ABIInput)
+ assert file == ABIInput(1, "foo.txt", path, "some string")
+
+
+# check that unique paths give unique source ids
+def test_source_id_file_input(make_file, input_bundle, tmp_path):
+ foopath = make_file("foo.vy", "contents")
+ barpath = make_file("bar.vy", "contents 2")
+
+ file = input_bundle.load_file("foo.vy")
+ assert file.source_id == 0
+ assert file == FileInput(0, "foo.vy", foopath, "contents")
+
+ file2 = input_bundle.load_file("bar.vy")
+ # source id increments
+ assert file2.source_id == 1
+ assert file2 == FileInput(1, "bar.vy", barpath, "contents 2")
+
+ file3 = input_bundle.load_file("foo.vy")
+ assert file3.source_id == 0
+ assert file3 == FileInput(0, "foo.vy", foopath, "contents")
+
+ # test source id is stable across different search paths
+ with working_directory(tmp_path):
+ with input_bundle.search_path(Path(".")):
+ file4 = input_bundle.load_file("foo.vy")
+ assert file4.source_id == 0
+ assert file4 == FileInput(0, "foo.vy", foopath, "contents")
+
+ # test source id is stable even when requested filename is different
+ with working_directory(tmp_path.parent):
+ with input_bundle.search_path(Path(".")):
+ file5 = input_bundle.load_file(Path(tmp_path.stem) / "foo.vy")
+ assert file5.source_id == 0
+ assert file5 == FileInput(0, Path(tmp_path.stem) / "foo.vy", foopath, "contents")
+
+
+# check that unique paths give unique source ids
+def test_source_id_json_input(make_file, input_bundle, tmp_path):
+ contents = json.dumps("some string")
+ contents2 = json.dumps(["some list"])
+
+ foopath = make_file("foo.json", contents)
+
+ barpath = make_file("bar.json", contents2)
+
+ file = input_bundle.load_file("foo.json")
+ assert isinstance(file, ABIInput)
+ assert file == ABIInput(0, "foo.json", foopath, "some string")
+
+ file2 = input_bundle.load_file("bar.json")
+ assert isinstance(file2, ABIInput)
+ assert file2 == ABIInput(1, "bar.json", barpath, ["some list"])
+
+ file3 = input_bundle.load_file("foo.json")
+ assert file3.source_id == 0
+ assert file3 == ABIInput(0, "foo.json", foopath, "some string")
+
+ # test source id is stable across different search paths
+ with working_directory(tmp_path):
+ with input_bundle.search_path(Path(".")):
+ file4 = input_bundle.load_file("foo.json")
+ assert file4.source_id == 0
+ assert file4 == ABIInput(0, "foo.json", foopath, "some string")
+
+ # test source id is stable even when requested filename is different
+ with working_directory(tmp_path.parent):
+ with input_bundle.search_path(Path(".")):
+ file5 = input_bundle.load_file(Path(tmp_path.stem) / "foo.json")
+ assert file5.source_id == 0
+ assert file5 == ABIInput(0, Path(tmp_path.stem) / "foo.json", foopath, "some string")
+
+
+# test some pathological case where the file changes underneath
+def test_mutating_file_source_id(make_file, input_bundle, tmp_path):
+ foopath = make_file("foo.vy", "contents")
+
+ file = input_bundle.load_file("foo.vy")
+ assert file.source_id == 0
+ assert file == FileInput(0, "foo.vy", foopath, "contents")
+
+ foopath = make_file("foo.vy", "new contents")
+
+ file = input_bundle.load_file("foo.vy")
+ # source id hasn't changed, even though contents have
+ assert file.source_id == 0
+ assert file == FileInput(0, "foo.vy", foopath, "new contents")
+
+
+# test the os.normpath behavior of symlink
+# (slightly pathological, for illustration's sake)
+def test_load_file_symlink(make_file, input_bundle, tmp_path, tmp_path_factory):
+ dir1 = tmp_path / "first"
+ dir2 = tmp_path / "second"
+ symlink = tmp_path / "symlink"
+
+ dir1.mkdir()
+ dir2.mkdir()
+ symlink.symlink_to(dir2, target_is_directory=True)
+
+ outer_path = tmp_path / "foo.vy"
+ with outer_path.open("w") as f:
+ f.write("contents of the outer directory")
+
+ inner_path = dir1 / "foo.vy"
+ with inner_path.open("w") as f:
+ f.write("contents of the inner directory")
+
+ # symlink rules would be:
+ # base/symlink/../foo.vy =>
+ # base/first/second/../foo.vy =>
+ # base/first/foo.vy
+ # normpath would be base/symlink/../foo.vy =>
+ # base/foo.vy
+ to_load = symlink / ".." / "foo.vy"
+ file = input_bundle.load_file(to_load)
+
+ assert file == FileInput(0, to_load, outer_path.resolve(), "contents of the outer directory")
+
+
+def test_json_input_bundle_basic():
+ files = {PurePath("foo.vy"): {"content": "some text"}}
+ input_bundle = JSONInputBundle(files, [PurePath(".")])
+
+ file = input_bundle.load_file(PurePath("foo.vy"))
+ assert file == FileInput(0, PurePath("foo.vy"), PurePath("foo.vy"), "some text")
+
+
+def test_json_input_bundle_normpath():
+ contents = "some text"
+ files = {PurePath("foo/../bar.vy"): {"content": contents}}
+ input_bundle = JSONInputBundle(files, [PurePath(".")])
+
+ barpath = PurePath("bar.vy")
+
+ expected = FileInput(0, barpath, barpath, contents)
+
+ file = input_bundle.load_file(PurePath("bar.vy"))
+ assert file == expected
+
+ file = input_bundle.load_file(PurePath("baz/../bar.vy"))
+ assert file == FileInput(0, PurePath("baz/../bar.vy"), barpath, contents)
+
+ file = input_bundle.load_file(PurePath("./bar.vy"))
+ assert file == FileInput(0, PurePath("./bar.vy"), barpath, contents)
+
+ with input_bundle.search_path(PurePath("foo")):
+ file = input_bundle.load_file(PurePath("../bar.vy"))
+ assert file == FileInput(0, PurePath("../bar.vy"), barpath, contents)
+
+
+def test_json_input_abi():
+ some_abi = ["some abi"]
+ some_abi_str = json.dumps(some_abi)
+ foopath = PurePath("foo.json")
+ barpath = PurePath("bar.txt")
+ files = {foopath: {"abi": some_abi}, barpath: {"content": some_abi_str}}
+ input_bundle = JSONInputBundle(files, [PurePath(".")])
+
+ file = input_bundle.load_file(foopath)
+ assert file == ABIInput(0, foopath, foopath, some_abi)
+
+ file = input_bundle.load_file(barpath)
+ assert file == ABIInput(1, barpath, barpath, some_abi)
diff --git a/tests/compiler/test_opcodes.py b/tests/unit/compiler/test_opcodes.py
similarity index 95%
rename from tests/compiler/test_opcodes.py
rename to tests/unit/compiler/test_opcodes.py
index 20f45ced6b..15d2a617ba 100644
--- a/tests/compiler/test_opcodes.py
+++ b/tests/unit/compiler/test_opcodes.py
@@ -22,7 +22,7 @@ def a() -> bool:
return True
"""
- out = vyper.compile_code(code, ["opcodes_runtime", "opcodes"])
+ out = vyper.compile_code(code, output_formats=["opcodes_runtime", "opcodes"])
assert len(out["opcodes"]) > len(out["opcodes_runtime"])
assert out["opcodes_runtime"] in out["opcodes"]
diff --git a/tests/compiler/test_pre_parser.py b/tests/unit/compiler/test_pre_parser.py
similarity index 100%
rename from tests/compiler/test_pre_parser.py
rename to tests/unit/compiler/test_pre_parser.py
diff --git a/tests/compiler/test_sha3_32.py b/tests/unit/compiler/test_sha3_32.py
similarity index 100%
rename from tests/compiler/test_sha3_32.py
rename to tests/unit/compiler/test_sha3_32.py
diff --git a/tests/compiler/test_source_map.py b/tests/unit/compiler/test_source_map.py
similarity index 91%
rename from tests/compiler/test_source_map.py
rename to tests/unit/compiler/test_source_map.py
index 886596bb80..c9a152b09c 100644
--- a/tests/compiler/test_source_map.py
+++ b/tests/unit/compiler/test_source_map.py
@@ -28,7 +28,7 @@ def foo(a: uint256) -> int128:
def test_jump_map():
- source_map = compile_code(TEST_CODE, ["source_map"])["source_map"]
+ source_map = compile_code(TEST_CODE, output_formats=["source_map"])["source_map"]
pos_map = source_map["pc_pos_map"]
jump_map = source_map["pc_jump_map"]
@@ -46,7 +46,7 @@ def test_jump_map():
def test_pos_map_offsets():
- source_map = compile_code(TEST_CODE, ["source_map"])["source_map"]
+ source_map = compile_code(TEST_CODE, output_formats=["source_map"])["source_map"]
expanded = expand_source_map(source_map["pc_pos_map_compressed"])
pc_iter = iter(source_map["pc_pos_map"][i] for i in sorted(source_map["pc_pos_map"]))
@@ -76,7 +76,7 @@ def test_error_map():
def update_foo():
self.foo += 1
"""
- error_map = compile_code(code, ["source_map"])["source_map"]["error_map"]
+ error_map = compile_code(code, output_formats=["source_map"])["source_map"]["error_map"]
assert "safeadd" in list(error_map.values())
assert "fallback function" in list(error_map.values())
diff --git a/tests/unit/compiler/venom/test_duplicate_operands.py b/tests/unit/compiler/venom/test_duplicate_operands.py
new file mode 100644
index 0000000000..b96c7f3351
--- /dev/null
+++ b/tests/unit/compiler/venom/test_duplicate_operands.py
@@ -0,0 +1,27 @@
+from vyper.compiler.settings import OptimizationLevel
+from vyper.venom import generate_assembly_experimental
+from vyper.venom.function import IRFunction
+
+
+def test_duplicate_operands():
+ """
+ Test the duplicate operands code generation.
+ The venom code:
+
+ %1 = 10
+ %2 = add %1, %1
+ %3 = mul %1, %2
+ stop
+
+ Should compile to: [PUSH1, 10, DUP1, DUP1, DUP1, ADD, MUL, STOP]
+ """
+ ctx = IRFunction()
+ bb = ctx.get_basic_block()
+ op = bb.append_instruction("store", 10)
+ sum_ = bb.append_instruction("add", op, op)
+ bb.append_instruction("mul", sum_, op)
+ bb.append_instruction("stop")
+
+ asm = generate_assembly_experimental(ctx, optimize=OptimizationLevel.CODESIZE)
+
+ assert asm == ["PUSH1", 10, "DUP1", "DUP1", "DUP1", "ADD", "MUL", "STOP", "REVERT"]
diff --git a/tests/unit/compiler/venom/test_multi_entry_block.py b/tests/unit/compiler/venom/test_multi_entry_block.py
new file mode 100644
index 0000000000..6d8b074994
--- /dev/null
+++ b/tests/unit/compiler/venom/test_multi_entry_block.py
@@ -0,0 +1,138 @@
+from vyper.venom.analysis import calculate_cfg
+from vyper.venom.function import IRBasicBlock, IRFunction, IRLabel
+from vyper.venom.passes.normalization import NormalizationPass
+
+
+def test_multi_entry_block_1():
+ ctx = IRFunction()
+
+ finish_label = IRLabel("finish")
+ target_label = IRLabel("target")
+ block_1_label = IRLabel("block_1", ctx)
+
+ bb = ctx.get_basic_block()
+ op = bb.append_instruction("store", 10)
+ acc = bb.append_instruction("add", op, op)
+ bb.append_instruction("jnz", acc, finish_label, block_1_label)
+
+ block_1 = IRBasicBlock(block_1_label, ctx)
+ ctx.append_basic_block(block_1)
+ acc = block_1.append_instruction("add", acc, op)
+ op = block_1.append_instruction("store", 10)
+ block_1.append_instruction("mstore", acc, op)
+ block_1.append_instruction("jnz", acc, finish_label, target_label)
+
+ target_bb = IRBasicBlock(target_label, ctx)
+ ctx.append_basic_block(target_bb)
+ target_bb.append_instruction("mul", acc, acc)
+ target_bb.append_instruction("jmp", finish_label)
+
+ finish_bb = IRBasicBlock(finish_label, ctx)
+ ctx.append_basic_block(finish_bb)
+ finish_bb.append_instruction("stop")
+
+ calculate_cfg(ctx)
+ assert not ctx.normalized, "CFG should not be normalized"
+
+ NormalizationPass.run_pass(ctx)
+
+ assert ctx.normalized, "CFG should be normalized"
+
+ finish_bb = ctx.get_basic_block(finish_label.value)
+ cfg_in = list(finish_bb.cfg_in.keys())
+ assert cfg_in[0].label.value == "target", "Should contain target"
+ assert cfg_in[1].label.value == "finish_split___global", "Should contain finish_split___global"
+ assert cfg_in[2].label.value == "finish_split_block_1", "Should contain finish_split_block_1"
+
+
+# more complicated one
+def test_multi_entry_block_2():
+ ctx = IRFunction()
+
+ finish_label = IRLabel("finish")
+ target_label = IRLabel("target")
+ block_1_label = IRLabel("block_1", ctx)
+ block_2_label = IRLabel("block_2", ctx)
+
+ bb = ctx.get_basic_block()
+ op = bb.append_instruction("store", 10)
+ acc = bb.append_instruction("add", op, op)
+ bb.append_instruction("jnz", acc, finish_label, block_1_label)
+
+ block_1 = IRBasicBlock(block_1_label, ctx)
+ ctx.append_basic_block(block_1)
+ acc = block_1.append_instruction("add", acc, op)
+ op = block_1.append_instruction("store", 10)
+ block_1.append_instruction("mstore", acc, op)
+ block_1.append_instruction("jnz", acc, target_label, finish_label)
+
+ block_2 = IRBasicBlock(block_2_label, ctx)
+ ctx.append_basic_block(block_2)
+ acc = block_2.append_instruction("add", acc, op)
+ op = block_2.append_instruction("store", 10)
+ block_2.append_instruction("mstore", acc, op)
+ # switch the order of the labels, for fun and profit
+ block_2.append_instruction("jnz", acc, finish_label, target_label)
+
+ target_bb = IRBasicBlock(target_label, ctx)
+ ctx.append_basic_block(target_bb)
+ target_bb.append_instruction("mul", acc, acc)
+ target_bb.append_instruction("jmp", finish_label)
+
+ finish_bb = IRBasicBlock(finish_label, ctx)
+ ctx.append_basic_block(finish_bb)
+ finish_bb.append_instruction("stop")
+
+ calculate_cfg(ctx)
+ assert not ctx.normalized, "CFG should not be normalized"
+
+ NormalizationPass.run_pass(ctx)
+
+ assert ctx.normalized, "CFG should be normalized"
+
+ finish_bb = ctx.get_basic_block(finish_label.value)
+ cfg_in = list(finish_bb.cfg_in.keys())
+ assert cfg_in[0].label.value == "target", "Should contain target"
+ assert cfg_in[1].label.value == "finish_split___global", "Should contain finish_split___global"
+ assert cfg_in[2].label.value == "finish_split_block_1", "Should contain finish_split_block_1"
+
+
+def test_multi_entry_block_with_dynamic_jump():
+ ctx = IRFunction()
+
+ finish_label = IRLabel("finish")
+ target_label = IRLabel("target")
+ block_1_label = IRLabel("block_1", ctx)
+
+ bb = ctx.get_basic_block()
+ op = bb.append_instruction("store", 10)
+ acc = bb.append_instruction("add", op, op)
+ bb.append_instruction("djmp", acc, finish_label, block_1_label)
+
+ block_1 = IRBasicBlock(block_1_label, ctx)
+ ctx.append_basic_block(block_1)
+ acc = block_1.append_instruction("add", acc, op)
+ op = block_1.append_instruction("store", 10)
+ block_1.append_instruction("mstore", acc, op)
+ block_1.append_instruction("jnz", acc, finish_label, target_label)
+
+ target_bb = IRBasicBlock(target_label, ctx)
+ ctx.append_basic_block(target_bb)
+ target_bb.append_instruction("mul", acc, acc)
+ target_bb.append_instruction("jmp", finish_label)
+
+ finish_bb = IRBasicBlock(finish_label, ctx)
+ ctx.append_basic_block(finish_bb)
+ finish_bb.append_instruction("stop")
+
+ calculate_cfg(ctx)
+ assert not ctx.normalized, "CFG should not be normalized"
+
+ NormalizationPass.run_pass(ctx)
+ assert ctx.normalized, "CFG should be normalized"
+
+ finish_bb = ctx.get_basic_block(finish_label.value)
+ cfg_in = list(finish_bb.cfg_in.keys())
+ assert cfg_in[0].label.value == "target", "Should contain target"
+ assert cfg_in[1].label.value == "finish_split___global", "Should contain finish_split___global"
+ assert cfg_in[2].label.value == "finish_split_block_1", "Should contain finish_split_block_1"
diff --git a/tests/unit/compiler/venom/test_stack_at_external_return.py b/tests/unit/compiler/venom/test_stack_at_external_return.py
new file mode 100644
index 0000000000..be9fa66e9a
--- /dev/null
+++ b/tests/unit/compiler/venom/test_stack_at_external_return.py
@@ -0,0 +1,5 @@
+def test_stack_at_external_return():
+ """
+ TODO: USE BOA DO GENERATE THIS TEST
+ """
+ pass
diff --git a/tests/functional/semantics/analysis/test_array_index.py b/tests/unit/semantics/analysis/test_array_index.py
similarity index 69%
rename from tests/functional/semantics/analysis/test_array_index.py
rename to tests/unit/semantics/analysis/test_array_index.py
index 27c0634cf8..5ea373fc19 100644
--- a/tests/functional/semantics/analysis/test_array_index.py
+++ b/tests/unit/semantics/analysis/test_array_index.py
@@ -12,7 +12,7 @@
@pytest.mark.parametrize("value", ["address", "Bytes[10]", "decimal", "bool"])
-def test_type_mismatch(namespace, value):
+def test_type_mismatch(namespace, value, dummy_input_bundle):
code = f"""
a: uint256[3]
@@ -23,11 +23,11 @@ def foo(b: {value}):
"""
vyper_module = parse_to_ast(code)
with pytest.raises(TypeMismatch):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
@pytest.mark.parametrize("value", ["1.0", "0.0", "'foo'", "0x00", "b'\x01'", "False"])
-def test_invalid_literal(namespace, value):
+def test_invalid_literal(namespace, value, dummy_input_bundle):
code = f"""
a: uint256[3]
@@ -38,11 +38,11 @@ def foo():
"""
vyper_module = parse_to_ast(code)
with pytest.raises(InvalidType):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
@pytest.mark.parametrize("value", [-1, 3, -(2**127), 2**127 - 1, 2**256 - 1])
-def test_out_of_bounds(namespace, value):
+def test_out_of_bounds(namespace, value, dummy_input_bundle):
code = f"""
a: uint256[3]
@@ -53,11 +53,11 @@ def foo():
"""
vyper_module = parse_to_ast(code)
with pytest.raises(ArrayIndexException):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
@pytest.mark.parametrize("value", ["b", "self.b"])
-def test_undeclared_definition(namespace, value):
+def test_undeclared_definition(namespace, value, dummy_input_bundle):
code = f"""
a: uint256[3]
@@ -68,11 +68,11 @@ def foo():
"""
vyper_module = parse_to_ast(code)
with pytest.raises(UndeclaredDefinition):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
@pytest.mark.parametrize("value", ["a", "foo", "int128"])
-def test_invalid_reference(namespace, value):
+def test_invalid_reference(namespace, value, dummy_input_bundle):
code = f"""
a: uint256[3]
@@ -83,4 +83,4 @@ def foo():
"""
vyper_module = parse_to_ast(code)
with pytest.raises(InvalidReference):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
diff --git a/tests/functional/semantics/analysis/test_cyclic_function_calls.py b/tests/unit/semantics/analysis/test_cyclic_function_calls.py
similarity index 53%
rename from tests/functional/semantics/analysis/test_cyclic_function_calls.py
rename to tests/unit/semantics/analysis/test_cyclic_function_calls.py
index 2a09bd5ed5..c31146b16f 100644
--- a/tests/functional/semantics/analysis/test_cyclic_function_calls.py
+++ b/tests/unit/semantics/analysis/test_cyclic_function_calls.py
@@ -3,22 +3,20 @@
from vyper.ast import parse_to_ast
from vyper.exceptions import CallViolation, StructureException
from vyper.semantics.analysis import validate_semantics
-from vyper.semantics.analysis.module import ModuleAnalyzer
-def test_self_function_call(namespace):
+def test_self_function_call(dummy_input_bundle):
code = """
@internal
def foo():
self.foo()
"""
vyper_module = parse_to_ast(code)
- with namespace.enter_scope():
- with pytest.raises(CallViolation):
- ModuleAnalyzer(vyper_module, {}, namespace)
+ with pytest.raises(CallViolation):
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_cyclic_function_call(namespace):
+def test_cyclic_function_call(dummy_input_bundle):
code = """
@internal
def foo():
@@ -29,12 +27,11 @@ def bar():
self.foo()
"""
vyper_module = parse_to_ast(code)
- with namespace.enter_scope():
- with pytest.raises(CallViolation):
- ModuleAnalyzer(vyper_module, {}, namespace)
+ with pytest.raises(CallViolation):
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_multi_cyclic_function_call(namespace):
+def test_multi_cyclic_function_call(dummy_input_bundle):
code = """
@internal
def foo():
@@ -53,12 +50,11 @@ def potato():
self.foo()
"""
vyper_module = parse_to_ast(code)
- with namespace.enter_scope():
- with pytest.raises(CallViolation):
- ModuleAnalyzer(vyper_module, {}, namespace)
+ with pytest.raises(CallViolation):
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_global_ann_assign_callable_no_crash():
+def test_global_ann_assign_callable_no_crash(dummy_input_bundle):
code = """
balanceOf: public(HashMap[address, uint256])
@@ -68,5 +64,5 @@ def foo(to : address):
"""
vyper_module = parse_to_ast(code)
with pytest.raises(StructureException) as excinfo:
- validate_semantics(vyper_module, {})
- assert excinfo.value.message == "Value is not callable"
+ validate_semantics(vyper_module, dummy_input_bundle)
+ assert excinfo.value.message == "HashMap[address, uint256] is not callable"
diff --git a/tests/functional/semantics/analysis/test_for_loop.py b/tests/unit/semantics/analysis/test_for_loop.py
similarity index 72%
rename from tests/functional/semantics/analysis/test_for_loop.py
rename to tests/unit/semantics/analysis/test_for_loop.py
index 0d61a8f8f8..e2c0f555af 100644
--- a/tests/functional/semantics/analysis/test_for_loop.py
+++ b/tests/unit/semantics/analysis/test_for_loop.py
@@ -10,7 +10,7 @@
from vyper.semantics.analysis import validate_semantics
-def test_modify_iterator_function_outside_loop(namespace):
+def test_modify_iterator_function_outside_loop(dummy_input_bundle):
code = """
a: uint256[3]
@@ -26,10 +26,10 @@ def bar():
pass
"""
vyper_module = parse_to_ast(code)
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_pass_memory_var_to_other_function(namespace):
+def test_pass_memory_var_to_other_function(dummy_input_bundle):
code = """
@internal
@@ -46,10 +46,10 @@ def bar():
self.foo(a)
"""
vyper_module = parse_to_ast(code)
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_modify_iterator(namespace):
+def test_modify_iterator(dummy_input_bundle):
code = """
a: uint256[3]
@@ -61,10 +61,10 @@ def bar():
"""
vyper_module = parse_to_ast(code)
with pytest.raises(ImmutableViolation):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_bad_keywords(namespace):
+def test_bad_keywords(dummy_input_bundle):
code = """
@internal
@@ -75,10 +75,10 @@ def bar(n: uint256):
"""
vyper_module = parse_to_ast(code)
with pytest.raises(ArgumentException):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_bad_bound(namespace):
+def test_bad_bound(dummy_input_bundle):
code = """
@internal
@@ -89,10 +89,10 @@ def bar(n: uint256):
"""
vyper_module = parse_to_ast(code)
with pytest.raises(StateAccessViolation):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_modify_iterator_function_call(namespace):
+def test_modify_iterator_function_call(dummy_input_bundle):
code = """
a: uint256[3]
@@ -108,10 +108,10 @@ def bar():
"""
vyper_module = parse_to_ast(code)
with pytest.raises(ImmutableViolation):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
-def test_modify_iterator_recursive_function_call(namespace):
+def test_modify_iterator_recursive_function_call(dummy_input_bundle):
code = """
a: uint256[3]
@@ -131,7 +131,7 @@ def baz():
"""
vyper_module = parse_to_ast(code)
with pytest.raises(ImmutableViolation):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
iterator_inference_codes = [
@@ -169,7 +169,7 @@ def foo():
@pytest.mark.parametrize("code", iterator_inference_codes)
-def test_iterator_type_inference_checker(namespace, code):
+def test_iterator_type_inference_checker(code, dummy_input_bundle):
vyper_module = parse_to_ast(code)
with pytest.raises(TypeMismatch):
- validate_semantics(vyper_module, {})
+ validate_semantics(vyper_module, dummy_input_bundle)
diff --git a/tests/functional/semantics/analysis/test_potential_types.py b/tests/unit/semantics/analysis/test_potential_types.py
similarity index 100%
rename from tests/functional/semantics/analysis/test_potential_types.py
rename to tests/unit/semantics/analysis/test_potential_types.py
diff --git a/tests/functional/semantics/conftest.py b/tests/unit/semantics/conftest.py
similarity index 100%
rename from tests/functional/semantics/conftest.py
rename to tests/unit/semantics/conftest.py
diff --git a/tests/functional/semantics/test_namespace.py b/tests/unit/semantics/test_namespace.py
similarity index 100%
rename from tests/functional/semantics/test_namespace.py
rename to tests/unit/semantics/test_namespace.py
diff --git a/tests/functional/test_storage_slots.py b/tests/unit/semantics/test_storage_slots.py
similarity index 99%
rename from tests/functional/test_storage_slots.py
rename to tests/unit/semantics/test_storage_slots.py
index d390fe9a39..002ee38cd2 100644
--- a/tests/functional/test_storage_slots.py
+++ b/tests/unit/semantics/test_storage_slots.py
@@ -110,6 +110,6 @@ def test_allocator_overflow(get_contract):
"""
with pytest.raises(
StorageLayoutException,
- match=f"Invalid storage slot for var y, tried to allocate slots 1 through {2**256}\n",
+ match=f"Invalid storage slot for var y, tried to allocate slots 1 through {2**256}",
):
get_contract(code)
diff --git a/tests/functional/semantics/types/test_event.py b/tests/unit/semantics/types/test_event.py
similarity index 100%
rename from tests/functional/semantics/types/test_event.py
rename to tests/unit/semantics/types/test_event.py
diff --git a/tests/functional/semantics/types/test_pure_types.py b/tests/unit/semantics/types/test_pure_types.py
similarity index 100%
rename from tests/functional/semantics/types/test_pure_types.py
rename to tests/unit/semantics/types/test_pure_types.py
diff --git a/tests/functional/semantics/types/test_size_in_bytes.py b/tests/unit/semantics/types/test_size_in_bytes.py
similarity index 100%
rename from tests/functional/semantics/types/test_size_in_bytes.py
rename to tests/unit/semantics/types/test_size_in_bytes.py
diff --git a/tests/functional/semantics/types/test_type_from_abi.py b/tests/unit/semantics/types/test_type_from_abi.py
similarity index 100%
rename from tests/functional/semantics/types/test_type_from_abi.py
rename to tests/unit/semantics/types/test_type_from_abi.py
diff --git a/tests/functional/semantics/types/test_type_from_annotation.py b/tests/unit/semantics/types/test_type_from_annotation.py
similarity index 100%
rename from tests/functional/semantics/types/test_type_from_annotation.py
rename to tests/unit/semantics/types/test_type_from_annotation.py
diff --git a/tests/utils.py b/tests/utils.py
new file mode 100644
index 0000000000..0c89c39ff3
--- /dev/null
+++ b/tests/utils.py
@@ -0,0 +1,12 @@
+import contextlib
+import os
+
+
+@contextlib.contextmanager
+def working_directory(directory):
+ tmp = os.getcwd()
+ try:
+ os.chdir(directory)
+ yield
+ finally:
+ os.chdir(tmp)
diff --git a/tox.ini b/tox.ini
index c949354dfe..f9d4c3b60b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -53,4 +53,4 @@ commands =
basepython = python3
extras = lint
commands =
- mypy --install-types --non-interactive --follow-imports=silent --ignore-missing-imports --disallow-incomplete-defs -p vyper
+ mypy --install-types --non-interactive --follow-imports=silent --ignore-missing-imports --implicit-optional -p vyper
diff --git a/vyper/__init__.py b/vyper/__init__.py
index 35237bd044..5bb6469757 100644
--- a/vyper/__init__.py
+++ b/vyper/__init__.py
@@ -1,6 +1,6 @@
from pathlib import Path as _Path
-from vyper.compiler import compile_code, compile_codes # noqa: F401
+from vyper.compiler import compile_code, compile_from_file_input
try:
from importlib.metadata import PackageNotFoundError # type: ignore
diff --git a/vyper/__main__.py b/vyper/__main__.py
index 371975c301..c5bda47bea 100644
--- a/vyper/__main__.py
+++ b/vyper/__main__.py
@@ -2,10 +2,10 @@
# -*- coding: UTF-8 -*-
import sys
-from vyper.cli import vyper_compile, vyper_ir, vyper_serve
+from vyper.cli import vyper_compile, vyper_ir
if __name__ == "__main__":
- allowed_subcommands = ("--vyper-compile", "--vyper-ir", "--vyper-serve")
+ allowed_subcommands = ("--vyper-compile", "--vyper-ir")
if len(sys.argv) <= 1 or sys.argv[1] not in allowed_subcommands:
# default (no args, no switch in first arg): run vyper_compile
@@ -13,9 +13,7 @@
else:
# pop switch and forward args to subcommand
subcommand = sys.argv.pop(1)
- if subcommand == "--vyper-serve":
- vyper_serve._parse_cli_args()
- elif subcommand == "--vyper-ir":
+ if subcommand == "--vyper-ir":
vyper_ir._parse_cli_args()
else:
vyper_compile._parse_cli_args()
diff --git a/vyper/abi_types.py b/vyper/abi_types.py
index b272996aed..051f8db19f 100644
--- a/vyper/abi_types.py
+++ b/vyper/abi_types.py
@@ -1,4 +1,4 @@
-from vyper.exceptions import CompilerPanic
+from vyper.exceptions import InvalidABIType
from vyper.utils import ceil32
@@ -69,7 +69,7 @@ def __repr__(self):
class ABI_GIntM(ABIType):
def __init__(self, m_bits, signed):
if not (0 < m_bits <= 256 and 0 == m_bits % 8):
- raise CompilerPanic("Invalid M provided for GIntM")
+ raise InvalidABIType("Invalid M provided for GIntM")
self.m_bits = m_bits
self.signed = signed
@@ -117,9 +117,9 @@ def selector_name(self):
class ABI_FixedMxN(ABIType):
def __init__(self, m_bits, n_places, signed):
if not (0 < m_bits <= 256 and 0 == m_bits % 8):
- raise CompilerPanic("Invalid M for FixedMxN")
+ raise InvalidABIType("Invalid M for FixedMxN")
if not (0 < n_places and n_places <= 80):
- raise CompilerPanic("Invalid N for FixedMxN")
+ raise InvalidABIType("Invalid N for FixedMxN")
self.m_bits = m_bits
self.n_places = n_places
@@ -142,7 +142,7 @@ def is_complex_type(self):
class ABI_BytesM(ABIType):
def __init__(self, m_bytes):
if not 0 < m_bytes <= 32:
- raise CompilerPanic("Invalid M for BytesM")
+ raise InvalidABIType("Invalid M for BytesM")
self.m_bytes = m_bytes
@@ -173,7 +173,7 @@ def selector_name(self):
class ABI_StaticArray(ABIType):
def __init__(self, subtyp, m_elems):
if not m_elems >= 0:
- raise CompilerPanic("Invalid M")
+ raise InvalidABIType("Invalid M")
self.subtyp = subtyp
self.m_elems = m_elems
@@ -200,7 +200,7 @@ def is_complex_type(self):
class ABI_Bytes(ABIType):
def __init__(self, bytes_bound):
if not bytes_bound >= 0:
- raise CompilerPanic("Negative bytes_bound provided to ABI_Bytes")
+ raise InvalidABIType("Negative bytes_bound provided to ABI_Bytes")
self.bytes_bound = bytes_bound
@@ -234,7 +234,7 @@ def selector_name(self):
class ABI_DynamicArray(ABIType):
def __init__(self, subtyp, elems_bound):
if not elems_bound >= 0:
- raise CompilerPanic("Negative bound provided to DynamicArray")
+ raise InvalidABIType("Negative bound provided to DynamicArray")
self.subtyp = subtyp
self.elems_bound = elems_bound
diff --git a/vyper/ast/README.md b/vyper/ast/README.md
index 320c69da0c..7400091993 100644
--- a/vyper/ast/README.md
+++ b/vyper/ast/README.md
@@ -12,8 +12,6 @@ and parsing NatSpec docstrings.
* [`annotation.py`](annotation.py): Contains the `AnnotatingVisitor` class, used to
annotate and modify the Python AST prior to converting it to a Vyper AST.
-* [`folding.py`](folding.py): Functions for evaluating and replacing literal
-nodes within the Vyper AST.
* [`natspec.py`](natspec.py): Functions for parsing NatSpec docstrings within the
source.
* [`nodes.py`](nodes.py): Contains the Vyper node classes, and the `get_node`
@@ -70,25 +68,6 @@ or parents that match a desired pattern.
To learn more about these methods, read their docstrings in the `VyperNode` class
in [`nodes.py`](nodes.py).
-### Modifying the AST
-
-[`folding.py`](folding.py) contains the `fold` function, a high-level method called
-to evaluating and replacing literal nodes within the AST. Some examples of literal
-folding include:
-
-* arithmetic operations (`3+2` becomes `5`)
-* references to literal arrays (`["foo", "bar"][1]` becomes `"bar"`)
-* builtin functions applied to literals (`min(1,2)` becomes `1`)
-
-The process of literal folding includes:
-
-1. Foldable node classes are evaluated via their `evaluate` method, which attempts
-to create a new `Constant` from the content of the given node.
-2. Replacement nodes are generated using the `from_node` class method within the new
-node class.
-3. The modification of the tree is handled by `Module.replace_in_tree`, which locates
-the existing node and replaces it with a new one.
-
## Design
### `__slots__`
diff --git a/vyper/ast/__init__.py b/vyper/ast/__init__.py
index e5b81f1e7f..bc08626b59 100644
--- a/vyper/ast/__init__.py
+++ b/vyper/ast/__init__.py
@@ -6,7 +6,8 @@
from . import nodes, validation
from .natspec import parse_natspec
from .nodes import compare_nodes
-from .utils import ast_to_dict, parse_to_ast, parse_to_ast_with_settings
+from .utils import ast_to_dict
+from .parse import parse_to_ast, parse_to_ast_with_settings
# adds vyper.ast.nodes classes into the local namespace
for name, obj in (
@@ -16,4 +17,4 @@
# required to avoid circular dependency
-from . import expansion, folding # noqa: E402
+from . import expansion # noqa: E402
diff --git a/vyper/ast/__init__.pyi b/vyper/ast/__init__.pyi
index d349e804d6..5581e82fe2 100644
--- a/vyper/ast/__init__.pyi
+++ b/vyper/ast/__init__.pyi
@@ -1,8 +1,8 @@
import ast as python_ast
from typing import Any, Optional, Union
-from . import expansion, folding, nodes, validation
+from . import expansion, nodes, validation
from .natspec import parse_natspec as parse_natspec
from .nodes import *
+from .parse import parse_to_ast as parse_to_ast
from .utils import ast_to_dict as ast_to_dict
-from .utils import parse_to_ast as parse_to_ast
diff --git a/vyper/ast/expansion.py b/vyper/ast/expansion.py
index 5471b971a4..1536f39165 100644
--- a/vyper/ast/expansion.py
+++ b/vyper/ast/expansion.py
@@ -5,22 +5,9 @@
from vyper.semantics.types.function import ContractFunctionT
-def expand_annotated_ast(vyper_module: vy_ast.Module) -> None:
- """
- Perform expansion / simplification operations on an annotated Vyper AST.
-
- This pass uses annotated type information to modify the AST, simplifying
- logic and expanding subtrees to reduce the compexity during codegen.
-
- Arguments
- ---------
- vyper_module : Module
- Top-level Vyper AST node that has been type-checked and annotated.
- """
- generate_public_variable_getters(vyper_module)
- remove_unused_statements(vyper_module)
-
-
+# TODO: remove this function. it causes correctness/performance problems
+# because of copying and mutating the AST - getter generation should be handled
+# during code generation.
def generate_public_variable_getters(vyper_module: vy_ast.Module) -> None:
"""
Create getter functions for public variables.
@@ -32,7 +19,7 @@ def generate_public_variable_getters(vyper_module: vy_ast.Module) -> None:
"""
for node in vyper_module.get_children(vy_ast.VariableDecl, {"is_public": True}):
- func_type = node._metadata["func_type"]
+ func_type = node._metadata["getter_type"]
input_types, return_type = node._metadata["type"].getter_signature
input_nodes = []
@@ -86,31 +73,11 @@ def generate_public_variable_getters(vyper_module: vy_ast.Module) -> None:
returns=return_node,
)
- with vyper_module.namespace():
- func_type = ContractFunctionT.from_FunctionDef(expanded)
-
- expanded._metadata["type"] = func_type
- return_node.set_parent(expanded)
+ # update pointers
vyper_module.add_to_body(expanded)
+ return_node.set_parent(expanded)
+ with vyper_module.namespace():
+ func_type = ContractFunctionT.from_FunctionDef(expanded)
-def remove_unused_statements(vyper_module: vy_ast.Module) -> None:
- """
- Remove statement nodes that are unused after type checking.
-
- Once type checking is complete, we can remove now-meaningless statements to
- simplify the AST prior to IR generation.
-
- Arguments
- ---------
- vyper_module : Module
- Top-level Vyper AST node.
- """
-
- # constant declarations - values were substituted within the AST during folding
- for node in vyper_module.get_children(vy_ast.VariableDecl, {"is_constant": True}):
- vyper_module.remove_from_body(node)
-
- # `implements: interface` statements - validated during type checking
- for node in vyper_module.get_children(vy_ast.ImplementsDecl):
- vyper_module.remove_from_body(node)
+ expanded._metadata["func_type"] = func_type
diff --git a/vyper/ast/folding.py b/vyper/ast/folding.py
deleted file mode 100644
index fbd1dfc2f4..0000000000
--- a/vyper/ast/folding.py
+++ /dev/null
@@ -1,296 +0,0 @@
-import warnings
-from typing import Optional, Union
-
-from vyper.ast import nodes as vy_ast
-from vyper.builtins.functions import DISPATCH_TABLE
-from vyper.exceptions import UnfoldableNode, UnknownType
-from vyper.semantics.types.base import VyperType
-from vyper.semantics.types.utils import type_from_annotation
-from vyper.utils import SizeLimits
-
-BUILTIN_CONSTANTS = {
- "EMPTY_BYTES32": (
- vy_ast.Hex,
- "0x0000000000000000000000000000000000000000000000000000000000000000",
- "empty(bytes32)",
- ), # NOQA: E501
- "ZERO_ADDRESS": (vy_ast.Hex, "0x0000000000000000000000000000000000000000", "empty(address)"),
- "MAX_INT128": (vy_ast.Int, 2**127 - 1, "max_value(int128)"),
- "MIN_INT128": (vy_ast.Int, -(2**127), "min_value(int128)"),
- "MAX_DECIMAL": (vy_ast.Decimal, SizeLimits.MAX_AST_DECIMAL, "max_value(decimal)"),
- "MIN_DECIMAL": (vy_ast.Decimal, SizeLimits.MIN_AST_DECIMAL, "min_value(decimal)"),
- "MAX_UINT256": (vy_ast.Int, 2**256 - 1, "max_value(uint256)"),
-}
-
-
-def fold(vyper_module: vy_ast.Module) -> None:
- """
- Perform literal folding operations on a Vyper AST.
-
- Arguments
- ---------
- vyper_module : Module
- Top-level Vyper AST node.
- """
- replace_builtin_constants(vyper_module)
-
- changed_nodes = 1
- while changed_nodes:
- changed_nodes = 0
- changed_nodes += replace_user_defined_constants(vyper_module)
- changed_nodes += replace_literal_ops(vyper_module)
- changed_nodes += replace_subscripts(vyper_module)
- changed_nodes += replace_builtin_functions(vyper_module)
-
-
-def replace_literal_ops(vyper_module: vy_ast.Module) -> int:
- """
- Find and evaluate operation and comparison nodes within the Vyper AST,
- replacing them with Constant nodes where possible.
-
- Arguments
- ---------
- vyper_module : Module
- Top-level Vyper AST node.
-
- Returns
- -------
- int
- Number of nodes that were replaced.
- """
- changed_nodes = 0
-
- node_types = (vy_ast.BoolOp, vy_ast.BinOp, vy_ast.UnaryOp, vy_ast.Compare)
- for node in vyper_module.get_descendants(node_types, reverse=True):
- try:
- new_node = node.evaluate()
- except UnfoldableNode:
- continue
-
- changed_nodes += 1
- vyper_module.replace_in_tree(node, new_node)
-
- return changed_nodes
-
-
-def replace_subscripts(vyper_module: vy_ast.Module) -> int:
- """
- Find and evaluate Subscript nodes within the Vyper AST, replacing them with
- Constant nodes where possible.
-
- Arguments
- ---------
- vyper_module : Module
- Top-level Vyper AST node.
-
- Returns
- -------
- int
- Number of nodes that were replaced.
- """
- changed_nodes = 0
-
- for node in vyper_module.get_descendants(vy_ast.Subscript, reverse=True):
- try:
- new_node = node.evaluate()
- except UnfoldableNode:
- continue
-
- changed_nodes += 1
- vyper_module.replace_in_tree(node, new_node)
-
- return changed_nodes
-
-
-def replace_builtin_functions(vyper_module: vy_ast.Module) -> int:
- """
- Find and evaluate builtin function calls within the Vyper AST, replacing
- them with Constant nodes where possible.
-
- Arguments
- ---------
- vyper_module : Module
- Top-level Vyper AST node.
-
- Returns
- -------
- int
- Number of nodes that were replaced.
- """
- changed_nodes = 0
-
- for node in vyper_module.get_descendants(vy_ast.Call, reverse=True):
- if not isinstance(node.func, vy_ast.Name):
- continue
-
- name = node.func.id
- func = DISPATCH_TABLE.get(name)
- if func is None or not hasattr(func, "evaluate"):
- continue
- try:
- new_node = func.evaluate(node) # type: ignore
- except UnfoldableNode:
- continue
-
- changed_nodes += 1
- vyper_module.replace_in_tree(node, new_node)
-
- return changed_nodes
-
-
-def replace_builtin_constants(vyper_module: vy_ast.Module) -> None:
- """
- Replace references to builtin constants with their literal values.
-
- Arguments
- ---------
- vyper_module : Module
- Top-level Vyper AST node.
- """
- for name, (node, value, replacement) in BUILTIN_CONSTANTS.items():
- found = replace_constant(vyper_module, name, node(value=value), True)
- if found > 0:
- warnings.warn(f"{name} is deprecated. Please use `{replacement}` instead.")
-
-
-def replace_user_defined_constants(vyper_module: vy_ast.Module) -> int:
- """
- Find user-defined constant assignments, and replace references
- to the constants with their literal values.
-
- Arguments
- ---------
- vyper_module : Module
- Top-level Vyper AST node.
-
- Returns
- -------
- int
- Number of nodes that were replaced.
- """
- changed_nodes = 0
-
- for node in vyper_module.get_children(vy_ast.VariableDecl):
- if not isinstance(node.target, vy_ast.Name):
- # left-hand-side of assignment is not a variable
- continue
- if not node.is_constant:
- # annotation is not wrapped in `constant(...)`
- continue
-
- # Extract type definition from propagated annotation
- type_ = None
- try:
- type_ = type_from_annotation(node.annotation)
- except UnknownType:
- # handle user-defined types e.g. structs - it's OK to not
- # propagate the type annotation here because user-defined
- # types can be unambiguously inferred at typechecking time
- pass
-
- changed_nodes += replace_constant(
- vyper_module, node.target.id, node.value, False, type_=type_
- )
-
- return changed_nodes
-
-
-# TODO constant folding on log events
-
-
-def _replace(old_node, new_node, type_=None):
- if isinstance(new_node, vy_ast.Constant):
- new_node = new_node.from_node(old_node, value=new_node.value)
- if type_:
- new_node._metadata["type"] = type_
- return new_node
- elif isinstance(new_node, vy_ast.List):
- base_type = type_.value_type if type_ else None
- list_values = [_replace(old_node, i, type_=base_type) for i in new_node.elements]
- new_node = new_node.from_node(old_node, elements=list_values)
- if type_:
- new_node._metadata["type"] = type_
- return new_node
- elif isinstance(new_node, vy_ast.Call):
- # Replace `Name` node with `Call` node
- keyword = keywords = None
- if hasattr(new_node, "keyword"):
- keyword = new_node.keyword
- if hasattr(new_node, "keywords"):
- keywords = new_node.keywords
- new_node = new_node.from_node(
- old_node, func=new_node.func, args=new_node.args, keyword=keyword, keywords=keywords
- )
- return new_node
- else:
- raise UnfoldableNode
-
-
-def replace_constant(
- vyper_module: vy_ast.Module,
- id_: str,
- replacement_node: Union[vy_ast.Constant, vy_ast.List, vy_ast.Call],
- raise_on_error: bool,
- type_: Optional[VyperType] = None,
-) -> int:
- """
- Replace references to a variable name with a literal value.
-
- Arguments
- ---------
- vyper_module : Module
- Module-level ast node to perform replacement in.
- id_ : str
- String representing the `.id` attribute of the node(s) to be replaced.
- replacement_node : Constant | List | Call
- Vyper ast node representing the literal value to be substituted in.
- `Call` nodes are for struct constants.
- raise_on_error: bool
- Boolean indicating if `UnfoldableNode` exception should be raised or ignored.
- type_ : VyperType, optional
- Type definition to be propagated to type checker.
-
- Returns
- -------
- int
- Number of nodes that were replaced.
- """
- changed_nodes = 0
-
- for node in vyper_module.get_descendants(vy_ast.Name, {"id": id_}, reverse=True):
- parent = node.get_ancestor()
-
- if isinstance(parent, vy_ast.Call) and node == parent.func:
- # do not replace calls because splicing a constant into a callable site is
- # never valid and it worsens the error message
- continue
-
- # do not replace dictionary keys
- if isinstance(parent, vy_ast.Dict) and node in parent.keys:
- continue
-
- if not node.get_ancestor(vy_ast.Index):
- # do not replace left-hand side of assignments
- assign = node.get_ancestor(
- (vy_ast.Assign, vy_ast.AnnAssign, vy_ast.AugAssign, vy_ast.VariableDecl)
- )
-
- if assign and node in assign.target.get_descendants(include_self=True):
- continue
-
- # do not replace enum members
- if node.get_ancestor(vy_ast.EnumDef):
- continue
-
- try:
- # note: _replace creates a copy of the replacement_node
- new_node = _replace(node, replacement_node, type_=type_)
- except UnfoldableNode:
- if raise_on_error:
- raise
- continue
-
- changed_nodes += 1
- vyper_module.replace_in_tree(node, new_node)
-
- return changed_nodes
diff --git a/vyper/ast/grammar.lark b/vyper/ast/grammar.lark
index ca9979b2a3..7889473b19 100644
--- a/vyper/ast/grammar.lark
+++ b/vyper/ast/grammar.lark
@@ -10,7 +10,8 @@ module: ( DOCSTRING
| interface_def
| constant_def
| variable_def
- | enum_def
+ | enum_def // TODO deprecate at some point in favor of flag
+ | flag_def
| event_def
| function_def
| immutable_def
@@ -76,12 +77,19 @@ indexed_event_arg: NAME ":" "indexed" "(" type ")"
event_body: _NEWLINE _INDENT (((event_member | indexed_event_arg ) _NEWLINE)+ | _PASS _NEWLINE) _DEDENT
event_def: _EVENT_DECL NAME ":" ( event_body | _PASS )
+// TODO deprecate in favor of flag
// Enums
_ENUM_DECL: "enum"
enum_member: NAME
enum_body: _NEWLINE _INDENT (enum_member _NEWLINE)+ _DEDENT
enum_def: _ENUM_DECL NAME ":" enum_body
+// Flags
+_FLAG_DECL: "flag"
+flag_member: NAME
+flag_body: _NEWLINE _INDENT (flag_member _NEWLINE)+ _DEDENT
+flag_def: _FLAG_DECL NAME ":" flag_body
+
// Types
array_def: (NAME | array_def | dyn_array_def) "[" _expr "]"
dyn_array_def: "DynArray" "[" (NAME | array_def | dyn_array_def) "," _expr "]"
@@ -89,7 +97,8 @@ tuple_def: "(" ( NAME | array_def | dyn_array_def | tuple_def ) ( "," ( NAME | a
// NOTE: Map takes a basic type and maps to another type (can be non-basic, including maps)
_MAP: "HashMap"
map_def: _MAP "[" ( NAME | array_def ) "," type "]"
-type: ( NAME | array_def | tuple_def | map_def | dyn_array_def )
+imported_type: NAME "." NAME
+type: ( NAME | imported_type | array_def | tuple_def | map_def | dyn_array_def )
// Structs can be composed of 1+ basic types or other custom_types
_STRUCT_DECL: "struct"
@@ -291,7 +300,7 @@ special_builtins: empty | abi_decode
// Adapted from: https://docs.python.org/3/reference/grammar.html
// Adapted by: Erez Shinan
NAME: /[a-zA-Z_]\w*/
-COMMENT: /#[^\n]*/
+COMMENT: /#[^\n\r]*/
_NEWLINE: ( /\r?\n[\t ]*/ | COMMENT )+
@@ -312,8 +321,10 @@ _number: DEC_NUMBER
BOOL.2: "True" | "False"
+ELLIPSIS: "..."
+
// TODO: Remove Docstring from here, and add to first part of body
-?literal: ( _number | STRING | DOCSTRING | BOOL )
+?literal: ( _number | STRING | DOCSTRING | BOOL | ELLIPSIS)
%ignore /[\t \f]+/ // WS
%ignore /\\[\t \f]*\r?\n/ // LINE_CONT
diff --git a/vyper/ast/identifiers.py b/vyper/ast/identifiers.py
new file mode 100644
index 0000000000..7d42727066
--- /dev/null
+++ b/vyper/ast/identifiers.py
@@ -0,0 +1,112 @@
+import re
+
+from vyper.exceptions import StructureException
+
+
+def validate_identifier(attr, ast_node=None):
+ if not re.match("^[_a-zA-Z][a-zA-Z0-9_]*$", attr):
+ raise StructureException(f"'{attr}' contains invalid character(s)", ast_node)
+ if attr.lower() in RESERVED_KEYWORDS:
+ raise StructureException(f"'{attr}' is a reserved keyword", ast_node)
+
+
+# https://docs.python.org/3/reference/lexical_analysis.html#keywords
+# note we don't technically need to block all python reserved keywords,
+# but do it for hygiene
+_PYTHON_RESERVED_KEYWORDS = {
+ "False",
+ "None",
+ "True",
+ "and",
+ "as",
+ "assert",
+ "async",
+ "await",
+ "break",
+ "class",
+ "continue",
+ "def",
+ "del",
+ "elif",
+ "else",
+ "except",
+ "finally",
+ "for",
+ "from",
+ "global",
+ "if",
+ "import",
+ "in",
+ "is",
+ "lambda",
+ "nonlocal",
+ "not",
+ "or",
+ "pass",
+ "raise",
+ "return",
+ "try",
+ "while",
+ "with",
+ "yield",
+}
+_PYTHON_RESERVED_KEYWORDS = {s.lower() for s in _PYTHON_RESERVED_KEYWORDS}
+
+# Cannot be used for variable or member naming
+RESERVED_KEYWORDS = _PYTHON_RESERVED_KEYWORDS | {
+ # decorators
+ "public",
+ "external",
+ "nonpayable",
+ "constant",
+ "immutable",
+ "transient",
+ "internal",
+ "payable",
+ "nonreentrant",
+ # "class" keywords
+ "interface",
+ "struct",
+ "event",
+ "enum",
+ "flag"
+ # EVM operations
+ "unreachable",
+ # special functions (no name mangling)
+ "init",
+ "_init_",
+ "___init___",
+ "____init____",
+ "default",
+ "_default_",
+ "___default___",
+ "____default____",
+ # more control flow and special operations
+ "range",
+ # more special operations
+ "indexed",
+ # denominations
+ "ether",
+ "wei",
+ "finney",
+ "szabo",
+ "shannon",
+ "lovelace",
+ "ada",
+ "babbage",
+ "gwei",
+ "kwei",
+ "mwei",
+ "twei",
+ "pwei",
+ # sentinal constant values
+ # TODO remove when these are removed from the language
+ "zero_address",
+ "empty_bytes32",
+ "max_int128",
+ "min_int128",
+ "max_decimal",
+ "min_decimal",
+ "max_uint256",
+ "zero_wei",
+}
diff --git a/vyper/ast/natspec.py b/vyper/ast/natspec.py
index c25fc423f8..41a6703b6e 100644
--- a/vyper/ast/natspec.py
+++ b/vyper/ast/natspec.py
@@ -11,13 +11,13 @@
USERDOCS_FIELDS = ("notice",)
-def parse_natspec(vyper_module_folded: vy_ast.Module) -> Tuple[dict, dict]:
+def parse_natspec(annotated_vyper_module: vy_ast.Module) -> Tuple[dict, dict]:
"""
Parses NatSpec documentation from a contract.
Arguments
---------
- vyper_module_folded : Module
+ annotated_vyper_module: Module
Module-level vyper ast node.
interface_codes: Dict, optional
Dict containing relevant data for any import statements related to
@@ -33,17 +33,17 @@ def parse_natspec(vyper_module_folded: vy_ast.Module) -> Tuple[dict, dict]:
from vyper.semantics.types.function import FunctionVisibility
userdoc, devdoc = {}, {}
- source: str = vyper_module_folded.full_source_code
+ source: str = annotated_vyper_module.full_source_code
- docstring = vyper_module_folded.get("doc_string.value")
+ docstring = annotated_vyper_module.get("doc_string.value")
if docstring:
devdoc.update(_parse_docstring(source, docstring, ("param", "return")))
if "notice" in devdoc:
userdoc["notice"] = devdoc.pop("notice")
- for node in [i for i in vyper_module_folded.body if i.get("doc_string.value")]:
+ for node in [i for i in annotated_vyper_module.body if i.get("doc_string.value")]:
docstring = node.doc_string.value
- func_type = node._metadata["type"]
+ func_type = node._metadata["func_type"]
if func_type.visibility != FunctionVisibility.EXTERNAL:
continue
diff --git a/vyper/ast/nodes.py b/vyper/ast/nodes.py
index 2497928035..efab5117d4 100644
--- a/vyper/ast/nodes.py
+++ b/vyper/ast/nodes.py
@@ -4,13 +4,13 @@
import decimal
import operator
import sys
+import warnings
from typing import Any, Optional, Union
from vyper.ast.metadata import NodeMetadata
from vyper.compiler.settings import VYPER_ERROR_CONTEXT_LINES, VYPER_ERROR_LINE_NUMBERS
from vyper.exceptions import (
ArgumentException,
- CompilerPanic,
InvalidLiteral,
InvalidOperation,
OverflowException,
@@ -18,6 +18,8 @@
SyntaxException,
TypeMismatch,
UnfoldableNode,
+ VariableDeclarationException,
+ VyperException,
ZeroDivisionException,
)
from vyper.utils import MAX_DECIMAL_PLACES, SizeLimits, annotate_source_code
@@ -78,6 +80,11 @@ def get_node(
else:
ast_struct["ast_type"] = "VariableDecl"
+ enum_warn = False
+ if ast_struct["ast_type"] == "EnumDef":
+ enum_warn = True
+ ast_struct["ast_type"] = "FlagDef"
+
vy_class = getattr(sys.modules[__name__], ast_struct["ast_type"], None)
if not vy_class:
if ast_struct["ast_type"] == "Delete":
@@ -92,7 +99,17 @@ def get_node(
ast_struct,
)
- return vy_class(parent=parent, **ast_struct)
+ node = vy_class(parent=parent, **ast_struct)
+
+ # TODO: Putting this after node creation to pretty print, remove after enum deprecation
+ if enum_warn:
+ # TODO: hack to pretty print, logic should be factored out of exception
+ pretty_printed_node = str(VyperException("", node))
+ warnings.warn(
+ f"enum will be deprecated in a future release, use flag instead. {pretty_printed_node}",
+ stacklevel=2,
+ )
+ return node
def compare_nodes(left_node: "VyperNode", right_node: "VyperNode") -> bool:
@@ -193,23 +210,6 @@ def _raise_syntax_exc(error_msg: str, ast_struct: dict) -> None:
)
-def _validate_numeric_bounds(
- node: Union["BinOp", "UnaryOp"], value: Union[decimal.Decimal, int]
-) -> None:
- if isinstance(value, decimal.Decimal):
- # this will change if/when we add more decimal types
- lower, upper = SizeLimits.MIN_AST_DECIMAL, SizeLimits.MAX_AST_DECIMAL
- elif isinstance(value, int):
- lower, upper = SizeLimits.MIN_INT256, SizeLimits.MAX_UINT256
- else:
- raise CompilerPanic(f"Unexpected return type from {node._op}: {type(value)}")
- if not lower <= value <= upper:
- raise OverflowException(
- f"Result of {node.op.description} ({value}) is outside bounds of all numeric types",
- node,
- )
-
-
class VyperNode:
"""
Base class for all vyper AST nodes.
@@ -229,7 +229,7 @@ class VyperNode:
Field names that, if present, must be set to None or a `SyntaxException`
is raised. This attribute is used to exclude syntax that is valid in Python
but not in Vyper.
- _terminus : bool, optional
+ _is_terminus : bool, optional
If `True`, indicates that execution halts upon reaching this node.
_translated_fields : Dict, optional
Field names that are reassigned if encountered. Used to normalize fields
@@ -373,22 +373,67 @@ def description(self):
"""
return getattr(self, "_description", type(self).__name__)
- def evaluate(self) -> "VyperNode":
+ @property
+ def is_literal_value(self):
+ """
+ Check if the node is a literal value.
"""
- Attempt to evaluate the content of a node and generate a new node from it.
+ return False
- If a node cannot be evaluated it should raise `UnfoldableNode`. This base
- method acts as a catch-all to raise on any inherited classes that do not
- implement the method.
+ @property
+ def has_folded_value(self):
+ """
+ Property method to check if the node has a folded value.
"""
- raise UnfoldableNode(f"{type(self)} cannot be evaluated")
+ return "folded_value" in self._metadata
+
+ def get_folded_value(self) -> "VyperNode":
+ """
+ Attempt to get the folded value, bubbling up UnfoldableNode if the node
+ is not foldable.
+
+
+ The returned value is cached on `_metadata["folded_value"]`.
+
+ For constant/literal nodes, the node should be directly returned
+ without caching to the metadata.
+ """
+ if self.is_literal_value:
+ return self
+
+ if "folded_value" not in self._metadata:
+ res = self._try_fold() # possibly throws UnfoldableNode
+ self._set_folded_value(res)
+
+ return self._metadata["folded_value"]
+
+ def _set_folded_value(self, node: "VyperNode") -> None:
+ # sanity check this is only called once
+ assert "folded_value" not in self._metadata
+
+ # set the folded node's parent so that get_ancestor works
+ # this is mainly important for error messages.
+ node._parent = self._parent
+
+ self._metadata["folded_value"] = node
+
+ def _try_fold(self) -> "VyperNode":
+ """
+ Attempt to constant-fold the content of a node, returning the result of
+ constant-folding if possible.
+
+ If a node cannot be folded, it should raise `UnfoldableNode`. This
+ base implementation acts as a catch-all to raise on any inherited
+ classes that do not implement the method.
+ """
+ raise UnfoldableNode(f"{type(self)} cannot be folded")
def validate(self) -> None:
"""
Validate the content of a node.
- Called by `ast.validation.validate_literal_nodes` to verify values within
- literal nodes.
+ Called by `ast.validation.validate_literal_nodes` to verify values
+ within literal nodes.
Returns `None` if the node is valid, raises `InvalidLiteral` or another
more expressive exception if the value cannot be valid within a Vyper
@@ -589,49 +634,8 @@ def __contains__(self, obj):
class Module(TopLevel):
- __slots__ = ()
-
- def replace_in_tree(self, old_node: VyperNode, new_node: VyperNode) -> None:
- """
- Perform an in-place substitution of a node within the tree.
-
- Parameters
- ----------
- old_node : VyperNode
- Node object to be replaced.
- new_node : VyperNode
- Node object to replace new_node.
-
- Returns
- -------
- None
- """
- parent = old_node._parent
-
- if old_node not in parent._children:
- raise CompilerPanic("Node to be replaced does not exist within parent children")
-
- is_replaced = False
- for key in parent.get_fields():
- obj = getattr(parent, key, None)
- if obj == old_node:
- if is_replaced:
- raise CompilerPanic("Node to be replaced exists as multiple members in parent")
- setattr(parent, key, new_node)
- is_replaced = True
- elif isinstance(obj, list) and obj.count(old_node):
- if is_replaced or obj.count(old_node) > 1:
- raise CompilerPanic("Node to be replaced exists as multiple members in parent")
- obj[obj.index(old_node)] = new_node
- is_replaced = True
- if not is_replaced:
- raise CompilerPanic("Node to be replaced does not exist within parent members")
-
- parent._children.remove(old_node)
-
- new_node._parent = parent
- new_node._depth = old_node._depth
- parent._children.add(new_node)
+ # metadata
+ __slots__ = ("path", "resolved_path", "source_id")
def add_to_body(self, node: VyperNode) -> None:
"""
@@ -724,7 +728,7 @@ class Log(Stmt):
__slots__ = ("value",)
-class EnumDef(TopLevel):
+class FlagDef(TopLevel):
__slots__ = ("name", "body")
@@ -751,6 +755,10 @@ class Constant(ExprNode):
# inherited class for all simple constant node types
__slots__ = ("value",)
+ @property
+ def is_literal_value(self):
+ return True
+
class Num(Constant):
# inherited class for all numeric constant node types
@@ -844,7 +852,14 @@ def n_bytes(self):
"""
The number of bytes this hex value represents
"""
- return self.n_nibbles // 2
+ return len(self.bytes_value)
+
+ @property
+ def bytes_value(self):
+ """
+ This value as bytes
+ """
+ return bytes.fromhex(self.value.removeprefix("0x"))
class Str(Constant):
@@ -887,22 +902,54 @@ class List(ExprNode):
__slots__ = ("elements",)
_translated_fields = {"elts": "elements"}
+ @property
+ def is_literal_value(self):
+ return all(e.is_literal_value for e in self.elements)
+
+ def _try_fold(self) -> ExprNode:
+ elements = [e.get_folded_value() for e in self.elements]
+ return type(self).from_node(self, elements=elements)
+
class Tuple(ExprNode):
__slots__ = ("elements",)
_translated_fields = {"elts": "elements"}
+ @property
+ def is_literal_value(self):
+ return all(e.is_literal_value for e in self.elements)
+
def validate(self):
if not self.elements:
raise InvalidLiteral("Cannot have an empty tuple", self)
+ def _try_fold(self) -> ExprNode:
+ elements = [e.get_folded_value() for e in self.elements]
+ return type(self).from_node(self, elements=elements)
+
+
+class NameConstant(Constant):
+ __slots__ = ()
+
+ def validate(self):
+ if self.value is None:
+ raise InvalidLiteral("`None` is not a valid vyper value!", self)
+
+
+class Ellipsis(Constant):
+ __slots__ = ()
+
class Dict(ExprNode):
__slots__ = ("keys", "values")
+ @property
+ def is_literal_value(self):
+ return all(v.is_literal_value for v in self.values)
-class NameConstant(Constant):
- __slots__ = ("value",)
+ def _try_fold(self) -> ExprNode:
+ values = [v.get_folded_value() for v in self.values]
+ return type(self).from_node(self, values=values)
class Name(ExprNode):
@@ -912,7 +959,7 @@ class Name(ExprNode):
class UnaryOp(ExprNode):
__slots__ = ("op", "operand")
- def evaluate(self) -> ExprNode:
+ def _try_fold(self) -> ExprNode:
"""
Attempt to evaluate the unary operation.
@@ -921,16 +968,17 @@ def evaluate(self) -> ExprNode:
Int | Decimal
Node representing the result of the evaluation.
"""
- if isinstance(self.op, Not) and not isinstance(self.operand, NameConstant):
- raise UnfoldableNode("Node contains invalid field(s) for evaluation")
- if isinstance(self.op, USub) and not isinstance(self.operand, (Int, Decimal)):
- raise UnfoldableNode("Node contains invalid field(s) for evaluation")
- if isinstance(self.op, Invert) and not isinstance(self.operand, Int):
- raise UnfoldableNode("Node contains invalid field(s) for evaluation")
+ operand = self.operand.get_folded_value()
- value = self.op._op(self.operand.value)
- _validate_numeric_bounds(self, value)
- return type(self.operand).from_node(self, value=value)
+ if isinstance(self.op, Not) and not isinstance(operand, NameConstant):
+ raise UnfoldableNode("not a boolean!", self.operand)
+ if isinstance(self.op, USub) and not isinstance(operand, Num):
+ raise UnfoldableNode("not a number!", self.operand)
+ if isinstance(self.op, Invert) and not isinstance(operand, Int):
+ raise UnfoldableNode("not an int!", self.operand)
+
+ value = self.op._op(operand.value)
+ return type(operand).from_node(self, value=value)
class Operator(VyperNode):
@@ -960,7 +1008,7 @@ def _op(self, value):
class BinOp(ExprNode):
__slots__ = ("left", "op", "right")
- def evaluate(self) -> ExprNode:
+ def _try_fold(self) -> ExprNode:
"""
Attempt to evaluate the arithmetic operation.
@@ -969,20 +1017,19 @@ def evaluate(self) -> ExprNode:
Int | Decimal
Node representing the result of the evaluation.
"""
- left, right = self.left, self.right
+ left, right = [i.get_folded_value() for i in (self.left, self.right)]
if type(left) is not type(right):
- raise UnfoldableNode("Node contains invalid field(s) for evaluation")
- if not isinstance(left, (Int, Decimal)):
- raise UnfoldableNode("Node contains invalid field(s) for evaluation")
+ raise UnfoldableNode("invalid operation", self)
+ if not isinstance(left, Num):
+ raise UnfoldableNode("not a number!", self.left)
# this validation is performed to prevent the compiler from hanging
# on very large shifts and improve the error message for negative
# values.
if isinstance(self.op, (LShift, RShift)) and not (0 <= right.value <= 256):
- raise InvalidLiteral("Shift bits must be between 0 and 256", right)
+ raise InvalidLiteral("Shift bits must be between 0 and 256", self.right)
value = self.op._op(left.value, right.value)
- _validate_numeric_bounds(self, value)
return type(left).from_node(self, value=value)
@@ -1110,7 +1157,7 @@ class RShift(Operator):
class BoolOp(ExprNode):
__slots__ = ("op", "values")
- def evaluate(self) -> ExprNode:
+ def _try_fold(self) -> ExprNode:
"""
Attempt to evaluate the boolean operation.
@@ -1119,13 +1166,12 @@ def evaluate(self) -> ExprNode:
NameConstant
Node representing the result of the evaluation.
"""
- if next((i for i in self.values if not isinstance(i, NameConstant)), None):
- raise UnfoldableNode("Node contains invalid field(s) for evaluation")
+ values = [v.get_folded_value() for v in self.values]
- values = [i.value for i in self.values]
- if None in values:
+ if any(not isinstance(v, NameConstant) for v in values):
raise UnfoldableNode("Node contains invalid field(s) for evaluation")
+ values = [v.value for v in values]
value = self.op._op(values)
return NameConstant.from_node(self, value=value)
@@ -1166,7 +1212,7 @@ def __init__(self, *args, **kwargs):
kwargs["right"] = kwargs.pop("comparators")[0]
super().__init__(*args, **kwargs)
- def evaluate(self) -> ExprNode:
+ def _try_fold(self) -> ExprNode:
"""
Attempt to evaluate the comparison.
@@ -1175,7 +1221,7 @@ def evaluate(self) -> ExprNode:
NameConstant
Node representing the result of the evaluation.
"""
- left, right = self.left, self.right
+ left, right = [i.get_folded_value() for i in (self.left, self.right)]
if not isinstance(left, Constant):
raise UnfoldableNode("Node contains invalid field(s) for evaluation")
@@ -1254,7 +1300,22 @@ def _op(self, left, right):
class Call(ExprNode):
- __slots__ = ("func", "args", "keywords", "keyword")
+ __slots__ = ("func", "args", "keywords")
+
+ # try checking if this is a builtin, which is foldable
+ def _try_fold(self):
+ if not isinstance(self.func, Name):
+ raise UnfoldableNode("not a builtin", self)
+
+ # cursed import cycle!
+ from vyper.builtins.functions import DISPATCH_TABLE
+
+ func_name = self.func.id
+ if func_name not in DISPATCH_TABLE:
+ raise UnfoldableNode("not a builtin", self)
+
+ builtin_t = DISPATCH_TABLE[func_name]
+ return builtin_t._try_fold(self)
class keyword(VyperNode):
@@ -1268,7 +1329,7 @@ class Attribute(ExprNode):
class Subscript(ExprNode):
__slots__ = ("slice", "value")
- def evaluate(self) -> ExprNode:
+ def _try_fold(self) -> ExprNode:
"""
Attempt to evaluate the subscript.
@@ -1280,14 +1341,22 @@ def evaluate(self) -> ExprNode:
ExprNode
Node representing the result of the evaluation.
"""
- if not isinstance(self.value, List):
+ slice_ = self.slice.value.get_folded_value()
+ value = self.value.get_folded_value()
+
+ if not isinstance(value, List):
raise UnfoldableNode("Subscript object is not a literal list")
- elements = self.value.elements
+
+ elements = value.elements
if len(set([type(i) for i in elements])) > 1:
raise UnfoldableNode("List contains multiple node types")
- idx = self.slice.get("value.value")
- if not isinstance(idx, int) or idx < 0 or idx >= len(elements):
- raise UnfoldableNode("Invalid index value")
+
+ if not isinstance(slice_, Int):
+ raise UnfoldableNode("invalid index type", slice_)
+
+ idx = slice_.value
+ if idx < 0 or idx >= len(elements):
+ raise UnfoldableNode("invalid index value")
return elements[idx]
@@ -1388,6 +1457,24 @@ def _check_args(annotation, call_name):
if isinstance(self.annotation, Call):
_raise_syntax_exc("Invalid scope for variable declaration", self.annotation)
+ def _pretty_location(self) -> str:
+ if self.is_constant:
+ return "Constant"
+ if self.is_transient:
+ return "Transient"
+ if self.is_immutable:
+ return "Immutable"
+ return "Storage"
+
+ def validate(self):
+ if self.is_constant and self.value is None:
+ raise VariableDeclarationException("Constant must be declared with a value", self)
+
+ if not self.is_constant and self.value is not None:
+ raise VariableDeclarationException(
+ f"{self._pretty_location} variables cannot have an initial value", self.value
+ )
+
class AugAssign(Stmt):
__slots__ = ("op", "target", "value")
@@ -1407,7 +1494,7 @@ class Pass(Stmt):
__slots__ = ()
-class _Import(Stmt):
+class _ImportStmt(Stmt):
__slots__ = ("name", "alias")
def __init__(self, *args, **kwargs):
@@ -1419,11 +1506,11 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
-class Import(_Import):
+class Import(_ImportStmt):
__slots__ = ()
-class ImportFrom(_Import):
+class ImportFrom(_ImportStmt):
__slots__ = ("level", "module")
diff --git a/vyper/ast/nodes.pyi b/vyper/ast/nodes.pyi
index 0d59a2fa63..8bc4a4eb57 100644
--- a/vyper/ast/nodes.pyi
+++ b/vyper/ast/nodes.pyi
@@ -2,9 +2,9 @@ import ast as python_ast
from typing import Any, Optional, Sequence, Type, Union
from .natspec import parse_natspec as parse_natspec
+from .parse import parse_to_ast as parse_to_ast
+from .parse import parse_to_ast_with_settings as parse_to_ast_with_settings
from .utils import ast_to_dict as ast_to_dict
-from .utils import parse_to_ast as parse_to_ast
-from .utils import parse_to_ast_with_settings as parse_to_ast_with_settings
NODE_BASE_ATTRIBUTES: Any
NODE_SRC_ATTRIBUTES: Any
@@ -24,9 +24,15 @@ class VyperNode:
def __eq__(self, other: Any) -> Any: ...
@property
def description(self): ...
+ @property
+ def is_literal_value(self): ...
+ @property
+ def has_folded_value(self): ...
@classmethod
def get_fields(cls: Any) -> set: ...
- def evaluate(self) -> VyperNode: ...
+ def get_folded_value(self) -> VyperNode: ...
+ def _try_fold(self) -> VyperNode: ...
+ def _set_folded_value(self, node: VyperNode) -> None: ...
@classmethod
def from_node(cls, node: VyperNode, **kwargs: Any) -> Any: ...
def to_dict(self) -> dict: ...
@@ -35,14 +41,14 @@ class VyperNode:
node_type: Union[Type[VyperNode], Sequence[Type[VyperNode]], None] = ...,
filters: Optional[dict] = ...,
reverse: bool = ...,
- ) -> Sequence: ...
+ ) -> list: ...
def get_descendants(
self,
node_type: Union[Type[VyperNode], Sequence[Type[VyperNode]], None] = ...,
filters: Optional[dict] = ...,
include_self: bool = ...,
reverse: bool = ...,
- ) -> Sequence: ...
+ ) -> list: ...
def get_ancestor(
self, node_type: Union[Type[VyperNode], Sequence[Type[VyperNode]], None] = ...
) -> VyperNode: ...
@@ -59,7 +65,8 @@ class TopLevel(VyperNode):
def __contains__(self, obj: Any) -> bool: ...
class Module(TopLevel):
- def replace_in_tree(self, old_node: VyperNode, new_node: VyperNode) -> None: ...
+ path: str = ...
+ resolved_path: str = ...
def add_to_body(self, node: VyperNode) -> None: ...
def remove_from_body(self, node: VyperNode) -> None: ...
def namespace(self) -> Any: ... # context manager
@@ -79,7 +86,7 @@ class Return(VyperNode): ...
class Log(VyperNode):
value: VyperNode = ...
-class EnumDef(VyperNode):
+class FlagDef(VyperNode):
body: list = ...
name: str = ...
@@ -121,6 +128,9 @@ class Bytes(Constant):
@property
def s(self): ...
+class NameConstant(Constant): ...
+class Ellipsis(Constant): ...
+
class List(VyperNode):
elements: list = ...
@@ -131,8 +141,6 @@ class Dict(VyperNode):
keys: list = ...
values: list = ...
-class NameConstant(Constant): ...
-
class Name(VyperNode):
id: str = ...
_type: str = ...
@@ -142,6 +150,7 @@ class Expr(VyperNode):
class UnaryOp(ExprNode):
op: VyperNode = ...
+ operand: VyperNode = ...
class USub(VyperNode): ...
class Not(VyperNode): ...
@@ -165,12 +174,15 @@ class BitXor(VyperNode): ...
class BoolOp(ExprNode):
op: VyperNode = ...
+ values: list[VyperNode] = ...
class And(VyperNode): ...
class Or(VyperNode): ...
class Compare(ExprNode):
op: VyperNode = ...
+ left: VyperNode = ...
+ right: VyperNode = ...
class Eq(VyperNode): ...
class NotEq(VyperNode): ...
@@ -179,11 +191,12 @@ class LtE(VyperNode): ...
class Gt(VyperNode): ...
class GtE(VyperNode): ...
class In(VyperNode): ...
+class NotIn(VyperNode): ...
class Call(ExprNode):
args: list = ...
keywords: list = ...
- func: Name = ...
+ func: VyperNode = ...
class keyword(VyperNode): ...
diff --git a/vyper/ast/annotation.py b/vyper/ast/parse.py
similarity index 68%
rename from vyper/ast/annotation.py
rename to vyper/ast/parse.py
index fa1742859d..b4dd9531d0 100644
--- a/vyper/ast/annotation.py
+++ b/vyper/ast/parse.py
@@ -1,14 +1,116 @@
import ast as python_ast
import tokenize
from decimal import Decimal
-from typing import Any, Optional, cast
+from typing import Any, Dict, List, Optional, Union, cast
import asttokens
-from vyper.exceptions import CompilerPanic, SyntaxException
+from vyper.ast import nodes as vy_ast
+from vyper.ast.pre_parser import pre_parse
+from vyper.compiler.settings import Settings
+from vyper.exceptions import CompilerPanic, ParserException, SyntaxException
from vyper.typing import ModificationOffsets
+def parse_to_ast(*args: Any, **kwargs: Any) -> tuple[vy_ast.Module, dict[int, dict[str, Any]]]:
+ _settings, ast, loop_var_annotations = parse_to_ast_with_settings(*args, **kwargs)
+ return ast, loop_var_annotations
+
+
+def parse_to_ast_with_settings(
+ source_code: str,
+ source_id: int = 0,
+ module_path: Optional[str] = None,
+ resolved_path: Optional[str] = None,
+ add_fn_node: Optional[str] = None,
+) -> tuple[Settings, vy_ast.Module, dict[int, dict[str, Any]]]:
+ """
+ Parses a Vyper source string and generates basic Vyper AST nodes.
+
+ Parameters
+ ----------
+ source_code : str
+ The Vyper source code to parse.
+ source_id : int, optional
+ Source id to use in the `src` member of each node.
+ contract_name: str, optional
+ Name of contract.
+ add_fn_node: str, optional
+ If not None, adds a dummy Python AST FunctionDef wrapper node.
+ source_id: int, optional
+ The source ID generated for this source code.
+ Corresponds to FileInput.source_id
+ module_path: str, optional
+ The path of the source code
+ Corresponds to FileInput.path
+ resolved_path: str, optional
+ The resolved path of the source code
+ Corresponds to FileInput.resolved_path
+
+ Returns
+ -------
+ list
+ Untyped, unoptimized Vyper AST nodes.
+ """
+ if "\x00" in source_code:
+ raise ParserException("No null bytes (\\x00) allowed in the source code.")
+ settings, class_types, loop_var_annotations, reformatted_code = pre_parse(source_code)
+ try:
+ py_ast = python_ast.parse(reformatted_code)
+ except SyntaxError as e:
+ # TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors
+ raise SyntaxException(str(e), source_code, e.lineno, e.offset) from e
+
+ # Add dummy function node to ensure local variables are treated as `AnnAssign`
+ # instead of state variables (`VariableDecl`)
+ if add_fn_node:
+ fn_node = python_ast.FunctionDef(add_fn_node, py_ast.body, [], [])
+ fn_node.body = py_ast.body
+ fn_node.args = python_ast.arguments(defaults=[])
+ py_ast.body = [fn_node]
+
+ annotate_python_ast(
+ py_ast,
+ source_code,
+ class_types,
+ loop_var_annotations,
+ source_id,
+ module_path=module_path,
+ resolved_path=resolved_path,
+ )
+
+ # Convert to Vyper AST.
+ module = vy_ast.get_node(py_ast)
+ assert isinstance(module, vy_ast.Module) # mypy hint
+
+ return settings, module, loop_var_annotations
+
+
+def ast_to_dict(ast_struct: Union[vy_ast.VyperNode, List]) -> Union[Dict, List]:
+ """
+ Converts a Vyper AST node, or list of nodes, into a dictionary suitable for
+ output to the user.
+ """
+ if isinstance(ast_struct, vy_ast.VyperNode):
+ return ast_struct.to_dict()
+
+ if isinstance(ast_struct, list):
+ return [i.to_dict() for i in ast_struct]
+
+ raise CompilerPanic(f'Unknown Vyper AST node provided: "{type(ast_struct)}".')
+
+
+def dict_to_ast(ast_struct: Union[Dict, List]) -> Union[vy_ast.VyperNode, List]:
+ """
+ Converts an AST dict, or list of dicts, into Vyper AST node objects.
+ """
+ if isinstance(ast_struct, dict):
+ return vy_ast.get_node(ast_struct)
+ if isinstance(ast_struct, list):
+ return [vy_ast.get_node(i) for i in ast_struct]
+ raise CompilerPanic(f'Unknown ast_struct provided: "{type(ast_struct)}".')
+
+
class AnnotatingVisitor(python_ast.NodeTransformer):
_source_code: str
_modification_offsets: ModificationOffsets
@@ -19,11 +121,13 @@ def __init__(
modification_offsets: Optional[ModificationOffsets],
tokens: asttokens.ASTTokens,
source_id: int,
- contract_name: Optional[str],
+ module_path: Optional[str] = None,
+ resolved_path: Optional[str] = None,
):
self._tokens = tokens
self._source_id = source_id
- self._contract_name = contract_name
+ self._module_path = module_path
+ self._resolved_path = resolved_path
self._source_code: str = source_code
self.counter: int = 0
self._modification_offsets = {}
@@ -83,7 +187,9 @@ def _visit_docstring(self, node):
return node
def visit_Module(self, node):
- node.name = self._contract_name
+ node.path = self._module_path
+ node.resolved_path = self._resolved_path
+ node.source_id = self._source_id
return self._visit_docstring(node)
def visit_FunctionDef(self, node):
@@ -163,6 +269,8 @@ def visit_Constant(self, node):
node.ast_type = "Str"
elif isinstance(node.value, bytes):
node.ast_type = "Bytes"
+ elif isinstance(node.value, Ellipsis.__class__):
+ node.ast_type = "Ellipsis"
else:
raise SyntaxException(
"Invalid syntax (unsupported Python Constant AST node).",
@@ -251,7 +359,8 @@ def annotate_python_ast(
modification_offsets: Optional[ModificationOffsets] = None,
loop_var_annotations: Optional[dict[int, python_ast.AST]] = None,
source_id: int = 0,
- contract_name: Optional[str] = None,
+ module_path: Optional[str] = None,
+ resolved_path: Optional[str] = None,
) -> python_ast.AST:
"""
Annotate and optimize a Python AST in preparation conversion to a Vyper AST.
@@ -271,7 +380,14 @@ def annotate_python_ast(
"""
tokens = asttokens.ASTTokens(source_code, tree=cast(Optional[python_ast.Module], parsed_ast))
- visitor = AnnotatingVisitor(source_code, modification_offsets, tokens, source_id, contract_name)
+ visitor = AnnotatingVisitor(
+ source_code,
+ modification_offsets,
+ tokens,
+ source_id,
+ module_path=module_path,
+ resolved_path=resolved_path,
+ )
visitor.visit(parsed_ast)
for k, v in loop_var_annotations.items():
tokens = asttokens.ASTTokens(v["source_code"], tree=cast(Optional[python_ast.Module], v["parsed_ast"]))
diff --git a/vyper/ast/pre_parser.py b/vyper/ast/pre_parser.py
index b4bdf15cd8..da01dce080 100644
--- a/vyper/ast/pre_parser.py
+++ b/vyper/ast/pre_parser.py
@@ -44,7 +44,8 @@ def validate_version_pragma(version_str: str, start: ParserPosition) -> None:
# compound statements that are replaced with `class`
-VYPER_CLASS_TYPES = {"enum", "event", "interface", "struct"}
+# TODO remove enum in favor of flag
+VYPER_CLASS_TYPES = {"flag", "enum", "event", "interface", "struct"}
# simple statements or expressions that are replaced with `yield`
VYPER_EXPRESSION_TYPES = {"log"}
@@ -55,7 +56,7 @@ def pre_parse(code: str) -> tuple[Settings, ModificationOffsets, str]:
Re-formats a vyper source string into a python source string and performs
some validation. More specifically,
- * Translates "interface", "struct", "enum, and "event" keywords into python "class" keyword
+ * Translates "interface", "struct", "flag", and "event" keywords into python "class" keyword
* Validates "@version" pragma against current compiler version
* Prevents direct use of python "class" keyword
* Prevents use of python semi-colon statement separator
@@ -116,7 +117,7 @@ def pre_parse(code: str) -> tuple[Settings, ModificationOffsets, str]:
validate_version_pragma(compiler_version, start)
settings.compiler_version = compiler_version
- if pragma.startswith("optimize "):
+ elif pragma.startswith("optimize "):
if settings.optimize is not None:
raise StructureException("pragma optimize specified twice!", start)
try:
@@ -124,7 +125,7 @@ def pre_parse(code: str) -> tuple[Settings, ModificationOffsets, str]:
settings.optimize = OptimizationLevel.from_string(mode)
except ValueError:
raise StructureException(f"Invalid optimization mode `{mode}`", start)
- if pragma.startswith("evm-version "):
+ elif pragma.startswith("evm-version "):
if settings.evm_version is not None:
raise StructureException("pragma evm-version specified twice!", start)
evm_version = pragma.removeprefix("evm-version").strip()
@@ -132,6 +133,9 @@ def pre_parse(code: str) -> tuple[Settings, ModificationOffsets, str]:
raise StructureException("Invalid evm version: `{evm_version}`", start)
settings.evm_version = evm_version
+ else:
+ raise StructureException(f"Unknown pragma `{pragma.split()[0]}`")
+
if typ == NAME and string in ("class", "yield"):
raise SyntaxException(
f"The `{string}` keyword is not allowed. ", code, start[0], start[1]
diff --git a/vyper/ast/utils.py b/vyper/ast/utils.py
index 5d83c7996a..4c2e5394c9 100644
--- a/vyper/ast/utils.py
+++ b/vyper/ast/utils.py
@@ -1,75 +1,7 @@
-import ast as python_ast
-from typing import Any, Dict, List, Optional, Union
+from typing import Dict, List, Union
from vyper.ast import nodes as vy_ast
-from vyper.ast.annotation import annotate_python_ast
-from vyper.ast.pre_parser import pre_parse
-from vyper.compiler.settings import Settings
-from vyper.exceptions import CompilerPanic, ParserException, SyntaxException
-
-
-def parse_to_ast(*args: Any, **kwargs: Any) -> vy_ast.Module:
- return parse_to_ast_with_settings(*args, **kwargs)[1]
-
-
-def parse_to_ast_with_settings(
- source_code: str,
- source_id: int = 0,
- contract_name: Optional[str] = None,
- add_fn_node: Optional[str] = None,
-) -> tuple[Settings, vy_ast.Module, dict[int, dict[str, Any]]]:
- """
- Parses a Vyper source string and generates basic Vyper AST nodes.
-
- Parameters
- ----------
- source_code : str
- The Vyper source code to parse.
- source_id : int, optional
- Source id to use in the `src` member of each node.
- contract_name: str, optional
- Name of contract.
- add_fn_node: str, optional
- If not None, adds a dummy Python AST FunctionDef wrapper node.
-
- Returns
- -------
- list
- Untyped, unoptimized Vyper AST nodes.
- """
- if "\x00" in source_code:
- raise ParserException("No null bytes (\\x00) allowed in the source code.")
- settings, class_types, loop_var_annotations, reformatted_code = pre_parse(source_code)
- try:
- py_ast = python_ast.parse(reformatted_code)
-
- print("loop vars: ", loop_var_annotations)
- for k, v in loop_var_annotations.items():
- print("v: ", v)
- parsed_v = python_ast.parse(v["source_code"])
- print("parsed v: ", parsed_v.body[0].value)
- loop_var_annotations[k]["parsed_ast"] = parsed_v
- except SyntaxError as e:
- # TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors
- raise SyntaxException(str(e), source_code, e.lineno, e.offset) from e
-
- # Add dummy function node to ensure local variables are treated as `AnnAssign`
- # instead of state variables (`VariableDecl`)
- if add_fn_node:
- fn_node = python_ast.FunctionDef(add_fn_node, py_ast.body, [], [])
- fn_node.body = py_ast.body
- fn_node.args = python_ast.arguments(defaults=[])
- py_ast.body = [fn_node]
- annotate_python_ast(py_ast, source_code, class_types, loop_var_annotations, source_id, contract_name)
-
- # Convert to Vyper AST.
- module = vy_ast.get_node(py_ast)
-
- for k, v in loop_var_annotations.items():
- loop_var_annotations[k]["vy_ast"] = vy_ast.get_node(v["parsed_ast"])
-
- assert isinstance(module, vy_ast.Module) # mypy hint
- return settings, module, loop_var_annotations
+from vyper.exceptions import CompilerPanic
def ast_to_dict(ast_struct: Union[vy_ast.VyperNode, List]) -> Union[Dict, List]:
diff --git a/vyper/ast/validation.py b/vyper/ast/validation.py
index 36a6a0484c..387f7734b9 100644
--- a/vyper/ast/validation.py
+++ b/vyper/ast/validation.py
@@ -1,11 +1,11 @@
# validation utils for ast
-# TODO this really belongs in vyper/semantics/validation/utils
from typing import Optional, Union
from vyper.ast import nodes as vy_ast
from vyper.exceptions import ArgumentException, CompilerPanic, StructureException
+# TODO this really belongs in vyper/semantics/validation/utils
def validate_call_args(
node: vy_ast.Call, arg_count: Union[int, tuple], kwargs: Optional[list] = None
) -> None:
@@ -101,14 +101,13 @@ def validate_literal_nodes(vyper_module: vy_ast.Module) -> None:
"""
Individually validate Vyper AST nodes.
- Calls the `validate` method of each node to verify that literal nodes
- do not contain invalid values.
+ Recursively calls the `validate` method of each node to verify that
+ literal nodes do not contain invalid values.
Arguments
---------
vyper_module : vy_ast.Module
Top level Vyper AST node.
"""
- for node in vyper_module.get_descendants():
- if hasattr(node, "validate"):
- node.validate()
+ for node in vyper_module.get_descendants(include_self=True):
+ node.validate()
diff --git a/vyper/builtins/_convert.py b/vyper/builtins/_convert.py
index e09f5f3174..998cbbc9f6 100644
--- a/vyper/builtins/_convert.py
+++ b/vyper/builtins/_convert.py
@@ -14,7 +14,7 @@
int_clamp,
is_bytes_m_type,
is_decimal_type,
- is_enum_type,
+ is_flag_type,
is_integer_type,
sar,
shl,
@@ -35,7 +35,7 @@
BytesM_T,
BytesT,
DecimalT,
- EnumT,
+ FlagT,
IntegerT,
StringT,
)
@@ -277,7 +277,7 @@ def to_bool(expr, arg, out_typ):
return IRnode.from_list(["iszero", ["iszero", arg]], typ=out_typ)
-@_input_types(IntegerT, DecimalT, BytesM_T, AddressT, BoolT, EnumT, BytesT)
+@_input_types(IntegerT, DecimalT, BytesM_T, AddressT, BoolT, FlagT, BytesT)
def to_int(expr, arg, out_typ):
return _to_int(expr, arg, out_typ)
@@ -305,7 +305,7 @@ def _to_int(expr, arg, out_typ):
elif is_decimal_type(arg.typ):
arg = _fixed_to_int(arg, out_typ)
- elif is_enum_type(arg.typ):
+ elif is_flag_type(arg.typ):
if out_typ != UINT256_T:
_FAIL(arg.typ, out_typ, expr)
# pretend enum is uint256
@@ -468,7 +468,7 @@ def convert(expr, context):
ret = to_bool(arg_ast, arg, out_typ)
elif out_typ == AddressT():
ret = to_address(arg_ast, arg, out_typ)
- elif is_enum_type(out_typ):
+ elif is_flag_type(out_typ):
ret = to_enum(arg_ast, arg, out_typ)
elif is_integer_type(out_typ):
ret = to_int(arg_ast, arg, out_typ)
diff --git a/vyper/builtins/_signatures.py b/vyper/builtins/_signatures.py
index d39a4a085f..aac008ad1e 100644
--- a/vyper/builtins/_signatures.py
+++ b/vyper/builtins/_signatures.py
@@ -1,12 +1,17 @@
import functools
-from typing import Dict
+from typing import Any, Optional
-from vyper.ast import nodes as vy_ast
+from vyper import ast as vy_ast
from vyper.ast.validation import validate_call_args
from vyper.codegen.expr import Expr
from vyper.codegen.ir_node import IRnode
-from vyper.exceptions import CompilerPanic, TypeMismatch
-from vyper.semantics.analysis.utils import get_exact_type_from_node, validate_expected_type
+from vyper.exceptions import CompilerPanic, TypeMismatch, UnfoldableNode
+from vyper.semantics.analysis.base import Modifiability
+from vyper.semantics.analysis.utils import (
+ check_modifiability,
+ get_exact_type_from_node,
+ validate_expected_type,
+)
from vyper.semantics.types import TYPE_T, KwargSettings, VyperType
from vyper.semantics.types.utils import type_from_annotation
@@ -29,7 +34,7 @@ def process_arg(arg, expected_arg_type, context):
def process_kwarg(kwarg_node, kwarg_settings, expected_kwarg_type, context):
if kwarg_settings.require_literal:
- return kwarg_node.value
+ return kwarg_node.get_folded_value().value
return process_arg(kwarg_node, expected_kwarg_type, context)
@@ -74,12 +79,15 @@ def decorator_fn(self, node, context):
return decorator_fn
-class BuiltinFunction:
+class BuiltinFunctionT(VyperType):
_has_varargs = False
- _kwargs: Dict[str, KwargSettings] = {}
+ _inputs: list[tuple[str, Any]] = []
+ _kwargs: dict[str, KwargSettings] = {}
+ _modifiability: Modifiability = Modifiability.MODIFIABLE
+ _return_type: Optional[VyperType] = None
# helper function to deal with TYPE_DEFINITIONs
- def _validate_single(self, arg, expected_type):
+ def _validate_single(self, arg: vy_ast.VyperNode, expected_type: VyperType) -> None:
# TODO using "TYPE_DEFINITION" is a kludge in derived classes,
# refactor me.
if expected_type == "TYPE_DEFINITION":
@@ -89,23 +97,25 @@ def _validate_single(self, arg, expected_type):
else:
validate_expected_type(arg, expected_type)
- def _validate_arg_types(self, node):
+ def _validate_arg_types(self, node: vy_ast.Call) -> None:
num_args = len(self._inputs) # the number of args the signature indicates
- expect_num_args = num_args
+ expect_num_args: Any = num_args
if self._has_varargs:
# note special meaning for -1 in validate_call_args API
expect_num_args = (num_args, -1)
- validate_call_args(node, expect_num_args, self._kwargs)
+ validate_call_args(node, expect_num_args, list(self._kwargs.keys()))
for arg, (_, expected) in zip(node.args, self._inputs):
self._validate_single(arg, expected)
for kwarg in node.keywords:
kwarg_settings = self._kwargs[kwarg.arg]
- if kwarg_settings.require_literal and not isinstance(kwarg.value, vy_ast.Constant):
- raise TypeMismatch("Value for kwarg must be a literal", kwarg.value)
+ if kwarg_settings.require_literal and not check_modifiability(
+ kwarg.value, Modifiability.CONSTANT
+ ):
+ raise TypeMismatch("Value must be literal", kwarg.value)
self._validate_single(kwarg.value, kwarg_settings.typ)
# typecheck varargs. we don't have type info from the signature,
@@ -118,13 +128,12 @@ def _validate_arg_types(self, node):
# ensures the type can be inferred exactly.
get_exact_type_from_node(arg)
- def fetch_call_return(self, node):
+ def fetch_call_return(self, node: vy_ast.Call) -> Optional[VyperType]:
self._validate_arg_types(node)
- if self._return_type:
- return self._return_type
+ return self._return_type
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node: vy_ast.Call, expected_return_typ=None) -> list[VyperType]:
self._validate_arg_types(node)
ret = [expected for (_, expected) in self._inputs]
@@ -136,8 +145,11 @@ def infer_arg_types(self, node):
ret.extend(get_exact_type_from_node(arg) for arg in varargs)
return ret
- def infer_kwarg_types(self, node):
+ def infer_kwarg_types(self, node: vy_ast.Call) -> dict[str, VyperType]:
return {i.arg: self._kwargs[i.arg].typ for i in node.keywords}
def __repr__(self):
return f"(builtin) {self._id}"
+
+ def _try_fold(self, node):
+ raise UnfoldableNode(f"not foldable: {self}", node)
diff --git a/vyper/builtins/_utils.py b/vyper/builtins/_utils.py
index afc0987b6d..72b05f15e3 100644
--- a/vyper/builtins/_utils.py
+++ b/vyper/builtins/_utils.py
@@ -1,10 +1,10 @@
from vyper.ast import parse_to_ast
from vyper.codegen.context import Context
-from vyper.codegen.global_context import GlobalContext
from vyper.codegen.stmt import parse_body
from vyper.semantics.analysis.local import FunctionNodeVisitor
from vyper.semantics.namespace import Namespace, override_global_namespace
from vyper.semantics.types.function import ContractFunctionT, FunctionVisibility, StateMutability
+from vyper.semantics.types.module import ModuleT
def _strip_source_pos(ir_node):
@@ -22,15 +22,16 @@ def generate_inline_function(code, variables, variables_2, memory_allocator):
# Initialise a placeholder `FunctionDef` AST node and corresponding
# `ContractFunctionT` type to rely on the annotation visitors in semantics
# module.
- ast_code.body[0]._metadata["type"] = ContractFunctionT(
+ ast_code.body[0]._metadata["func_type"] = ContractFunctionT(
"sqrt_builtin", [], [], None, FunctionVisibility.INTERNAL, StateMutability.NONPAYABLE
)
# The FunctionNodeVisitor's constructor performs semantic checks
# annotate the AST as side effects.
- FunctionNodeVisitor(ast_code, ast_code.body[0], namespace)
+ analyzer = FunctionNodeVisitor(ast_code, ast_code.body[0], namespace)
+ analyzer.analyze()
new_context = Context(
- vars_=variables, global_ctx=GlobalContext(), memory_allocator=memory_allocator
+ vars_=variables, module_ctx=ModuleT(ast_code), memory_allocator=memory_allocator
)
generated_ir = parse_body(ast_code.body[0].body, new_context)
# strip source position info from the generated_ir since
diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py
index 3ec8f69934..c896fc7ef6 100644
--- a/vyper/builtins/functions.py
+++ b/vyper/builtins/functions.py
@@ -1,7 +1,6 @@
import hashlib
import math
import operator
-from decimal import Decimal
from vyper import ast as vy_ast
from vyper.abi_types import ABI_Tuple
@@ -21,6 +20,7 @@
clamp_basetype,
clamp_nonzero,
copy_bytes,
+ dummy_node_for_type,
ensure_in_memory,
eval_once_check,
eval_seq,
@@ -28,7 +28,6 @@
get_type_for_exact_size,
ir_tuple_from_args,
make_setter,
- needs_external_call_wrap,
promote_signed_int,
sar,
shl,
@@ -36,7 +35,7 @@
unwrap_location,
)
from vyper.codegen.expr import Expr
-from vyper.codegen.ir_node import Encoding
+from vyper.codegen.ir_node import Encoding, scope_multi
from vyper.codegen.keccak256_helper import keccak256_helper
from vyper.evm.address_space import MEMORY, STORAGE
from vyper.exceptions import (
@@ -44,14 +43,13 @@
CompilerPanic,
InvalidLiteral,
InvalidType,
- OverflowException,
StateAccessViolation,
StructureException,
TypeMismatch,
UnfoldableNode,
ZeroDivisionException,
)
-from vyper.semantics.analysis.base import VarInfo
+from vyper.semantics.analysis.base import Modifiability, VarInfo
from vyper.semantics.analysis.utils import (
get_common_types,
get_exact_type_from_node,
@@ -88,7 +86,6 @@
EIP_170_LIMIT,
SHA3_PER_WORD,
MemoryPositions,
- SizeLimits,
bytes_to_int,
ceil32,
fourbytes_to_int,
@@ -98,22 +95,20 @@
)
from ._convert import convert
-from ._signatures import BuiltinFunction, process_inputs
+from ._signatures import BuiltinFunctionT, process_inputs
SHA256_ADDRESS = 2
SHA256_BASE_GAS = 60
SHA256_PER_WORD_GAS = 12
-class FoldedFunction(BuiltinFunction):
+class FoldedFunctionT(BuiltinFunctionT):
# Base class for nodes which should always be folded
- # Since foldable builtin functions are not folded before semantics validation,
- # this flag is used for `check_kwargable` in semantics validation.
- _kwargable = True
+ _modifiability = Modifiability.CONSTANT
-class TypenameFoldedFunction(FoldedFunction):
+class TypenameFoldedFunctionT(FoldedFunctionT):
# Base class for builtin functions that:
# (1) take a typename as the only argument; and
# (2) should always be folded.
@@ -126,24 +121,25 @@ def fetch_call_return(self, node):
type_ = self.infer_arg_types(node)[0].typedef
return type_
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
validate_call_args(node, 1)
input_typedef = TYPE_T(type_from_annotation(node.args[0]))
return [input_typedef]
-class Floor(BuiltinFunction):
+class Floor(BuiltinFunctionT):
_id = "floor"
_inputs = [("value", DecimalT())]
# TODO: maybe use int136?
_return_type = INT256_T
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 1)
- if not isinstance(node.args[0], vy_ast.Decimal):
+ value = node.args[0].get_folded_value()
+ if not isinstance(value, vy_ast.Decimal):
raise UnfoldableNode
- value = math.floor(node.args[0].value)
+ value = math.floor(value.value)
return vy_ast.Int.from_node(node, value=value)
@process_inputs
@@ -162,18 +158,19 @@ def build_IR(self, expr, args, kwargs, context):
return b1.resolve(ret)
-class Ceil(BuiltinFunction):
+class Ceil(BuiltinFunctionT):
_id = "ceil"
_inputs = [("value", DecimalT())]
# TODO: maybe use int136?
_return_type = INT256_T
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 1)
- if not isinstance(node.args[0], vy_ast.Decimal):
+ value = node.args[0].get_folded_value()
+ if not isinstance(value, vy_ast.Decimal):
raise UnfoldableNode
- value = math.ceil(node.args[0].value)
+ value = math.ceil(value.value)
return vy_ast.Int.from_node(node, value=value)
@process_inputs
@@ -192,7 +189,7 @@ def build_IR(self, expr, args, kwargs, context):
return b1.resolve(ret)
-class Convert(BuiltinFunction):
+class Convert(BuiltinFunctionT):
_id = "convert"
def fetch_call_return(self, node):
@@ -202,7 +199,7 @@ def fetch_call_return(self, node):
return target_typedef.typedef
# TODO: push this down into convert.py for more consistency
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
validate_call_args(node, 2)
target_type = type_from_annotation(node.args[1])
@@ -285,14 +282,13 @@ def _build_adhoc_slice_node(sub: IRnode, start: IRnode, length: IRnode, context:
# note: this and a lot of other builtins could be refactored to accept any uint type
-class Slice(BuiltinFunction):
+class Slice(BuiltinFunctionT):
_id = "slice"
_inputs = [
("b", (BYTES32_T, BytesT.any(), StringT.any())),
("start", UINT256_T),
("length", UINT256_T),
]
- _return_type = None
def fetch_call_return(self, node):
arg_type, _, _ = self.infer_arg_types(node)
@@ -338,7 +334,7 @@ def fetch_call_return(self, node):
return return_type
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
# return a concrete type for `b`
b_type = get_possible_types_from_node(node.args[0]).pop()
@@ -457,24 +453,29 @@ def build_IR(self, expr, args, kwargs, context):
return b1.resolve(b2.resolve(b3.resolve(ret)))
-class Len(BuiltinFunction):
+class Len(BuiltinFunctionT):
_id = "len"
_inputs = [("b", (StringT.any(), BytesT.any(), DArrayT.any()))]
_return_type = UINT256_T
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 1)
- arg = node.args[0]
+ arg = node.args[0].get_folded_value()
if isinstance(arg, (vy_ast.Str, vy_ast.Bytes)):
length = len(arg.value)
elif isinstance(arg, vy_ast.Hex):
- # 2 characters represent 1 byte and we subtract 1 to ignore the leading `0x`
- length = len(arg.value) // 2 - 1
+ length = len(arg.bytes_value)
else:
raise UnfoldableNode
return vy_ast.Int.from_node(node, value=length)
+ def infer_arg_types(self, node, expected_return_typ=None):
+ self._validate_arg_types(node)
+ # return a concrete type
+ typ = get_possible_types_from_node(node.args[0]).pop()
+ return [typ]
+
def build_IR(self, node, context):
arg = Expr(node.args[0], context).ir_node
if arg.value == "~calldata":
@@ -482,7 +483,7 @@ def build_IR(self, node, context):
return get_bytearray_length(arg)
-class Concat(BuiltinFunction):
+class Concat(BuiltinFunctionT):
_id = "concat"
def fetch_call_return(self, node):
@@ -499,7 +500,7 @@ def fetch_call_return(self, node):
return_type.set_length(length)
return return_type
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
if len(node.args) < 2:
raise ArgumentException("Invalid argument count: expected at least 2", node)
@@ -587,28 +588,28 @@ def build_IR(self, expr, context):
)
-class Keccak256(BuiltinFunction):
+class Keccak256(BuiltinFunctionT):
_id = "keccak256"
# TODO allow any BytesM_T
_inputs = [("value", (BytesT.any(), BYTES32_T, StringT.any()))]
_return_type = BYTES32_T
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 1)
- if isinstance(node.args[0], vy_ast.Bytes):
- value = node.args[0].value
- elif isinstance(node.args[0], vy_ast.Str):
- value = node.args[0].value.encode()
- elif isinstance(node.args[0], vy_ast.Hex):
- length = len(node.args[0].value) // 2 - 1
- value = int(node.args[0].value, 16).to_bytes(length, "big")
+ value = node.args[0].get_folded_value()
+ if isinstance(value, vy_ast.Bytes):
+ value = value.value
+ elif isinstance(value, vy_ast.Str):
+ value = value.value.encode()
+ elif isinstance(value, vy_ast.Hex):
+ value = value.bytes_value
else:
raise UnfoldableNode
hash_ = f"0x{keccak256(value).hex()}"
return vy_ast.Hex.from_node(node, value=hash_)
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
# return a concrete type for `value`
value_type = get_possible_types_from_node(node.args[0]).pop()
@@ -635,27 +636,27 @@ def _make_sha256_call(inp_start, inp_len, out_start, out_len):
]
-class Sha256(BuiltinFunction):
+class Sha256(BuiltinFunctionT):
_id = "sha256"
_inputs = [("value", (BYTES32_T, BytesT.any(), StringT.any()))]
_return_type = BYTES32_T
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 1)
- if isinstance(node.args[0], vy_ast.Bytes):
- value = node.args[0].value
- elif isinstance(node.args[0], vy_ast.Str):
- value = node.args[0].value.encode()
- elif isinstance(node.args[0], vy_ast.Hex):
- length = len(node.args[0].value) // 2 - 1
- value = int(node.args[0].value, 16).to_bytes(length, "big")
+ value = node.args[0].get_folded_value()
+ if isinstance(value, vy_ast.Bytes):
+ value = value.value
+ elif isinstance(value, vy_ast.Str):
+ value = value.value.encode()
+ elif isinstance(value, vy_ast.Hex):
+ value = value.bytes_value
else:
raise UnfoldableNode
hash_ = f"0x{hashlib.sha256(value).hexdigest()}"
return vy_ast.Hex.from_node(node, value=hash_)
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
# return a concrete type for `value`
value_type = get_possible_types_from_node(node.args[0]).pop()
@@ -707,20 +708,22 @@ def build_IR(self, expr, args, kwargs, context):
)
-class MethodID(FoldedFunction):
+class MethodID(FoldedFunctionT):
_id = "method_id"
+ _inputs = [("value", StringT.any())]
+ _kwargs = {"output_type": KwargSettings("TYPE_DEFINITION", BytesT(4))}
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 1, ["output_type"])
- args = node.args
- if not isinstance(args[0], vy_ast.Str):
- raise InvalidType("method id must be given as a literal string", args[0])
- if " " in args[0].value:
- raise InvalidLiteral("Invalid function signature - no spaces allowed.")
+ value = node.args[0].get_folded_value()
+ if not isinstance(value, vy_ast.Str):
+ raise InvalidType("method id must be given as a literal string", node.args[0])
+ if " " in value.value:
+ raise InvalidLiteral("Invalid function signature - no spaces allowed.", node.args[0])
- return_type = self.infer_kwarg_types(node)
- value = method_id_int(args[0].value)
+ return_type = self.infer_kwarg_types(node)["output_type"].typedef
+ value = method_id_int(value.value)
if return_type.compare_type(BYTES4_T):
return vy_ast.Hex.from_node(node, value=hex(value))
@@ -730,24 +733,25 @@ def evaluate(self, node):
def fetch_call_return(self, node):
validate_call_args(node, 1, ["output_type"])
- type_ = self.infer_kwarg_types(node)
+ type_ = self.infer_kwarg_types(node)["output_type"].typedef
return type_
+ def infer_arg_types(self, node, expected_return_typ=None):
+ return [self._inputs[0][1]]
+
def infer_kwarg_types(self, node):
if node.keywords:
- return_type = type_from_annotation(node.keywords[0].value)
- if return_type.compare_type(BYTES4_T):
- return BYTES4_T
- elif isinstance(return_type, BytesT) and return_type.length == 4:
- return BytesT(4)
- else:
+ output_type = type_from_annotation(node.keywords[0].value)
+ if output_type not in (BytesT(4), BYTES4_T):
raise ArgumentException("output_type must be Bytes[4] or bytes4", node.keywords[0])
+ else:
+ # default to `Bytes[4]`
+ output_type = BytesT(4)
- # If `output_type` is not given, default to `Bytes[4]`
- return BytesT(4)
+ return {"output_type": TYPE_T(output_type)}
-class ECRecover(BuiltinFunction):
+class ECRecover(BuiltinFunctionT):
_id = "ecrecover"
_inputs = [
("hash", BYTES32_T),
@@ -757,7 +761,7 @@ class ECRecover(BuiltinFunction):
]
_return_type = AddressT()
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
v_t, r_t, s_t = [get_possible_types_from_node(arg).pop() for arg in node.args[1:]]
return [BYTES32_T, v_t, r_t, s_t]
@@ -782,7 +786,7 @@ def build_IR(self, expr, args, kwargs, context):
)
-class _ECArith(BuiltinFunction):
+class _ECArith(BuiltinFunctionT):
@process_inputs
def build_IR(self, expr, _args, kwargs, context):
args_tuple = ir_tuple_from_args(_args)
@@ -841,21 +845,20 @@ def _storage_element_getter(index):
return IRnode.from_list(["sload", ["add", "_sub", ["add", 1, index]]], typ=INT128_T)
-class Extract32(BuiltinFunction):
+class Extract32(BuiltinFunctionT):
_id = "extract32"
_inputs = [("b", BytesT.any()), ("start", IntegerT.unsigneds())]
# "TYPE_DEFINITION" is a placeholder value for a type definition string, and
# will be replaced by a `TYPE_T` object in `infer_kwarg_types`
# (note that it is ignored in _validate_arg_types)
_kwargs = {"output_type": KwargSettings("TYPE_DEFINITION", BYTES32_T)}
- _return_type = None
def fetch_call_return(self, node):
self._validate_arg_types(node)
return_type = self.infer_kwarg_types(node)["output_type"].typedef
return return_type
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
input_type = get_possible_types_from_node(node.args[0]).pop()
return [input_type, UINT256_T]
@@ -953,7 +956,7 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode.from_list(clamp_basetype(o), typ=ret_type)
-class AsWeiValue(BuiltinFunction):
+class AsWeiValue(BuiltinFunctionT):
_id = "as_wei_value"
_inputs = [("value", (IntegerT.any(), DecimalT())), ("unit", StringT.any())]
_return_type = UINT256_T
@@ -970,42 +973,37 @@ class AsWeiValue(BuiltinFunction):
}
def get_denomination(self, node):
- if not isinstance(node.args[1], vy_ast.Str):
+ value = node.args[1].get_folded_value()
+ if not isinstance(value, vy_ast.Str):
raise ArgumentException(
"Wei denomination must be given as a literal string", node.args[1]
)
try:
- denom = next(v for k, v in self.wei_denoms.items() if node.args[1].value in k)
+ denom = next(v for k, v in self.wei_denoms.items() if value.value in k)
except StopIteration:
- raise ArgumentException(
- f"Unknown denomination: {node.args[1].value}", node.args[1]
- ) from None
+ raise ArgumentException(f"Unknown denomination: {value.value}", node.args[1]) from None
return denom
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 2)
denom = self.get_denomination(node)
- if not isinstance(node.args[0], (vy_ast.Decimal, vy_ast.Int)):
+ value = node.args[0].get_folded_value()
+ if not isinstance(value, (vy_ast.Decimal, vy_ast.Int)):
raise UnfoldableNode
- value = node.args[0].value
+ value = value.value
if value < 0:
raise InvalidLiteral("Negative wei value not allowed", node.args[0])
- if isinstance(value, int) and value >= 2**256:
- raise InvalidLiteral("Value out of range for uint256", node.args[0])
- if isinstance(value, Decimal) and value > SizeLimits.MAX_AST_DECIMAL:
- raise InvalidLiteral("Value out of range for decimal", node.args[0])
-
return vy_ast.Int.from_node(node, value=int(value * denom))
def fetch_call_return(self, node):
self.infer_arg_types(node)
return self._return_type
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
# return a concrete type instead of abstract type
value_type = get_possible_types_from_node(node.args[0]).pop()
@@ -1052,7 +1050,7 @@ def build_IR(self, expr, args, kwargs, context):
empty_value = IRnode.from_list(0, typ=BYTES32_T)
-class RawCall(BuiltinFunction):
+class RawCall(BuiltinFunctionT):
_id = "raw_call"
_inputs = [("to", AddressT()), ("data", BytesT.any())]
_kwargs = {
@@ -1063,7 +1061,6 @@ class RawCall(BuiltinFunction):
"is_static_call": KwargSettings(BoolT(), False, require_literal=True),
"revert_on_failure": KwargSettings(BoolT(), True, require_literal=True),
}
- _return_type = None
def fetch_call_return(self, node):
self._validate_arg_types(node)
@@ -1071,8 +1068,14 @@ def fetch_call_return(self, node):
kwargz = {i.arg: i.value for i in node.keywords}
outsize = kwargz.get("max_outsize")
+ if outsize is not None:
+ outsize = outsize.get_folded_value()
+
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 revert_on_failure is not None:
+ revert_on_failure = revert_on_failure.get_folded_value().value
+ else:
+ revert_on_failure = True
if outsize is None or outsize.value == 0:
if revert_on_failure:
@@ -1090,7 +1093,7 @@ def fetch_call_return(self, node):
return return_type
return TupleT([BoolT(), return_type])
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
# return a concrete type for `data`
data_type = get_possible_types_from_node(node.args[1]).pop()
@@ -1155,14 +1158,17 @@ def build_IR(self, expr, args, kwargs, context):
outsize,
]
- if delegate_call:
- call_op = ["delegatecall", gas, to, *common_call_args]
- elif static_call:
- call_op = ["staticcall", gas, to, *common_call_args]
- else:
- call_op = ["call", gas, to, value, *common_call_args]
+ gas, value = IRnode.from_list(gas), IRnode.from_list(value)
+ with scope_multi((to, value, gas), ("_to", "_value", "_gas")) as (b1, (to, value, gas)):
+ if delegate_call:
+ call_op = ["delegatecall", gas, to, *common_call_args]
+ elif static_call:
+ call_op = ["staticcall", gas, to, *common_call_args]
+ else:
+ call_op = ["call", gas, to, value, *common_call_args]
- call_ir += [call_op]
+ call_ir += [call_op]
+ call_ir = b1.resolve(call_ir)
# build sequence IR
if outsize:
@@ -1206,12 +1212,11 @@ def build_IR(self, expr, args, kwargs, context):
raise CompilerPanic("unreachable!")
-class Send(BuiltinFunction):
+class Send(BuiltinFunctionT):
_id = "send"
_inputs = [("to", AddressT()), ("value", UINT256_T)]
# default gas stipend is 0
_kwargs = {"gas": KwargSettings(UINT256_T, 0)}
- _return_type = None
@process_inputs
def build_IR(self, expr, args, kwargs, context):
@@ -1223,10 +1228,9 @@ def build_IR(self, expr, args, kwargs, context):
)
-class SelfDestruct(BuiltinFunction):
+class SelfDestruct(BuiltinFunctionT):
_id = "selfdestruct"
_inputs = [("to", AddressT())]
- _return_type = None
_is_terminus = True
_warned = False
@@ -1242,7 +1246,7 @@ def build_IR(self, expr, args, kwargs, context):
)
-class BlockHash(BuiltinFunction):
+class BlockHash(BuiltinFunctionT):
_id = "blockhash"
_inputs = [("block_num", UINT256_T)]
_return_type = BYTES32_T
@@ -1255,7 +1259,7 @@ def build_IR(self, expr, args, kwargs, contact):
)
-class RawRevert(BuiltinFunction):
+class RawRevert(BuiltinFunctionT):
_id = "raw_revert"
_inputs = [("data", BytesT.any())]
_return_type = None
@@ -1264,7 +1268,7 @@ class RawRevert(BuiltinFunction):
def fetch_call_return(self, node):
return None
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
data_type = get_possible_types_from_node(node.args[0]).pop()
return [data_type]
@@ -1277,14 +1281,14 @@ def build_IR(self, expr, args, kwargs, context):
return b.resolve(IRnode.from_list(["revert", data, len_]))
-class RawLog(BuiltinFunction):
+class RawLog(BuiltinFunctionT):
_id = "raw_log"
_inputs = [("topics", DArrayT(BYTES32_T, 4)), ("data", (BYTES32_T, BytesT.any()))]
def fetch_call_return(self, node):
self.infer_arg_types(node)
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
if not isinstance(node.args[0], vy_ast.List) or len(node.args[0].elements) > 4:
@@ -1328,25 +1332,24 @@ def build_IR(self, expr, args, kwargs, context):
)
-class BitwiseAnd(BuiltinFunction):
+class BitwiseAnd(BuiltinFunctionT):
_id = "bitwise_and"
_inputs = [("x", UINT256_T), ("y", UINT256_T)]
_return_type = UINT256_T
_warned = False
- def evaluate(self, node):
+ def _try_fold(self, node):
if not self.__class__._warned:
vyper_warn("`bitwise_and()` is deprecated! Please use the & operator instead.")
self.__class__._warned = True
validate_call_args(node, 2)
- for arg in node.args:
- if not isinstance(arg, vy_ast.Int):
+ values = [i.get_folded_value() for i in node.args]
+ for val in values:
+ if not isinstance(val, vy_ast.Int):
raise UnfoldableNode
- if arg.value < 0 or arg.value >= 2**256:
- raise InvalidLiteral("Value out of range for uint256", arg)
- value = node.args[0].value & node.args[1].value
+ value = values[0].value & values[1].value
return vy_ast.Int.from_node(node, value=value)
@process_inputs
@@ -1354,25 +1357,24 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode.from_list(["and", args[0], args[1]], typ=UINT256_T)
-class BitwiseOr(BuiltinFunction):
+class BitwiseOr(BuiltinFunctionT):
_id = "bitwise_or"
_inputs = [("x", UINT256_T), ("y", UINT256_T)]
_return_type = UINT256_T
_warned = False
- def evaluate(self, node):
+ def _try_fold(self, node):
if not self.__class__._warned:
vyper_warn("`bitwise_or()` is deprecated! Please use the | operator instead.")
self.__class__._warned = True
validate_call_args(node, 2)
- for arg in node.args:
- if not isinstance(arg, vy_ast.Int):
+ values = [i.get_folded_value() for i in node.args]
+ for val in values:
+ if not isinstance(val, vy_ast.Int):
raise UnfoldableNode
- if arg.value < 0 or arg.value >= 2**256:
- raise InvalidLiteral("Value out of range for uint256", arg)
- value = node.args[0].value | node.args[1].value
+ value = values[0].value | values[1].value
return vy_ast.Int.from_node(node, value=value)
@process_inputs
@@ -1380,25 +1382,24 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode.from_list(["or", args[0], args[1]], typ=UINT256_T)
-class BitwiseXor(BuiltinFunction):
+class BitwiseXor(BuiltinFunctionT):
_id = "bitwise_xor"
_inputs = [("x", UINT256_T), ("y", UINT256_T)]
_return_type = UINT256_T
_warned = False
- def evaluate(self, node):
+ def _try_fold(self, node):
if not self.__class__._warned:
vyper_warn("`bitwise_xor()` is deprecated! Please use the ^ operator instead.")
self.__class__._warned = True
validate_call_args(node, 2)
- for arg in node.args:
- if not isinstance(arg, vy_ast.Int):
+ values = [i.get_folded_value() for i in node.args]
+ for val in values:
+ if not isinstance(val, vy_ast.Int):
raise UnfoldableNode
- if arg.value < 0 or arg.value >= 2**256:
- raise InvalidLiteral("Value out of range for uint256", arg)
- value = node.args[0].value ^ node.args[1].value
+ value = values[0].value ^ values[1].value
return vy_ast.Int.from_node(node, value=value)
@process_inputs
@@ -1406,24 +1407,23 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode.from_list(["xor", args[0], args[1]], typ=UINT256_T)
-class BitwiseNot(BuiltinFunction):
+class BitwiseNot(BuiltinFunctionT):
_id = "bitwise_not"
_inputs = [("x", UINT256_T)]
_return_type = UINT256_T
_warned = False
- def evaluate(self, node):
+ def _try_fold(self, node):
if not self.__class__._warned:
- vyper_warn("`bitwise_not()` is deprecated! Please use the ^ operator instead.")
+ vyper_warn("`bitwise_not()` is deprecated! Please use the ~ operator instead.")
self.__class__._warned = True
validate_call_args(node, 1)
- if not isinstance(node.args[0], vy_ast.Int):
+ value = node.args[0].get_folded_value()
+ if not isinstance(value, vy_ast.Int):
raise UnfoldableNode
- value = node.args[0].value
- if value < 0 or value >= 2**256:
- raise InvalidLiteral("Value out of range for uint256", node.args[0])
+ value = value.value
value = (2**256 - 1) - value
return vy_ast.Int.from_node(node, value=value)
@@ -1433,23 +1433,22 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode.from_list(["not", args[0]], typ=UINT256_T)
-class Shift(BuiltinFunction):
+class Shift(BuiltinFunctionT):
_id = "shift"
_inputs = [("x", (UINT256_T, INT256_T)), ("_shift_bits", IntegerT.any())]
_return_type = UINT256_T
_warned = False
- def evaluate(self, node):
+ def _try_fold(self, node):
if not self.__class__._warned:
vyper_warn("`shift()` is deprecated! Please use the << or >> operator instead.")
self.__class__._warned = True
validate_call_args(node, 2)
- if [i for i in node.args if not isinstance(i, vy_ast.Int)]:
+ args = [i.get_folded_value() for i in node.args]
+ if any(not isinstance(i, vy_ast.Int) for i in args):
raise UnfoldableNode
- value, shift = [i.value for i in node.args]
- if value < 0 or value >= 2**256:
- raise InvalidLiteral("Value out of range for uint256", node.args[0])
+ value, shift = [i.value for i in args]
if shift < -256 or shift > 256:
# this validation is performed to prevent the compiler from hanging
# rather than for correctness because the post-folded constant would
@@ -1466,7 +1465,7 @@ def fetch_call_return(self, node):
# return type is the type of the first argument
return self.infer_arg_types(node)[0]
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
# return a concrete type instead of SignedIntegerAbstractType
arg_ty = get_possible_types_from_node(node.args[0])[0]
@@ -1487,21 +1486,20 @@ def build_IR(self, expr, args, kwargs, context):
return b1.resolve(b2.resolve(IRnode.from_list(ret, typ=argty)))
-class _AddMulMod(BuiltinFunction):
+class _AddMulMod(BuiltinFunctionT):
_inputs = [("a", UINT256_T), ("b", UINT256_T), ("c", UINT256_T)]
_return_type = UINT256_T
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 3)
- if isinstance(node.args[2], vy_ast.Int) and node.args[2].value == 0:
+ args = [i.get_folded_value() for i in node.args]
+ if isinstance(args[2], vy_ast.Int) and args[2].value == 0:
raise ZeroDivisionException("Modulo by 0", node.args[2])
- for arg in node.args:
+ for arg in args:
if not isinstance(arg, vy_ast.Int):
raise UnfoldableNode
- if arg.value < 0 or arg.value >= 2**256:
- raise InvalidLiteral("Value out of range for uint256", arg)
- value = self._eval_fn(node.args[0].value, node.args[1].value) % node.args[2].value
+ value = self._eval_fn(args[0].value, args[1].value) % args[2].value
return vy_ast.Int.from_node(node, value=value)
@process_inputs
@@ -1528,20 +1526,18 @@ class MulMod(_AddMulMod):
_opcode = "mulmod"
-class PowMod256(BuiltinFunction):
+class PowMod256(BuiltinFunctionT):
_id = "pow_mod256"
_inputs = [("a", UINT256_T), ("b", UINT256_T)]
_return_type = UINT256_T
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 2)
- if next((i for i in node.args if not isinstance(i, vy_ast.Int)), None):
- raise UnfoldableNode
-
- left, right = node.args
- if left.value < 0 or right.value < 0:
+ values = [i.get_folded_value() for i in node.args]
+ if any(not isinstance(i, vy_ast.Int) for i in values):
raise UnfoldableNode
+ left, right = values
value = pow(left.value, right.value, 2**256)
return vy_ast.Int.from_node(node, value=value)
@@ -1551,23 +1547,18 @@ def build_IR(self, expr, context):
return IRnode.from_list(["exp", left, right], typ=left.typ)
-class Abs(BuiltinFunction):
+class Abs(BuiltinFunctionT):
_id = "abs"
_inputs = [("value", INT256_T)]
_return_type = INT256_T
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 1)
- if not isinstance(node.args[0], vy_ast.Int):
+ value = node.args[0].get_folded_value()
+ if not isinstance(value, vy_ast.Int):
raise UnfoldableNode
- value = node.args[0].value
- if not SizeLimits.MIN_INT256 <= value <= SizeLimits.MAX_INT256:
- raise OverflowException("Literal is outside of allowable range for int256")
- value = abs(value)
- if not SizeLimits.MIN_INT256 <= value <= SizeLimits.MAX_INT256:
- raise OverflowException("Absolute literal value is outside allowable range for int256")
-
+ value = abs(value.value)
return vy_ast.Int.from_node(node, value=value)
def build_IR(self, expr, context):
@@ -1589,13 +1580,15 @@ def build_IR(self, expr, context):
# CREATE* functions
+CREATE2_SENTINEL = dummy_node_for_type(BYTES32_T)
+
# create helper functions
# generates CREATE op sequence + zero check for result
-def _create_ir(value, buf, length, salt=None, checked=True):
+def _create_ir(value, buf, length, salt, checked=True):
args = [value, buf, length]
create_op = "create"
- if salt is not None:
+ if salt is not CREATE2_SENTINEL:
create_op = "create2"
args.append(salt)
@@ -1700,7 +1693,7 @@ def _create_preamble(codesize):
return ["or", bytes_to_int(evm), shl(shl_bits, codesize)], evm_len
-class _CreateBase(BuiltinFunction):
+class _CreateBase(BuiltinFunctionT):
_kwargs = {
"value": KwargSettings(UINT256_T, zero_value),
"salt": KwargSettings(BYTES32_T, empty_value),
@@ -1713,8 +1706,9 @@ def build_IR(self, expr, args, kwargs, context):
context.check_is_not_constant("use {self._id}", expr)
should_use_create2 = "salt" in [kwarg.arg for kwarg in expr.keywords]
+
if not should_use_create2:
- kwargs["salt"] = None
+ kwargs["salt"] = CREATE2_SENTINEL
ir_builder = self._build_create_IR(expr, args, context, **kwargs)
@@ -1794,13 +1788,16 @@ def _add_gas_estimate(self, args, should_use_create2):
def _build_create_IR(self, expr, args, context, value, salt):
target = args[0]
- with target.cache_when_complex("create_target") as (b1, target):
+ # something we can pass to scope_multi
+ with scope_multi(
+ (target, value, salt), ("create_target", "create_value", "create_salt")
+ ) as (b1, (target, value, salt)):
codesize = IRnode.from_list(["extcodesize", target])
msize = IRnode.from_list(["msize"])
- with codesize.cache_when_complex("target_codesize") as (
+ with scope_multi((codesize, msize), ("target_codesize", "mem_ofst")) as (
b2,
- codesize,
- ), msize.cache_when_complex("mem_ofst") as (b3, mem_ofst):
+ (codesize, mem_ofst),
+ ):
ir = ["seq"]
# make sure there is actually code at the target
@@ -1824,7 +1821,7 @@ def _build_create_IR(self, expr, args, context, value, salt):
ir.append(_create_ir(value, buf, buf_len, salt))
- return b1.resolve(b2.resolve(b3.resolve(ir)))
+ return b1.resolve(b2.resolve(ir))
class CreateFromBlueprint(_CreateBase):
@@ -1877,17 +1874,18 @@ def _build_create_IR(self, expr, args, context, value, salt, code_offset, raw_ar
# (since the abi encoder could write to fresh memory).
# it would be good to not require the memory copy, but need
# to evaluate memory safety.
- with target.cache_when_complex("create_target") as (b1, target), argslen.cache_when_complex(
- "encoded_args_len"
- ) as (b2, encoded_args_len), code_offset.cache_when_complex("code_ofst") as (b3, codeofst):
- codesize = IRnode.from_list(["sub", ["extcodesize", target], codeofst])
+ with scope_multi(
+ (target, value, salt, argslen, code_offset),
+ ("create_target", "create_value", "create_salt", "encoded_args_len", "code_offset"),
+ ) as (b1, (target, value, salt, encoded_args_len, code_offset)):
+ codesize = IRnode.from_list(["sub", ["extcodesize", target], code_offset])
# copy code to memory starting from msize. we are clobbering
# unused memory so it's safe.
msize = IRnode.from_list(["msize"], location=MEMORY)
- with codesize.cache_when_complex("target_codesize") as (
- b4,
- codesize,
- ), msize.cache_when_complex("mem_ofst") as (b5, mem_ofst):
+ with scope_multi((codesize, msize), ("target_codesize", "mem_ofst")) as (
+ b2,
+ (codesize, mem_ofst),
+ ):
ir = ["seq"]
# make sure there is code at the target, and that
@@ -1906,9 +1904,8 @@ def _build_create_IR(self, expr, args, context, value, salt, code_offset, raw_ar
# copy the target code into memory.
# layout starting from mem_ofst:
- # 00...00 (22 0's) | preamble | bytecode
- ir.append(["extcodecopy", target, mem_ofst, codeofst, codesize])
-
+ # |
+ ir.append(["extcodecopy", target, mem_ofst, code_offset, codesize])
ir.append(copy_bytes(add_ofst(mem_ofst, codesize), argbuf, encoded_args_len, bufsz))
# theoretically, dst = "msize", but just be safe.
@@ -1922,10 +1919,10 @@ def _build_create_IR(self, expr, args, context, value, salt, code_offset, raw_ar
ir.append(_create_ir(value, mem_ofst, length, salt))
- return b1.resolve(b2.resolve(b3.resolve(b4.resolve(b5.resolve(ir)))))
+ return b1.resolve(b2.resolve(ir))
-class _UnsafeMath(BuiltinFunction):
+class _UnsafeMath(BuiltinFunctionT):
# TODO add unsafe math for `decimal`s
_inputs = [("a", IntegerT.any()), ("b", IntegerT.any())]
@@ -1936,7 +1933,7 @@ def fetch_call_return(self, node):
return_type = self.infer_arg_types(node).pop()
return return_type
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
types_list = get_common_types(*node.args, filter_fn=lambda x: isinstance(x, IntegerT))
@@ -1991,37 +1988,29 @@ class UnsafeDiv(_UnsafeMath):
op = "div"
-class _MinMax(BuiltinFunction):
+class _MinMax(BuiltinFunctionT):
_inputs = [("a", (DecimalT(), IntegerT.any())), ("b", (DecimalT(), IntegerT.any()))]
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 2)
- if not isinstance(node.args[0], type(node.args[1])):
+
+ left = node.args[0].get_folded_value()
+ right = node.args[1].get_folded_value()
+ if not isinstance(left, type(right)):
raise UnfoldableNode
- if not isinstance(node.args[0], (vy_ast.Decimal, vy_ast.Int)):
+ if not isinstance(left, (vy_ast.Decimal, vy_ast.Int)):
raise UnfoldableNode
- left, right = (i.value for i in node.args)
- if isinstance(left, Decimal) and (
- min(left, right) < SizeLimits.MIN_AST_DECIMAL
- or max(left, right) > SizeLimits.MAX_AST_DECIMAL
- ):
- raise InvalidType("Decimal value is outside of allowable range", node)
-
types_list = get_common_types(
- *node.args, filter_fn=lambda x: isinstance(x, (IntegerT, DecimalT))
+ *(left, right), filter_fn=lambda x: isinstance(x, (IntegerT, DecimalT))
)
if not types_list:
raise TypeMismatch("Cannot perform action between dislike numeric types", node)
- value = self._eval_fn(left, right)
- return type(node.args[0]).from_node(node, value=value)
+ value = self._eval_fn(left.value, right.value)
+ return type(left).from_node(node, value=value)
def fetch_call_return(self, node):
- return_type = self.infer_arg_types(node).pop()
- return return_type
-
- def infer_arg_types(self, node):
self._validate_arg_types(node)
types_list = get_common_types(
@@ -2030,8 +2019,13 @@ def infer_arg_types(self, node):
if not types_list:
raise TypeMismatch("Cannot perform action between dislike numeric types", node)
- type_ = types_list.pop()
- return [type_, type_]
+ return types_list
+
+ def infer_arg_types(self, node, expected_return_typ=None):
+ types_list = self.fetch_call_return(node)
+ # type mismatch should have been caught in `fetch_call_return`
+ assert expected_return_typ in types_list
+ return [expected_return_typ, expected_return_typ]
@process_inputs
def build_IR(self, expr, args, kwargs, context):
@@ -2065,7 +2059,7 @@ class Max(_MinMax):
_opcode = "gt"
-class Uint2Str(BuiltinFunction):
+class Uint2Str(BuiltinFunctionT):
_id = "uint2str"
_inputs = [("x", IntegerT.unsigneds())]
@@ -2075,15 +2069,19 @@ def fetch_call_return(self, node):
len_needed = math.ceil(bits * math.log(2) / math.log(10))
return StringT(len_needed)
- def evaluate(self, node):
+ def _try_fold(self, node):
validate_call_args(node, 1)
- if not isinstance(node.args[0], vy_ast.Int):
+ value = node.args[0].get_folded_value()
+ if not isinstance(value, vy_ast.Int):
raise UnfoldableNode
- value = str(node.args[0].value)
+ value = value.value
+ if value < 0:
+ raise InvalidType("Only unsigned ints allowed", node)
+ value = str(value)
return vy_ast.Str.from_node(node, value=value)
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
self._validate_arg_types(node)
input_type = get_possible_types_from_node(node.args[0]).pop()
return [input_type]
@@ -2137,7 +2135,7 @@ def build_IR(self, expr, args, kwargs, context):
return b1.resolve(IRnode.from_list(ret, location=MEMORY, typ=return_t))
-class Sqrt(BuiltinFunction):
+class Sqrt(BuiltinFunctionT):
_id = "sqrt"
_inputs = [("d", DecimalT())]
_return_type = DecimalT()
@@ -2193,7 +2191,7 @@ def build_IR(self, expr, args, kwargs, context):
)
-class ISqrt(BuiltinFunction):
+class ISqrt(BuiltinFunctionT):
_id = "isqrt"
_inputs = [("d", UINT256_T)]
_return_type = UINT256_T
@@ -2243,7 +2241,7 @@ def build_IR(self, expr, args, kwargs, context):
return b1.resolve(IRnode.from_list(ret, typ=UINT256_T))
-class Empty(TypenameFoldedFunction):
+class Empty(TypenameFoldedFunctionT):
_id = "empty"
def fetch_call_return(self, node):
@@ -2258,7 +2256,7 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode("~empty", typ=output_type)
-class Breakpoint(BuiltinFunction):
+class Breakpoint(BuiltinFunctionT):
_id = "breakpoint"
_inputs: list = []
@@ -2276,7 +2274,7 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode.from_list("breakpoint", annotation="breakpoint()")
-class Print(BuiltinFunction):
+class Print(BuiltinFunctionT):
_id = "print"
_inputs: list = []
_has_varargs = True
@@ -2354,11 +2352,9 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode.from_list(ret, annotation="print:" + sig)
-class ABIEncode(BuiltinFunction):
+class ABIEncode(BuiltinFunctionT):
_id = "_abi_encode" # TODO prettier to rename this to abi.encode
# signature: *, ensure_tuple= -> Bytes[]
- # (check the signature manually since we have no utility methods
- # to handle varargs.)
# explanation of ensure_tuple:
# default is to force even a single value into a tuple,
# e.g. _abi_encode(bytes) -> _abi_encode((bytes,))
@@ -2473,7 +2469,7 @@ def build_IR(self, expr, args, kwargs, context):
return IRnode.from_list(ret, location=MEMORY, typ=buf_t)
-class ABIDecode(BuiltinFunction):
+class ABIDecode(BuiltinFunctionT):
_id = "_abi_decode"
_inputs = [("data", BytesT.any()), ("output_type", "TYPE_DEFINITION")]
_kwargs = {"unwrap_tuple": KwargSettings(BoolT(), True, require_literal=True)}
@@ -2482,13 +2478,15 @@ def fetch_call_return(self, node):
_, output_type = self.infer_arg_types(node)
return output_type.typedef
- def infer_arg_types(self, node):
+ def infer_arg_types(self, node, expected_return_typ=None):
+ self._validate_arg_types(node)
+
validate_call_args(node, 2, ["unwrap_tuple"])
data_type = get_exact_type_from_node(node.args[0])
- output_typedef = TYPE_T(type_from_annotation(node.args[1]))
+ output_type = type_from_annotation(node.args[1])
- return [data_type, output_typedef]
+ return [data_type, TYPE_T(output_type)]
@process_inputs
def build_IR(self, expr, args, kwargs, context):
@@ -2519,24 +2517,11 @@ def build_IR(self, expr, args, kwargs, context):
)
data = ensure_in_memory(data, context)
+
with data.cache_when_complex("to_decode") as (b1, data):
data_ptr = bytes_data_ptr(data)
data_len = get_bytearray_length(data)
- # Normally, ABI-encoded data assumes the argument is a tuple
- # (See comments for `wrap_value_for_external_return`)
- # However, we do not want to use `wrap_value_for_external_return`
- # technique as used in external call codegen because in order to be
- # type-safe we would need an extra memory copy. To avoid a copy,
- # we manually add the ABI-dynamic offset so that it is
- # re-interpreted in-place.
- if (
- unwrap_tuple is True
- and needs_external_call_wrap(output_typ)
- and output_typ.abi_type.is_dynamic()
- ):
- data_ptr = add_ofst(data_ptr, 32)
-
ret = ["seq"]
if abi_min_size == abi_size_bound:
@@ -2545,22 +2530,34 @@ def build_IR(self, expr, args, kwargs, context):
# runtime assert: abi_min_size <= data_len <= abi_size_bound
ret.append(clamp2(abi_min_size, data_len, abi_size_bound, signed=False))
- # return pointer to the buffer
- ret.append(data_ptr)
-
- return b1.resolve(
- IRnode.from_list(
- ret,
- typ=output_typ,
- location=data.location,
- encoding=Encoding.ABI,
- annotation=f"abi_decode({output_typ})",
- )
+ to_decode = IRnode.from_list(
+ data_ptr,
+ typ=wrapped_typ,
+ location=data.location,
+ encoding=Encoding.ABI,
+ annotation=f"abi_decode({output_typ})",
)
+ to_decode.encoding = Encoding.ABI
+
+ # TODO optimization: skip make_setter when we don't need
+ # input validation
+ output_buf = context.new_internal_variable(wrapped_typ)
+ output = IRnode.from_list(output_buf, typ=wrapped_typ, location=MEMORY)
-class _MinMaxValue(TypenameFoldedFunction):
- def evaluate(self, node):
+ # sanity check buffer size for wrapped output type will not buffer overflow
+ assert wrapped_typ.memory_bytes_required == output_typ.memory_bytes_required
+ ret.append(make_setter(output, to_decode))
+
+ ret.append(output)
+ # finalize. set the type and location for the return buffer.
+ # (note: unwraps the tuple type if necessary)
+ ret = IRnode.from_list(ret, typ=output_typ, location=MEMORY)
+ return b1.resolve(ret)
+
+
+class _MinMaxValue(TypenameFoldedFunctionT):
+ def _try_fold(self, node):
self._validate_arg_types(node)
input_type = type_from_annotation(node.args[0])
@@ -2575,10 +2572,13 @@ def evaluate(self, node):
if isinstance(input_type, IntegerT):
ret = vy_ast.Int.from_node(node, value=val)
- # TODO: to change to known_type once #3213 is merged
ret._metadata["type"] = input_type
return ret
+ def infer_arg_types(self, node, expected_return_typ=None):
+ input_typedef = TYPE_T(type_from_annotation(node.args[0]))
+ return [input_typedef]
+
class MinValue(_MinMaxValue):
_id = "min_value"
@@ -2594,10 +2594,10 @@ def _eval(self, type_):
return type_.ast_bounds[1]
-class Epsilon(TypenameFoldedFunction):
+class Epsilon(TypenameFoldedFunctionT):
_id = "epsilon"
- def evaluate(self, node):
+ def _try_fold(self, node):
self._validate_arg_types(node)
input_type = type_from_annotation(node.args[0])
diff --git a/vyper/builtins/interfaces/ERC165.py b/vyper/builtins/interfaces/ERC165.vyi
similarity index 66%
rename from vyper/builtins/interfaces/ERC165.py
rename to vyper/builtins/interfaces/ERC165.vyi
index 0a75431f3c..441130f77c 100644
--- a/vyper/builtins/interfaces/ERC165.py
+++ b/vyper/builtins/interfaces/ERC165.vyi
@@ -1,6 +1,4 @@
-interface_code = """
@view
@external
def supportsInterface(interface_id: bytes4) -> bool:
- pass
-"""
+ ...
diff --git a/vyper/builtins/interfaces/ERC20.py b/vyper/builtins/interfaces/ERC20.vyi
similarity index 66%
rename from vyper/builtins/interfaces/ERC20.py
rename to vyper/builtins/interfaces/ERC20.vyi
index a63408672b..ee533ab326 100644
--- a/vyper/builtins/interfaces/ERC20.py
+++ b/vyper/builtins/interfaces/ERC20.vyi
@@ -1,40 +1,38 @@
-interface_code = """
# Events
event Transfer:
- _from: indexed(address)
- _to: indexed(address)
- _value: uint256
+ sender: indexed(address)
+ recipient: indexed(address)
+ value: uint256
event Approval:
- _owner: indexed(address)
- _spender: indexed(address)
- _value: uint256
+ owner: indexed(address)
+ spender: indexed(address)
+ value: uint256
# Functions
@view
@external
def totalSupply() -> uint256:
- pass
+ ...
@view
@external
def balanceOf(_owner: address) -> uint256:
- pass
+ ...
@view
@external
def allowance(_owner: address, _spender: address) -> uint256:
- pass
+ ...
@external
def transfer(_to: address, _value: uint256) -> bool:
- pass
+ ...
@external
def transferFrom(_from: address, _to: address, _value: uint256) -> bool:
- pass
+ ...
@external
def approve(_spender: address, _value: uint256) -> bool:
- pass
-"""
+ ...
diff --git a/vyper/builtins/interfaces/ERC20Detailed.py b/vyper/builtins/interfaces/ERC20Detailed.py
deleted file mode 100644
index 03dd597e8a..0000000000
--- a/vyper/builtins/interfaces/ERC20Detailed.py
+++ /dev/null
@@ -1,22 +0,0 @@
-"""
-NOTE: interface uses `String[1]` where 1 is the lower bound of the string returned by the function.
- For end-users this means they can't use `implements: ERC20Detailed` unless their implementation
- uses a value n >= 1. Regardless this is fine as one can't do String[0] where n == 0.
-"""
-
-interface_code = """
-@view
-@external
-def name() -> String[1]:
- pass
-
-@view
-@external
-def symbol() -> String[1]:
- pass
-
-@view
-@external
-def decimals() -> uint8:
- pass
-"""
diff --git a/vyper/builtins/interfaces/ERC20Detailed.vyi b/vyper/builtins/interfaces/ERC20Detailed.vyi
new file mode 100644
index 0000000000..0be1c6f153
--- /dev/null
+++ b/vyper/builtins/interfaces/ERC20Detailed.vyi
@@ -0,0 +1,18 @@
+#NOTE: interface uses `String[1]` where 1 is the lower bound of the string returned by the function.
+# For end-users this means they can't use `implements: ERC20Detailed` unless their implementation
+# uses a value n >= 1. Regardless this is fine as one can't do String[0] where n == 0.
+
+@view
+@external
+def name() -> String[1]:
+ ...
+
+@view
+@external
+def symbol() -> String[1]:
+ ...
+
+@view
+@external
+def decimals() -> uint8:
+ ...
diff --git a/vyper/builtins/interfaces/ERC4626.py b/vyper/builtins/interfaces/ERC4626.vyi
similarity index 89%
rename from vyper/builtins/interfaces/ERC4626.py
rename to vyper/builtins/interfaces/ERC4626.vyi
index 21a9ce723a..6d9e4c6ef7 100644
--- a/vyper/builtins/interfaces/ERC4626.py
+++ b/vyper/builtins/interfaces/ERC4626.vyi
@@ -1,4 +1,3 @@
-interface_code = """
# Events
event Deposit:
sender: indexed(address)
@@ -17,76 +16,75 @@
@view
@external
def asset() -> address:
- pass
+ ...
@view
@external
def totalAssets() -> uint256:
- pass
+ ...
@view
@external
def convertToShares(assetAmount: uint256) -> uint256:
- pass
+ ...
@view
@external
def convertToAssets(shareAmount: uint256) -> uint256:
- pass
+ ...
@view
@external
def maxDeposit(owner: address) -> uint256:
- pass
+ ...
@view
@external
def previewDeposit(assets: uint256) -> uint256:
- pass
+ ...
@external
def deposit(assets: uint256, receiver: address=msg.sender) -> uint256:
- pass
+ ...
@view
@external
def maxMint(owner: address) -> uint256:
- pass
+ ...
@view
@external
def previewMint(shares: uint256) -> uint256:
- pass
+ ...
@external
def mint(shares: uint256, receiver: address=msg.sender) -> uint256:
- pass
+ ...
@view
@external
def maxWithdraw(owner: address) -> uint256:
- pass
+ ...
@view
@external
def previewWithdraw(assets: uint256) -> uint256:
- pass
+ ...
@external
def withdraw(assets: uint256, receiver: address=msg.sender, owner: address=msg.sender) -> uint256:
- pass
+ ...
@view
@external
def maxRedeem(owner: address) -> uint256:
- pass
+ ...
@view
@external
def previewRedeem(shares: uint256) -> uint256:
- pass
+ ...
@external
def redeem(shares: uint256, receiver: address=msg.sender, owner: address=msg.sender) -> uint256:
- pass
-"""
+ ...
diff --git a/vyper/builtins/interfaces/ERC721.py b/vyper/builtins/interfaces/ERC721.vyi
similarity index 59%
rename from vyper/builtins/interfaces/ERC721.py
rename to vyper/builtins/interfaces/ERC721.vyi
index 8dea4e4976..b8dcfd3c5f 100644
--- a/vyper/builtins/interfaces/ERC721.py
+++ b/vyper/builtins/interfaces/ERC721.vyi
@@ -1,70 +1,62 @@
-interface_code = """
# Events
event Transfer:
- _from: indexed(address)
- _to: indexed(address)
- _tokenId: indexed(uint256)
+ sender: indexed(address)
+ recipient: indexed(address)
+ token_id: indexed(uint256)
event Approval:
- _owner: indexed(address)
- _approved: indexed(address)
- _tokenId: indexed(uint256)
+ owner: indexed(address)
+ approved: indexed(address)
+ token_id: indexed(uint256)
event ApprovalForAll:
- _owner: indexed(address)
- _operator: indexed(address)
- _approved: bool
+ owner: indexed(address)
+ operator: indexed(address)
+ approved: bool
# Functions
@view
@external
def supportsInterface(interface_id: bytes4) -> bool:
- pass
+ ...
@view
@external
def balanceOf(_owner: address) -> uint256:
- pass
+ ...
@view
@external
def ownerOf(_tokenId: uint256) -> address:
- pass
+ ...
@view
@external
def getApproved(_tokenId: uint256) -> address:
- pass
+ ...
@view
@external
def isApprovedForAll(_owner: address, _operator: address) -> bool:
- pass
+ ...
@external
@payable
def transferFrom(_from: address, _to: address, _tokenId: uint256):
- pass
+ ...
@external
@payable
-def safeTransferFrom(_from: address, _to: address, _tokenId: uint256):
- pass
-
-@external
-@payable
-def safeTransferFrom(_from: address, _to: address, _tokenId: uint256, _data: Bytes[1024]):
- pass
+def safeTransferFrom(_from: address, _to: address, _tokenId: uint256, _data: Bytes[1024] = b""):
+ ...
@external
@payable
def approve(_approved: address, _tokenId: uint256):
- pass
+ ...
@external
def setApprovalForAll(_operator: address, _approved: bool):
- pass
-
-"""
+ ...
diff --git a/vyper/cli/utils.py b/vyper/cli/utils.py
deleted file mode 100644
index 1110ecdfdd..0000000000
--- a/vyper/cli/utils.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from pathlib import Path
-from typing import Sequence
-
-from vyper import ast as vy_ast
-from vyper.exceptions import StructureException
-from vyper.typing import InterfaceImports, SourceCode
-
-
-def get_interface_file_path(base_paths: Sequence, import_path: str) -> Path:
- relative_path = Path(import_path)
- for path in base_paths:
- # Find ABI JSON files
- file_path = path.joinpath(relative_path)
- suffix = next((i for i in (".vy", ".json") if file_path.with_suffix(i).exists()), None)
- if suffix:
- return file_path.with_suffix(suffix)
-
- # Find ethPM Manifest files (`from path.to.Manifest import InterfaceName`)
- # NOTE: Use file parent because this assumes that `file_path`
- # coincides with an ABI interface file
- file_path = file_path.parent
- suffix = next((i for i in (".vy", ".json") if file_path.with_suffix(i).exists()), None)
- if suffix:
- return file_path.with_suffix(suffix)
-
- raise FileNotFoundError(f" Cannot locate interface '{import_path}{{.vy,.json}}'")
-
-
-def extract_file_interface_imports(code: SourceCode) -> InterfaceImports:
- ast_tree = vy_ast.parse_to_ast(code)
-
- imports_dict: InterfaceImports = {}
- for node in ast_tree.get_children((vy_ast.Import, vy_ast.ImportFrom)):
- if isinstance(node, vy_ast.Import): # type: ignore
- if not node.alias:
- raise StructureException("Import requires an accompanying `as` statement", node)
- if node.alias in imports_dict:
- raise StructureException(f"Interface with alias {node.alias} already exists", node)
- imports_dict[node.alias] = node.name.replace(".", "/")
- elif isinstance(node, vy_ast.ImportFrom): # type: ignore
- level = node.level # type: ignore
- module = node.module or "" # type: ignore
- if not level and module == "vyper.interfaces":
- # uses a builtin interface, so skip adding to imports
- continue
-
- base_path = ""
- if level > 1:
- base_path = "../" * (level - 1)
- elif level == 1:
- base_path = "./"
- base_path = f"{base_path}{module.replace('.','/')}/"
-
- if node.name in imports_dict and imports_dict[node.name] != f"{base_path}{node.name}":
- raise StructureException(f"Interface with name {node.name} already exists", node)
- imports_dict[node.name] = f"{base_path}{node.name}"
-
- return imports_dict
diff --git a/vyper/cli/vyper_compile.py b/vyper/cli/vyper_compile.py
index bdd01eebbe..d6ba9e180a 100755
--- a/vyper/cli/vyper_compile.py
+++ b/vyper/cli/vyper_compile.py
@@ -3,14 +3,13 @@
import json
import sys
import warnings
-from collections import OrderedDict
from pathlib import Path
-from typing import Dict, Iterable, Iterator, Optional, Set, TypeVar
+from typing import Any, Iterable, Iterator, Optional, Set, TypeVar
import vyper
import vyper.codegen.ir_node as ir_node
from vyper.cli import vyper_json
-from vyper.cli.utils import extract_file_interface_imports, get_interface_file_path
+from vyper.compiler.input_bundle import FileInput, FilesystemInputBundle
from vyper.compiler.settings import (
VYPER_TRACEBACK_LIMIT,
OptimizationLevel,
@@ -18,7 +17,7 @@
_set_debug_mode,
)
from vyper.evm.opcodes import DEFAULT_EVM_VERSION, EVM_VERSIONS
-from vyper.typing import ContractCodes, ContractPath, OutputFormats
+from vyper.typing import ContractPath, OutputFormats
T = TypeVar("T")
@@ -34,7 +33,8 @@
devdoc - Natspec developer documentation
combined_json - All of the above format options combined as single JSON output
layout - Storage layout of a Vyper contract
-ast - AST in JSON format
+ast - AST (not yet annotated) in JSON format
+annotated_ast - Annotated AST in JSON format
interface - Vyper interface of a contract
external_interface - External interface of a contract, used for outside contract calls
opcodes - List of opcodes as a string
@@ -43,7 +43,6 @@
ir_json - Intermediate representation in JSON format
ir_runtime - Intermediate representation of runtime bytecode in list format
asm - Output the EVM assembly of the deployable bytecode
-hex-ir - Output IR and assembly constants in hex instead of decimal
"""
combined_json_outputs = [
@@ -113,6 +112,7 @@ def _parse_args(argv):
)
parser.add_argument("--no-optimize", help="Do not optimize", action="store_true")
parser.add_argument(
+ "-O",
"--optimize",
help="Optimization flag (defaults to 'gas')",
choices=["gas", "codesize", "none"],
@@ -127,6 +127,7 @@ def _parse_args(argv):
type=int,
)
parser.add_argument(
+ "-v",
"--verbose",
help="Turn on compiler verbose output. "
"Currently an alias for --traceback-limit but "
@@ -140,9 +141,15 @@ def _parse_args(argv):
)
parser.add_argument("--hex-ir", action="store_true")
parser.add_argument(
- "-p", help="Set the root path for contract imports", default=".", dest="root_folder"
+ "--path", "-p", help="Set the root path for contract imports", action="append", dest="paths"
)
parser.add_argument("-o", help="Set the output path", dest="output_path")
+ parser.add_argument(
+ "--experimental-codegen",
+ help="The compiler use the new IR codegen. This is an experimental feature.",
+ action="store_true",
+ dest="experimental_codegen",
+ )
args = parser.parse_args(argv)
@@ -179,13 +186,16 @@ def _parse_args(argv):
if args.evm_version:
settings.evm_version = args.evm_version
+ if args.experimental_codegen:
+ settings.experimental_codegen = args.experimental_codegen
+
if args.verbose:
print(f"cli specified: `{settings}`", file=sys.stderr)
compiled = compile_files(
args.input_files,
output_formats,
- args.root_folder,
+ args.paths,
args.show_gas_estimates,
settings,
args.storage_layout,
@@ -219,94 +229,25 @@ def exc_handler(contract_path: ContractPath, exception: Exception) -> None:
raise exception
-def get_interface_codes(root_path: Path, contract_sources: ContractCodes) -> Dict:
- interface_codes: Dict = {}
- interfaces: Dict = {}
-
- for file_path, code in contract_sources.items():
- interfaces[file_path] = {}
- parent_path = root_path.joinpath(file_path).parent
-
- interface_codes = extract_file_interface_imports(code)
- for interface_name, interface_path in interface_codes.items():
- base_paths = [parent_path]
- if not interface_path.startswith(".") and root_path.joinpath(file_path).exists():
- base_paths.append(root_path)
- elif interface_path.startswith("../") and len(Path(file_path).parent.parts) < Path(
- interface_path
- ).parts.count(".."):
- raise FileNotFoundError(
- f"{file_path} - Cannot perform relative import outside of base folder"
- )
-
- valid_path = get_interface_file_path(base_paths, interface_path)
- with valid_path.open() as fh:
- code = fh.read()
- if valid_path.suffix == ".json":
- contents = json.loads(code.encode())
-
- # EthPM Manifest (EIP-2678)
- if "contractTypes" in contents:
- if (
- interface_name not in contents["contractTypes"]
- or "abi" not in contents["contractTypes"][interface_name]
- ):
- raise ValueError(
- f"Could not find interface '{interface_name}'"
- f" in manifest '{valid_path}'."
- )
-
- interfaces[file_path][interface_name] = {
- "type": "json",
- "code": contents["contractTypes"][interface_name]["abi"],
- }
-
- # ABI JSON file (either `List[ABI]` or `{"abi": List[ABI]}`)
- elif isinstance(contents, list) or (
- "abi" in contents and isinstance(contents["abi"], list)
- ):
- interfaces[file_path][interface_name] = {"type": "json", "code": contents}
-
- else:
- raise ValueError(f"Corrupted file: '{valid_path}'")
-
- else:
- interfaces[file_path][interface_name] = {"type": "vyper", "code": code}
-
- return interfaces
-
-
def compile_files(
- input_files: Iterable[str],
+ input_files: list[str],
output_formats: OutputFormats,
- root_folder: str = ".",
+ paths: list[str] = None,
show_gas_estimates: bool = False,
settings: Optional[Settings] = None,
- storage_layout: Optional[Iterable[str]] = None,
+ storage_layout_paths: list[str] = None,
no_bytecode_metadata: bool = False,
-) -> OrderedDict:
- root_path = Path(root_folder).resolve()
- if not root_path.exists():
- raise FileNotFoundError(f"Invalid root path - '{root_path.as_posix()}' does not exist")
+) -> dict:
+ paths = paths or []
- contract_sources: ContractCodes = OrderedDict()
- for file_name in input_files:
- file_path = Path(file_name)
- try:
- file_str = file_path.resolve().relative_to(root_path).as_posix()
- except ValueError:
- file_str = file_path.as_posix()
- with file_path.open() as fh:
- # trailing newline fixes python parsing bug when source ends in a comment
- # https://bugs.python.org/issue35107
- contract_sources[file_str] = fh.read() + "\n"
-
- storage_layouts = OrderedDict()
- if storage_layout:
- for storage_file_name, contract_name in zip(storage_layout, contract_sources.keys()):
- storage_file_path = Path(storage_file_name)
- with storage_file_path.open() as sfh:
- storage_layouts[contract_name] = json.load(sfh)
+ # lowest precedence search path is always `.`
+ search_paths = [Path(".")]
+
+ for p in paths:
+ path = Path(p).resolve(strict=True)
+ search_paths.append(path)
+
+ input_bundle = FilesystemInputBundle(search_paths)
show_version = False
if "combined_json" in output_formats:
@@ -315,23 +256,51 @@ def compile_files(
output_formats = combined_json_outputs
show_version = True
- translate_map = {"abi_python": "abi", "json": "abi", "ast": "ast_dict", "ir_json": "ir_dict"}
+ translate_map = {
+ "abi_python": "abi",
+ "json": "abi",
+ "ast": "ast_dict",
+ "annotated_ast": "annotated_ast_dict",
+ "ir_json": "ir_dict",
+ }
final_formats = [translate_map.get(i, i) for i in output_formats]
- compiler_data = vyper.compile_codes(
- contract_sources,
- final_formats,
- exc_handler=exc_handler,
- interface_codes=get_interface_codes(root_path, contract_sources),
- settings=settings,
- storage_layouts=storage_layouts,
- show_gas_estimates=show_gas_estimates,
- no_bytecode_metadata=no_bytecode_metadata,
- )
+ if storage_layout_paths:
+ if len(storage_layout_paths) != len(input_files):
+ raise ValueError(
+ "provided {len(storage_layout_paths)} storage "
+ "layouts, but {len(input_files)} source files"
+ )
+
+ ret: dict[Any, Any] = {}
if show_version:
- compiler_data["version"] = vyper.__version__
+ ret["version"] = vyper.__version__
- return compiler_data
+ for file_name in input_files:
+ file_path = Path(file_name)
+ file = input_bundle.load_file(file_path)
+ assert isinstance(file, FileInput) # mypy hint
+
+ storage_layout_override = None
+ if storage_layout_paths:
+ storage_file_path = storage_layout_paths.pop(0)
+ with open(storage_file_path) as sfh:
+ storage_layout_override = json.load(sfh)
+
+ output = vyper.compile_from_file_input(
+ file,
+ input_bundle=input_bundle,
+ output_formats=final_formats,
+ exc_handler=exc_handler,
+ settings=settings,
+ storage_layout_override=storage_layout_override,
+ show_gas_estimates=show_gas_estimates,
+ no_bytecode_metadata=no_bytecode_metadata,
+ )
+
+ ret[file_path] = output
+
+ return ret
if __name__ == "__main__":
diff --git a/vyper/cli/vyper_json.py b/vyper/cli/vyper_json.py
index 4a1c91550e..032d7ebe64 100755
--- a/vyper/cli/vyper_json.py
+++ b/vyper/cli/vyper_json.py
@@ -4,16 +4,15 @@
import json
import sys
import warnings
-from pathlib import Path
-from typing import Any, Callable, Dict, Hashable, List, Optional, Tuple, Union
+from pathlib import Path, PurePath
+from typing import Any, Callable, Hashable, Optional
import vyper
-from vyper.cli.utils import extract_file_interface_imports, get_interface_file_path
+from vyper.compiler.input_bundle import FileInput, JSONInputBundle
from vyper.compiler.settings import OptimizationLevel, Settings
from vyper.evm.opcodes import EVM_VERSIONS
from vyper.exceptions import JSONError
-from vyper.typing import ContractCodes, ContractPath
-from vyper.utils import keccak256
+from vyper.utils import OrderedSet, keccak256
TRANSLATE_MAP = {
"abi": "abi",
@@ -29,7 +28,7 @@
"interface": "interface",
"ir": "ir_dict",
"ir_runtime": "ir_runtime_dict",
- # "metadata": "metadata", # don't include in "*" output for now
+ "metadata": "metadata",
"layout": "layout",
"userdoc": "userdoc",
}
@@ -97,15 +96,15 @@ def _parse_args(argv):
print(output_json)
-def exc_handler_raises(file_path: Union[str, None], exception: Exception, component: str) -> None:
+def exc_handler_raises(file_path: Optional[str], exception: Exception, component: str) -> None:
if file_path:
print(f"Unhandled exception in '{file_path}':")
exception._exc_handler = True # type: ignore
raise exception
-def exc_handler_to_dict(file_path: Union[str, None], exception: Exception, component: str) -> Dict:
- err_dict: Dict = {
+def exc_handler_to_dict(file_path: Optional[str], exception: Exception, component: str) -> dict:
+ err_dict: dict = {
"type": type(exception).__name__,
"component": component,
"severity": "error",
@@ -129,23 +128,7 @@ def exc_handler_to_dict(file_path: Union[str, None], exception: Exception, compo
return output_json
-def _standardize_path(path_str: str) -> str:
- try:
- path = Path(path_str)
-
- if path.is_absolute():
- path = path.resolve()
- else:
- pwd = Path(".").resolve()
- path = path.resolve().relative_to(pwd)
-
- except ValueError:
- raise JSONError(f"{path_str} - path exists outside base folder")
-
- return path.as_posix()
-
-
-def get_evm_version(input_dict: Dict) -> Optional[str]:
+def get_evm_version(input_dict: dict) -> Optional[str]:
if "settings" not in input_dict:
return None
@@ -168,86 +151,79 @@ def get_evm_version(input_dict: Dict) -> Optional[str]:
return evm_version
-def get_input_dict_contracts(input_dict: Dict) -> ContractCodes:
- contract_sources: ContractCodes = {}
+def get_inputs(input_dict: dict) -> dict[PurePath, Any]:
+ ret = {}
+ seen = {}
+
for path, value in input_dict["sources"].items():
+ path = PurePath(path)
if "urls" in value:
raise JSONError(f"{path} - 'urls' is not a supported field, use 'content' instead")
if "content" not in value:
raise JSONError(f"{path} missing required field - 'content'")
if "keccak256" in value:
- hash_ = value["keccak256"].lower()
- if hash_.startswith("0x"):
- hash_ = hash_[2:]
+ hash_ = value["keccak256"].lower().removeprefix("0x")
if hash_ != keccak256(value["content"].encode("utf-8")).hex():
raise JSONError(
f"Calculated keccak of '{path}' does not match keccak given in input JSON"
)
- key = _standardize_path(path)
- if key in contract_sources:
- raise JSONError(f"Contract namespace collision: {key}")
- contract_sources[key] = value["content"]
- return contract_sources
+ if path.stem in seen:
+ raise JSONError(f"Contract namespace collision: {path}")
-
-def get_input_dict_interfaces(input_dict: Dict) -> Dict:
- interface_sources: Dict = {}
+ # value looks like {"content":