├── .gitignore ├── .pre-commit-config.yaml ├── .readthedocs.yaml ├── Dockerfile ├── FUNDING.yml ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── docs ├── Makefile ├── _static │ ├── css │ │ ├── dark.css │ │ └── toggle.css │ └── js │ │ └── toggle.js ├── _templates │ └── versions.html ├── built-in-functions.rst ├── compiler-exceptions.rst ├── compiling-a-contract.rst ├── conf.py ├── constants-and-vars.rst ├── contributing.rst ├── control-structures.rst ├── deploying-contracts.rst ├── event-logging.rst ├── index.rst ├── installing-vyper.rst ├── interfaces.rst ├── make.bat ├── natspec.rst ├── release-notes.rst ├── resources.rst ├── scoping-and-declarations.rst ├── statements.rst ├── structure-of-a-contract.rst ├── style-guide.rst ├── testing-contracts-brownie.rst ├── testing-contracts-ethtester.rst ├── testing-contracts.rst ├── toctree.rst ├── types.rst ├── versioning.rst ├── vyper-by-example.rst └── vyper-logo-transparent.svg ├── examples ├── auctions │ ├── blind_auction.vy │ └── simple_open_auction.vy ├── crowdfund.vy ├── factory │ ├── Exchange.vy │ └── Factory.vy ├── market_maker │ └── on_chain_market_maker.vy ├── name_registry │ └── name_registry.vy ├── safe_remote_purchase │ └── safe_remote_purchase.vy ├── stock │ └── company.vy ├── storage │ ├── advanced_storage.vy │ └── storage.vy ├── tokens │ ├── ERC1155ownable.vy │ ├── ERC20.vy │ ├── ERC4626.vy │ └── ERC721.vy ├── voting │ └── ballot.vy └── wallet │ └── wallet.vy ├── hooks └── build ├── images ├── curve.png ├── cyfrin.png ├── lido.png ├── unore.png └── yearn-logo.png ├── logo ├── vyper-logo-flat.png ├── vyper-logo-flat.svg ├── vyper-logo-transparent.png ├── vyper-logo-transparent.svg └── vyper-logo.ai ├── make.cmd ├── pyproject.toml ├── quicktest.sh ├── requirements-docs.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── ast │ ├── nodes │ │ ├── test_binary.py │ │ ├── test_compare_nodes.py │ │ ├── test_evaluate_binop_decimal.py │ │ ├── test_evaluate_binop_int.py │ │ ├── test_evaluate_boolop.py │ │ ├── test_evaluate_compare.py │ │ ├── test_evaluate_subscript.py │ │ ├── test_evaluate_unaryop.py │ │ ├── test_from_node.py │ │ ├── test_get_children.py │ │ ├── test_get_descendants.py │ │ ├── test_hex.py │ │ └── test_replace_in_tree.py │ ├── test_folding.py │ ├── test_metadata_journal.py │ ├── test_natspec.py │ └── test_pre_parser.py ├── base_conftest.py ├── builtins │ └── folding │ │ ├── test_abs.py │ │ ├── test_addmod_mulmod.py │ │ ├── test_bitwise.py │ │ ├── test_epsilon.py │ │ ├── test_floor_ceil.py │ │ ├── test_fold_as_wei_value.py │ │ ├── test_keccak_sha.py │ │ ├── test_len.py │ │ ├── test_min_max.py │ │ └── test_powmod.py ├── cli │ ├── outputs │ │ ├── test_storage_layout.py │ │ └── test_storage_layout_overrides.py │ ├── vyper_compile │ │ ├── test_compile_files.py │ │ ├── test_import_paths.py │ │ └── test_parse_args.py │ └── vyper_json │ │ ├── test_compile_from_input_dict.py │ │ ├── test_compile_json.py │ │ ├── test_get_contracts.py │ │ ├── test_get_settings.py │ │ ├── test_interfaces.py │ │ ├── test_output_dict.py │ │ ├── test_output_selection.py │ │ └── test_parse_args_vyperjson.py ├── compiler │ ├── __init__.py │ ├── asm │ │ └── test_asm_optimizer.py │ ├── ir │ │ ├── __init__.py │ │ ├── test_compile_ir.py │ │ ├── test_optimize_ir.py │ │ ├── test_repeat.py │ │ └── test_with.py │ ├── test_bytecode_runtime.py │ ├── test_calldatacopy.py │ ├── test_compile_code.py │ ├── test_default_settings.py │ ├── test_opcodes.py │ ├── test_pre_parser.py │ ├── test_sha3_32.py │ └── test_source_map.py ├── conftest.py ├── examples │ ├── auctions │ │ ├── test_blind_auction.py │ │ └── test_simple_open_auction.py │ ├── company │ │ └── test_company.py │ ├── conftest.py │ ├── crowdfund │ │ └── test_crowdfund_example.py │ ├── factory │ │ └── test_factory.py │ ├── market_maker │ │ └── test_on_chain_market_maker.py │ ├── name_registry │ │ └── test_name_registry.py │ ├── safe_remote_purchase │ │ └── test_safe_remote_purchase.py │ ├── storage │ │ ├── test_advanced_storage.py │ │ └── test_storage.py │ ├── tokens │ │ ├── test_erc1155.py │ │ ├── test_erc20.py │ │ ├── test_erc4626.py │ │ └── test_erc721.py │ ├── voting │ │ └── test_ballot.py │ └── wallet │ │ └── test_wallet.py ├── fixtures │ ├── __init__.py │ └── memorymock.py ├── functional │ ├── codegen │ │ ├── test_struct_return.py │ │ └── test_tuple_return.py │ ├── semantics │ │ ├── analysis │ │ │ ├── test_array_index.py │ │ │ ├── test_cyclic_function_calls.py │ │ │ ├── test_for_loop.py │ │ │ └── test_potential_types.py │ │ ├── conftest.py │ │ ├── test_namespace.py │ │ └── types │ │ │ ├── test_event.py │ │ │ ├── test_pure_types.py │ │ │ ├── test_size_in_bytes.py │ │ │ ├── test_type_from_abi.py │ │ │ └── test_type_from_annotation.py │ └── test_storage_slots.py ├── fuzzing │ └── test_exponents.py ├── grammar │ └── test_grammar.py ├── parser │ ├── ast_utils │ │ ├── test_ast.py │ │ └── test_ast_dict.py │ ├── exceptions │ │ ├── __init__.py │ │ ├── test_argument_exception.py │ │ ├── test_call_violation.py │ │ ├── test_constancy_exception.py │ │ ├── test_function_declaration_exception.py │ │ ├── test_instantiation_exception.py │ │ ├── test_invalid_literal_exception.py │ │ ├── test_invalid_payable.py │ │ ├── test_invalid_reference.py │ │ ├── test_invalid_type_exception.py │ │ ├── test_namespace_collision.py │ │ ├── test_overflow_exception.py │ │ ├── test_structure_exception.py │ │ ├── test_syntax_exception.py │ │ ├── test_type_mismatch_exception.py │ │ ├── test_undeclared_definition.py │ │ ├── test_variable_declaration_exception.py │ │ └── test_vyper_exception_pos.py │ ├── features │ │ ├── arithmetic │ │ │ ├── test_division.py │ │ │ └── test_modulo.py │ │ ├── decorators │ │ │ ├── test_nonreentrant.py │ │ │ ├── test_payable.py │ │ │ ├── test_private.py │ │ │ ├── test_public.py │ │ │ ├── test_pure.py │ │ │ └── test_view.py │ │ ├── external_contracts │ │ │ ├── test_erc20_abi.py │ │ │ ├── test_external_contract_calls.py │ │ │ ├── test_modifiable_external_contract_calls.py │ │ │ └── test_self_call_struct.py │ │ ├── iteration │ │ │ ├── test_break.py │ │ │ ├── test_continue.py │ │ │ ├── test_for_in_list.py │ │ │ ├── test_for_range.py │ │ │ └── test_range_in.py │ │ ├── test_address_balance.py │ │ ├── test_assert.py │ │ ├── test_assert_unreachable.py │ │ ├── test_assignment.py │ │ ├── test_bytes_map_keys.py │ │ ├── test_clampers.py │ │ ├── test_comments.py │ │ ├── test_comparison.py │ │ ├── test_conditionals.py │ │ ├── test_constructor.py │ │ ├── test_gas.py │ │ ├── test_immutable.py │ │ ├── test_init.py │ │ ├── test_internal_call.py │ │ ├── test_logging.py │ │ ├── test_logging_bytes_extended.py │ │ ├── test_logging_from_call.py │ │ ├── test_memory_dealloc.py │ │ ├── test_packing.py │ │ ├── test_reverting.py │ │ ├── test_short_circuiting.py │ │ ├── test_string_map_keys.py │ │ ├── test_ternary.py │ │ └── test_transient.py │ ├── functions │ │ ├── __init__.py │ │ ├── test_abi.py │ │ ├── test_abi_decode.py │ │ ├── test_abi_encode.py │ │ ├── test_addmod.py │ │ ├── test_as_wei_value.py │ │ ├── test_bitwise.py │ │ ├── test_block.py │ │ ├── test_block_number.py │ │ ├── test_ceil.py │ │ ├── test_concat.py │ │ ├── test_convert.py │ │ ├── test_create_functions.py │ │ ├── test_default_function.py │ │ ├── test_default_parameters.py │ │ ├── test_ec.py │ │ ├── test_ecrecover.py │ │ ├── test_empty.py │ │ ├── test_extract32.py │ │ ├── test_floor.py │ │ ├── test_interfaces.py │ │ ├── test_is_contract.py │ │ ├── test_keccak256.py │ │ ├── test_length.py │ │ ├── test_method_id.py │ │ ├── test_minmax.py │ │ ├── test_minmax_value.py │ │ ├── test_mkstr.py │ │ ├── test_mulmod.py │ │ ├── test_raw_call.py │ │ ├── test_return.py │ │ ├── test_return_struct.py │ │ ├── test_return_tuple.py │ │ ├── test_send.py │ │ ├── test_sha256.py │ │ ├── test_slice.py │ │ ├── test_tx.py │ │ ├── test_unary.py │ │ └── test_unsafe_math.py │ ├── globals │ │ ├── test_getters.py │ │ ├── test_globals.py │ │ └── test_setters.py │ ├── integration │ │ ├── test_basics.py │ │ ├── test_crowdfund.py │ │ └── test_escrow.py │ ├── parser_utils │ │ └── test_annotate_and_optimize_ast.py │ ├── syntax │ │ ├── __init__.py │ │ ├── test_abi_encode.py │ │ ├── test_addmulmod.py │ │ ├── test_address_code.py │ │ ├── test_ann_assign.py │ │ ├── test_as_uint256.py │ │ ├── test_as_wei_value.py │ │ ├── test_block.py │ │ ├── test_blockscope.py │ │ ├── test_bool.py │ │ ├── test_bool_ops.py │ │ ├── test_bytes.py │ │ ├── test_chainid.py │ │ ├── test_code_size.py │ │ ├── test_codehash.py │ │ ├── test_concat.py │ │ ├── test_conditionals.py │ │ ├── test_constants.py │ │ ├── test_create_with_code_of.py │ │ ├── test_dynamic_array.py │ │ ├── test_enum.py │ │ ├── test_extract32.py │ │ ├── test_for_range.py │ │ ├── test_functions_call.py │ │ ├── test_immutables.py │ │ ├── test_interfaces.py │ │ ├── test_invalids.py │ │ ├── test_keccak256.py │ │ ├── test_len.py │ │ ├── test_list.py │ │ ├── test_logging.py │ │ ├── test_minmax.py │ │ ├── test_minmax_value.py │ │ ├── test_msg_data.py │ │ ├── test_nested_list.py │ │ ├── test_no_none.py │ │ ├── test_print.py │ │ ├── test_public.py │ │ ├── test_raw_call.py │ │ ├── test_return_tuple.py │ │ ├── test_self_balance.py │ │ ├── test_selfdestruct.py │ │ ├── test_send.py │ │ ├── test_slice.py │ │ ├── test_string.py │ │ ├── test_structs.py │ │ ├── test_ternary.py │ │ ├── test_tuple_assign.py │ │ ├── test_unbalanced_return.py │ │ └── utils │ │ │ ├── test_event_names.py │ │ │ ├── test_function_names.py │ │ │ └── test_variable_names.py │ ├── test_call_graph_stability.py │ ├── test_selector_table.py │ └── types │ │ ├── numbers │ │ ├── test_constants.py │ │ ├── test_decimals.py │ │ ├── test_isqrt.py │ │ ├── test_signed_ints.py │ │ ├── test_sqrt.py │ │ └── test_unsigned_ints.py │ │ ├── test_bytes.py │ │ ├── test_bytes_literal.py │ │ ├── test_bytes_zero_padding.py │ │ ├── test_dynamic_array.py │ │ ├── test_enum.py │ │ ├── test_identifier_naming.py │ │ ├── test_lists.py │ │ ├── test_node_types.py │ │ ├── test_string.py │ │ ├── test_string_literal.py │ │ └── value │ │ └── test_as_wei_value.py ├── signatures │ ├── test_invalid_function_decorators.py │ └── test_method_id_conflicts.py └── test_utils.py ├── tox.ini └── vyper ├── __init__.py ├── __main__.py ├── abi_types.py ├── ast ├── README.md ├── __init__.py ├── __init__.pyi ├── annotation.py ├── expansion.py ├── folding.py ├── grammar.lark ├── grammar.py ├── metadata.py ├── natspec.py ├── nodes.py ├── nodes.pyi ├── pre_parser.py ├── utils.py └── validation.py ├── builtins ├── __init__.py ├── _convert.py ├── _signatures.py ├── _utils.py ├── functions.py └── interfaces │ ├── ERC165.py │ ├── ERC20.py │ ├── ERC20Detailed.py │ ├── ERC4626.py │ ├── ERC721.py │ └── __init__.py ├── cli ├── __init__.py ├── utils.py ├── vyper_compile.py ├── vyper_ir.py ├── vyper_json.py └── vyper_serve.py ├── codegen ├── __init__.py ├── abi_encoder.py ├── arithmetic.py ├── context.py ├── core.py ├── events.py ├── expr.py ├── external_call.py ├── function_definitions │ ├── __init__.py │ ├── common.py │ ├── external_function.py │ ├── internal_function.py │ └── utils.py ├── global_context.py ├── ir_node.py ├── jumptable_utils.py ├── keccak256_helper.py ├── memory_allocator.py ├── module.py ├── return_.py ├── self_call.py └── stmt.py ├── compiler ├── README.md ├── __init__.py ├── output.py ├── phases.py ├── settings.py └── utils.py ├── evm ├── __init__.py ├── address_space.py └── opcodes.py ├── exceptions.py ├── ir ├── README.md ├── __init__.py ├── compile_ir.py ├── optimizer.py └── s_expressions.py ├── semantics ├── README.md ├── __init__.py ├── analysis │ ├── __init__.py │ ├── annotation.py │ ├── base.py │ ├── common.py │ ├── data_positions.py │ ├── levenshtein_utils.py │ ├── local.py │ ├── module.py │ └── utils.py ├── data_locations.py ├── environment.py ├── namespace.py └── types │ ├── __init__.py │ ├── base.py │ ├── bytestrings.py │ ├── function.py │ ├── primitives.py │ ├── shortcuts.py │ ├── subscriptable.py │ ├── user.py │ └── utils.py ├── typing.py ├── utils.py └── warnings.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # egg-related 7 | build/ 8 | .eggs/ 9 | dist/ 10 | vyper.egg-info/ 11 | 12 | .pytest_cache/ 13 | 14 | # pyenv 15 | .python-version 16 | 17 | # dotenv 18 | .env 19 | 20 | # virtualenv 21 | .venv/ 22 | venv/ 23 | ENV/ 24 | 25 | # Coverage tests 26 | .coverage* 27 | .cache/ 28 | htmlcov/ 29 | coverage.xml 30 | 31 | # sphinx 32 | docs/_build/ 33 | docs/modules.rst 34 | docs/vyper.rst 35 | docs/vyper.*.rst 36 | 37 | # IDEs 38 | .idea/ 39 | .vscode/ 40 | 41 | .tox/ 42 | .mypy_cache/ 43 | .hypothesis/ 44 | 45 | # vyper 46 | vyper/version.py 47 | vyper/vyper_git_version.txt 48 | vyper/vyper_git_commithash.txt 49 | *.spec 50 | 51 | # mac 52 | .DS_Store -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pycqa/isort 3 | rev: 5.9.3 4 | hooks: 5 | - id: isort 6 | name: isort 7 | 8 | - repo: https://github.com/psf/black 9 | rev: 21.9b0 10 | hooks: 11 | - id: black 12 | name: black 13 | 14 | - repo: https://github.com/PyCQA/flake8 15 | rev: 3.9.2 16 | hooks: 17 | - id: flake8 18 | 19 | - repo: https://github.com/pre-commit/mirrors-mypy 20 | rev: v0.910 21 | hooks: 22 | - id: mypy 23 | additional_dependencies: 24 | - "types-setuptools" 25 | 26 | default_language_version: 27 | python: python3.10 28 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # File: .readthedocs.yaml 2 | 3 | version: 2 4 | 5 | # Set the version of Python and other tools you might need 6 | build: 7 | # TODO: update to `latest` once supported 8 | # https://github.com/readthedocs/readthedocs.org/issues/8861 9 | os: ubuntu-22.04 10 | tools: 11 | python: "3.10" 12 | 13 | # Build from the docs/ directory with Sphinx 14 | sphinx: 15 | configuration: docs/conf.py 16 | 17 | formats: all 18 | 19 | 20 | # Optionally declare the Python requirements required to build your docs 21 | python: 22 | install: 23 | - requirements: requirements-docs.txt 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim 2 | 3 | # Specify label-schema specific arguments and labels. 4 | ARG BUILD_DATE 5 | ARG VCS_REF 6 | LABEL org.label-schema.build-date=$BUILD_DATE \ 7 | org.label-schema.name="Vyper" \ 8 | org.label-schema.description="Vyper is an experimental programming language" \ 9 | org.label-schema.url="https://vyper.readthedocs.io/en/latest/" \ 10 | org.label-schema.vcs-ref=$VCS_REF \ 11 | org.label-schema.vcs-url="https://github.com/vyperlang/vyper" \ 12 | org.label-schema.vendor="Vyper Team" \ 13 | org.label-schema.schema-version="1.0" 14 | 15 | # coincurve requires libgmp 16 | RUN apt-get update && \ 17 | apt-get install -y --no-install-recommends \ 18 | apt-utils \ 19 | gcc \ 20 | git \ 21 | libc6-dev \ 22 | libc-dev \ 23 | libssl-dev \ 24 | libgmp-dev \ 25 | && rm -rf /var/lib/apt/lists/* 26 | 27 | ADD . /code 28 | 29 | WORKDIR /code 30 | 31 | # force repository to be clean so the version string is right 32 | RUN git reset --hard 33 | 34 | # Using "test" optional to include test dependencies in built docker-image 35 | RUN pip install --no-cache-dir .[test] && \ 36 | apt-get purge -y --auto-remove apt-utils gcc libc6-dev libc-dev libssl-dev 37 | 38 | ENTRYPOINT ["/usr/local/bin/vyper"] 39 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://gitcoin.co/grants/200/vyper-smart-contract-language-2 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | ifeq (, $(shell which pip3)) 4 | pip := $(shell which pip3) 5 | else 6 | pip := $(shell which pip) 7 | endif 8 | 9 | .PHONY: test dev-deps lint clean clean-pyc clean-build clean-test docs 10 | 11 | init: 12 | python setup.py install 13 | 14 | dev-init: 15 | ${pip} install .[dev] 16 | 17 | test: 18 | pytest 19 | 20 | mypy: 21 | tox -e mypy 22 | 23 | lint: 24 | tox -e lint 25 | 26 | docs: 27 | rm -f docs/vyper.rst 28 | rm -f docs/modules.rst 29 | sphinx-apidoc -o docs/ -d 2 vyper/ 30 | $(MAKE) -C docs clean 31 | $(MAKE) -C docs html 32 | # To display docs, run: 33 | # open docs/_build/html/index.html (macOS) 34 | # start docs/_build/html/index.html (Windows) 35 | # xdg-open docs/_build/html/index.html (Linux) 36 | 37 | release: clean 38 | python setup.py sdist bdist_wheel 39 | twine check dist/* 40 | #twine upload dist/* 41 | 42 | freeze: clean init 43 | echo Generating binary... 44 | export OS="$$(uname -s | tr A-Z a-z)" && \ 45 | export VERSION="$$(PYTHONPATH=. python vyper/cli/vyper_compile.py --version)" && \ 46 | pyinstaller --target-architecture=universal2 --clean --onefile vyper/cli/vyper_compile.py --name "vyper.$${VERSION}.$${OS}" --add-data vyper:vyper 47 | 48 | clean: clean-build clean-docs clean-pyc clean-test 49 | 50 | clean-build: 51 | @echo Cleaning python build files... 52 | @rm -fr build/ 53 | @rm -fr _build/ # docs build dir 54 | @rm -fr dist/ 55 | @rm -fr *.egg-info 56 | @rm -f vyper/version.py vyper/vyper_git_version.txt vyper/vyper_git_commithash.txt 57 | @rm -f *.spec 58 | 59 | clean-docs: 60 | @echo Cleaning doc build files... 61 | @rm -rf docs/_build/ 62 | @rm -f docs/modules.rst 63 | @rm -f docs/vyper.rst 64 | @rm -f docs/vyper.*.rst 65 | 66 | clean-pyc: 67 | @echo Cleaning python files... 68 | @find . -name '*.pyc' -exec rm -f {} + 69 | @find . -name '*.pyo' -exec rm -f {} + 70 | @find . -name '*~' -exec rm -f {} + 71 | @find . -name '__pycache__' -exec rmdir {} + 72 | 73 | clean-test: 74 | @echo Cleaning test files... 75 | @find . -name 'htmlcov' -exec rm -rf {} + 76 | @rm -fr coverage.xml 77 | @rm -fr .coverage 78 | @rm -fr .eggs/ 79 | @rm -fr .hypothesis/ 80 | @rm -fr .pytest_cache/ 81 | @rm -rf .tox/ 82 | @rm -fr .mypy_cache/ 83 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = Vyper 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_static/css/toggle.css: -------------------------------------------------------------------------------- 1 | input[type=checkbox] { 2 | visibility: hidden; 3 | height: 0; 4 | width: 0; 5 | margin: 0; 6 | } 7 | 8 | .rst-versions .rst-current-version { 9 | padding: 10px; 10 | display: flex; 11 | justify-content: space-between; 12 | } 13 | 14 | .rst-versions .rst-current-version .fa-book, 15 | .rst-versions .rst-current-version .fa-v, 16 | .rst-versions .rst-current-version .fa-caret-down { 17 | height: 24px; 18 | line-height: 24px; 19 | vertical-align: middle; 20 | } 21 | 22 | .rst-versions .rst-current-version .fa-element { 23 | width: 80px; 24 | text-align: center; 25 | } 26 | 27 | .rst-versions .rst-current-version .fa-book { 28 | text-align: left; 29 | } 30 | 31 | .rst-versions .rst-current-version .fa-v { 32 | color: #27AE60; 33 | text-align: right; 34 | } 35 | 36 | label { 37 | margin: 0 auto; 38 | display: inline-block; 39 | justify-content: center; 40 | align-items: right; 41 | border-radius: 100px; 42 | position: relative; 43 | cursor: pointer; 44 | text-indent: -9999px; 45 | width: 50px; 46 | height: 21px; 47 | background: #000; 48 | } 49 | 50 | label:after { 51 | border-radius: 50%; 52 | position: absolute; 53 | content: ''; 54 | background: #fff; 55 | width: 15px; 56 | height: 15px; 57 | top: 3px; 58 | left: 3px; 59 | transition: ease-in-out 200ms; 60 | } 61 | 62 | input:checked+label { 63 | background: #3a7ca8; 64 | } 65 | 66 | input:checked+label:after { 67 | left: calc(100% - 5px); 68 | transform: translateX(-100%); 69 | } 70 | 71 | html.transition, 72 | html.transition *, 73 | html.transition *:before, 74 | html.transition *:after { 75 | transition: ease-in-out 200ms !important; 76 | transition-delay: 0 !important; 77 | } -------------------------------------------------------------------------------- /docs/_static/js/toggle.js: -------------------------------------------------------------------------------- 1 | document.addEventListener('DOMContentLoaded', function() { 2 | 3 | var checkbox = document.querySelector('input[name=mode]'); 4 | 5 | function toggleCssMode(isDay) { 6 | var mode = (isDay ? "Day" : "Night"); 7 | localStorage.setItem("css-mode", mode); 8 | 9 | var darksheet = $('link[href="_static/css/dark.css"]')[0].sheet; 10 | darksheet.disabled = isDay; 11 | } 12 | 13 | if (localStorage.getItem("css-mode") == "Day") { 14 | toggleCssMode(true); 15 | checkbox.setAttribute('checked', true); 16 | } 17 | 18 | checkbox.addEventListener('change', function() { 19 | document.documentElement.classList.add('transition'); 20 | window.setTimeout(() => { 21 | document.documentElement.classList.remove('transition'); 22 | }, 1000) 23 | toggleCssMode(this.checked); 24 | }) 25 | 26 | }); -------------------------------------------------------------------------------- /docs/_templates/versions.html: -------------------------------------------------------------------------------- 1 | {# Add rst-badge after rst-versions for small badge style. #} 2 |
3 | 4 | RTD 5 | 6 | 7 | 8 | 9 | 10 | 11 | v: {{ current_version }} 12 | 13 | 14 |
15 |
16 |
{{ _('Versions') }}
{% for slug, url in versions %} 17 |
{{ slug }}
18 | {% endfor %} 19 |
20 |
21 |
{{ _('Downloads') }}
{% for type, url in downloads %} 22 |
{{ type }}
23 | {% endfor %} 24 |
25 |
26 | {# Translators: The phrase "Read the Docs" is not translated #} 27 |
{{ _('On Read the Docs') }}
28 |
29 | {{ _('Project Home') }} 30 |
31 |
32 | {{ _('Builds') }} 33 |
34 |
35 |
36 |
-------------------------------------------------------------------------------- /docs/deploying-contracts.rst: -------------------------------------------------------------------------------- 1 | .. index:: deploying;deploying; 2 | 3 | .. _deploying: 4 | 5 | Deploying a Contract 6 | ******************** 7 | 8 | Once you are ready to deploy your contract to a public test net or the main net, you have several options: 9 | 10 | * Take the bytecode generated by the vyper compiler and manually deploy it through mist or geth: 11 | 12 | .. code-block:: bash 13 | 14 | vyper yourFileName.vy 15 | # returns bytecode 16 | 17 | * Take the byte code and ABI and deploy it with your current browser on `myetherwallet's `_ contract menu: 18 | 19 | .. code-block:: bash 20 | 21 | vyper -f abi yourFileName.vy 22 | # returns ABI 23 | 24 | * Use the remote compiler provided by the `Remix IDE `_ to compile and deploy your contract on your net of choice. Remix also provides a JavaScript VM to test deploy your contract. 25 | 26 | .. note:: 27 | While the vyper version of the Remix IDE compiler is updated on a regular basis it might be a bit behind the latest version found in the master branch of the repository. Make sure the byte code matches the output from your local compiler. 28 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=Vyper 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /docs/testing-contracts.rst: -------------------------------------------------------------------------------- 1 | .. _testing-contracts: 2 | 3 | Testing a Contract 4 | ################## 5 | 6 | For testing Vyper contracts we recommend the use of `pytest `_ along with one of the following packages: 7 | 8 | * `Brownie `_: A development and testing framework for smart contracts targeting the Ethereum Virtual Machine 9 | * `Ethereum Tester `_: A tool suite for testing ethereum applications 10 | 11 | Example usage for each package is provided in the sections listed below. 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | testing-contracts-brownie.rst 17 | testing-contracts-ethtester.rst 18 | -------------------------------------------------------------------------------- /docs/toctree.rst: -------------------------------------------------------------------------------- 1 | ===== 2 | Vyper 3 | ===== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | Overview 9 | 10 | .. toctree:: 11 | :caption: Getting Started 12 | :maxdepth: 2 13 | 14 | installing-vyper.rst 15 | vyper-by-example.rst 16 | 17 | .. toctree:: 18 | :caption: Language Description 19 | :maxdepth: 2 20 | 21 | structure-of-a-contract.rst 22 | types.rst 23 | constants-and-vars.rst 24 | statements.rst 25 | control-structures.rst 26 | scoping-and-declarations.rst 27 | built-in-functions.rst 28 | interfaces.rst 29 | event-logging.rst 30 | natspec.rst 31 | 32 | .. toctree:: 33 | :caption: Using the Compiler 34 | :maxdepth: 2 35 | 36 | compiling-a-contract.rst 37 | compiler-exceptions.rst 38 | deploying-contracts.rst 39 | testing-contracts.rst 40 | 41 | .. toctree:: 42 | :caption: Additional Resources 43 | :maxdepth: 2 44 | 45 | resources 46 | release-notes.rst 47 | contributing.rst 48 | style-guide.rst 49 | versioning.rst 50 | -------------------------------------------------------------------------------- /docs/vyper-logo-transparent.svg: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /examples/crowdfund.vy: -------------------------------------------------------------------------------- 1 | # Setup private variables (only callable from within the contract) 2 | 3 | funders: HashMap[address, uint256] 4 | beneficiary: address 5 | deadline: public(uint256) 6 | goal: public(uint256) 7 | timelimit: public(uint256) 8 | 9 | # Setup global variables 10 | @external 11 | def __init__(_beneficiary: address, _goal: uint256, _timelimit: uint256): 12 | self.beneficiary = _beneficiary 13 | self.deadline = block.timestamp + _timelimit 14 | self.timelimit = _timelimit 15 | self.goal = _goal 16 | 17 | # Participate in this crowdfunding campaign 18 | @external 19 | @payable 20 | def participate(): 21 | assert block.timestamp < self.deadline, "deadline not met (yet)" 22 | 23 | self.funders[msg.sender] += msg.value 24 | 25 | # Enough money was raised! Send funds to the beneficiary 26 | @external 27 | def finalize(): 28 | assert block.timestamp >= self.deadline, "deadline has passed" 29 | assert self.balance >= self.goal, "the goal has not been reached" 30 | 31 | selfdestruct(self.beneficiary) 32 | 33 | # Let participants withdraw their fund 34 | @external 35 | def refund(): 36 | assert block.timestamp >= self.deadline and self.balance < self.goal 37 | assert self.funders[msg.sender] > 0 38 | 39 | value: uint256 = self.funders[msg.sender] 40 | self.funders[msg.sender] = 0 41 | 42 | send(msg.sender, value) 43 | -------------------------------------------------------------------------------- /examples/factory/Exchange.vy: -------------------------------------------------------------------------------- 1 | from vyper.interfaces import ERC20 2 | 3 | 4 | interface Factory: 5 | def register(): nonpayable 6 | 7 | 8 | token: public(ERC20) 9 | factory: Factory 10 | 11 | 12 | @external 13 | def __init__(_token: ERC20, _factory: Factory): 14 | self.token = _token 15 | self.factory = _factory 16 | 17 | 18 | @external 19 | def initialize(): 20 | # Anyone can safely call this function because of EXTCODEHASH 21 | self.factory.register() 22 | 23 | 24 | # NOTE: This contract restricts trading to only be done by the factory. 25 | # A practical implementation would probably want counter-pairs 26 | # and liquidity management features for each exchange pool. 27 | 28 | 29 | @external 30 | def receive(_from: address, _amt: uint256): 31 | assert msg.sender == self.factory.address 32 | success: bool = self.token.transferFrom(_from, self, _amt) 33 | assert success 34 | 35 | 36 | @external 37 | def transfer(_to: address, _amt: uint256): 38 | assert msg.sender == self.factory.address 39 | success: bool = self.token.transfer(_to, _amt) 40 | assert success 41 | -------------------------------------------------------------------------------- /examples/factory/Factory.vy: -------------------------------------------------------------------------------- 1 | from vyper.interfaces import ERC20 2 | 3 | interface Exchange: 4 | def token() -> ERC20: view 5 | def receive(_from: address, _amt: uint256): nonpayable 6 | def transfer(_to: address, _amt: uint256): nonpayable 7 | 8 | 9 | exchange_codehash: public(bytes32) 10 | # Maps token addresses to exchange addresses 11 | exchanges: public(HashMap[ERC20, Exchange]) 12 | 13 | 14 | @external 15 | def __init__(_exchange_codehash: bytes32): 16 | # Register the exchange code hash during deployment of the factory 17 | self.exchange_codehash = _exchange_codehash 18 | 19 | 20 | # NOTE: Could implement fancier upgrade logic around self.exchange_codehash 21 | # For example, allowing the deployer of this contract to change this 22 | # value allows them to use a new contract if the old one has an issue. 23 | # This would trigger a cascade effect across all exchanges that would 24 | # need to be handled appropiately. 25 | 26 | 27 | @external 28 | def register(): 29 | # Verify code hash is the exchange's code hash 30 | assert msg.sender.codehash == self.exchange_codehash 31 | # Save a lookup for the exchange 32 | # NOTE: Use exchange's token address because it should be globally unique 33 | # NOTE: Should do checks that it hasn't already been set, 34 | # which has to be rectified with any upgrade strategy. 35 | exchange: Exchange = Exchange(msg.sender) 36 | self.exchanges[exchange.token()] = exchange 37 | 38 | 39 | @external 40 | def trade(_token1: ERC20, _token2: ERC20, _amt: uint256): 41 | # Perform a straight exchange of token1 to token 2 (1:1 price) 42 | # NOTE: Any practical implementation would need to solve the price oracle problem 43 | self.exchanges[_token1].receive(msg.sender, _amt) 44 | self.exchanges[_token2].transfer(msg.sender, _amt) 45 | -------------------------------------------------------------------------------- /examples/market_maker/on_chain_market_maker.vy: -------------------------------------------------------------------------------- 1 | from vyper.interfaces import ERC20 2 | 3 | 4 | totalEthQty: public(uint256) 5 | totalTokenQty: public(uint256) 6 | # Constant set in `initiate` that's used to calculate 7 | # the amount of ether/tokens that are exchanged 8 | invariant: public(uint256) 9 | token_address: ERC20 10 | owner: public(address) 11 | 12 | # Sets the on chain market maker with its owner, intial token quantity, 13 | # and initial ether quantity 14 | @external 15 | @payable 16 | def initiate(token_addr: address, token_quantity: uint256): 17 | assert self.invariant == 0 18 | self.token_address = ERC20(token_addr) 19 | self.token_address.transferFrom(msg.sender, self, token_quantity) 20 | self.owner = msg.sender 21 | self.totalEthQty = msg.value 22 | self.totalTokenQty = token_quantity 23 | self.invariant = msg.value * token_quantity 24 | assert self.invariant > 0 25 | 26 | # Sells ether to the contract in exchange for tokens (minus a fee) 27 | @external 28 | @payable 29 | def ethToTokens(): 30 | fee: uint256 = msg.value / 500 31 | eth_in_purchase: uint256 = msg.value - fee 32 | new_total_eth: uint256 = self.totalEthQty + eth_in_purchase 33 | new_total_tokens: uint256 = self.invariant / new_total_eth 34 | self.token_address.transfer(msg.sender, self.totalTokenQty - new_total_tokens) 35 | self.totalEthQty = new_total_eth 36 | self.totalTokenQty = new_total_tokens 37 | 38 | # Sells tokens to the contract in exchange for ether 39 | @external 40 | def tokensToEth(sell_quantity: uint256): 41 | self.token_address.transferFrom(msg.sender, self, sell_quantity) 42 | new_total_tokens: uint256 = self.totalTokenQty + sell_quantity 43 | new_total_eth: uint256 = self.invariant / new_total_tokens 44 | eth_to_send: uint256 = self.totalEthQty - new_total_eth 45 | send(msg.sender, eth_to_send) 46 | self.totalEthQty = new_total_eth 47 | self.totalTokenQty = new_total_tokens 48 | 49 | # Owner can withdraw their funds and destroy the market maker 50 | @external 51 | def ownerWithdraw(): 52 | assert self.owner == msg.sender 53 | self.token_address.transfer(self.owner, self.totalTokenQty) 54 | selfdestruct(self.owner) 55 | -------------------------------------------------------------------------------- /examples/name_registry/name_registry.vy: -------------------------------------------------------------------------------- 1 | 2 | registry: HashMap[Bytes[100], address] 3 | 4 | @external 5 | def register(name: Bytes[100], owner: address): 6 | assert self.registry[name] == empty(address) # check name has not been set yet. 7 | self.registry[name] = owner 8 | 9 | 10 | @view 11 | @external 12 | def lookup(name: Bytes[100]) -> address: 13 | return self.registry[name] 14 | -------------------------------------------------------------------------------- /examples/storage/advanced_storage.vy: -------------------------------------------------------------------------------- 1 | event DataChange: 2 | setter: indexed(address) 3 | value: int128 4 | 5 | storedData: public(int128) 6 | 7 | @external 8 | def __init__(_x: int128): 9 | self.storedData = _x 10 | 11 | @external 12 | def set(_x: int128): 13 | assert _x >= 0, "No negative values" 14 | assert self.storedData < 100, "Storage is locked when 100 or more is stored" 15 | self.storedData = _x 16 | log DataChange(msg.sender, _x) 17 | 18 | @external 19 | def reset(): 20 | self.storedData = 0 21 | -------------------------------------------------------------------------------- /examples/storage/storage.vy: -------------------------------------------------------------------------------- 1 | storedData: public(int128) 2 | 3 | @external 4 | def __init__(_x: int128): 5 | self.storedData = _x 6 | 7 | @external 8 | def set(_x: int128): 9 | self.storedData = _x -------------------------------------------------------------------------------- /hooks/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # DockerHub automated build override 4 | 5 | # $IMAGE_NAME var is injected into the build so the tag is correct. 6 | docker build \ 7 | --build-arg VCS_REF=`git rev-parse --short HEAD` \ 8 | --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \ 9 | -t $IMAGE_NAME . 10 | -------------------------------------------------------------------------------- /images/curve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/images/curve.png -------------------------------------------------------------------------------- /images/cyfrin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/images/cyfrin.png -------------------------------------------------------------------------------- /images/lido.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/images/lido.png -------------------------------------------------------------------------------- /images/unore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/images/unore.png -------------------------------------------------------------------------------- /images/yearn-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/images/yearn-logo.png -------------------------------------------------------------------------------- /logo/vyper-logo-flat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/logo/vyper-logo-flat.png -------------------------------------------------------------------------------- /logo/vyper-logo-flat.svg: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /logo/vyper-logo-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/logo/vyper-logo-transparent.png -------------------------------------------------------------------------------- /logo/vyper-logo-transparent.svg: -------------------------------------------------------------------------------- 1 | 12 | -------------------------------------------------------------------------------- /logo/vyper-logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/logo/vyper-logo.ai -------------------------------------------------------------------------------- /make.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | if "%1"=="init" goto :init 3 | if "%1"=="test" goto :test 4 | if "%1"=="dev-deps" goto :dev-deps 5 | if "%1"=="lint" goto :lint 6 | if "%1"=="docs" goto :docs 7 | if "%1"=="freeze" goto :freeze 8 | if "%1"=="clean" goto :clean 9 | if "%1"=="clean-build" goto :clean-build 10 | if "%1"=="clean-pyc" goto :clean-pyc 11 | if "%1"=="clean-test" goto :clean-test 12 | rem Default 13 | if "%1"=="" goto :init 14 | 15 | :error 16 | echo Unknown parameters: %* 17 | echo Expected: init test lint clean clean-pyc clean-build clean-test docs 18 | rem echo Expect: test lint clean clean-pyc clean-build clean-test docs docker-build 19 | goto :end 20 | 21 | :init 22 | python setup.py install 23 | goto :end 24 | 25 | :test 26 | python setup.py test 27 | goto :end 28 | 29 | :dev-deps 30 | python -m pip install .[test,lint] 31 | goto :end 32 | 33 | :lint 34 | tox -e lint 35 | goto :end 36 | 37 | :docs 38 | CALL docs\make clean 39 | CALL docs\make html 40 | START docs\_build\html\index.html 41 | goto :end 42 | 43 | :freeze 44 | CALL :clean 45 | CALL :init 46 | set PYTHONPATH=. 47 | for /f "delims=" %%a in ('python vyper/cli/vyper_compile.py --version') do @set VERSION=%%a 48 | pyinstaller --clean --onefile vyper/cli/vyper_compile.py --name vyper.%VERSION%.windows --add-data vyper;vyper 49 | goto :end 50 | 51 | :clean 52 | CALL :clean-build 53 | CALL :clean-pyc 54 | CALL :clean-test 55 | goto :end 56 | 57 | :clean-build 58 | if exist build RMDIR /Q /S build 59 | if exist dist RMDIR /Q /S dist 60 | for /d %%x in (*.egg-info) do if exist "%%x" RMDIR /Q /S "%%x" 61 | for /r %%x in (*.spec) do del %%x 62 | goto :end 63 | 64 | 65 | :clean-pyc 66 | for /r %%x in (*.pyc) do del %%x 67 | for /r %%x in (*.pyo) do del %%x 68 | for /r %%x in (*~) do del %%x 69 | for /r /d %%x in (__pycache__) do if exist "%%x" RMDIR "%%x" 70 | goto :end 71 | 72 | 73 | :clean-test 74 | for /r /d %%x in (htmlcov) do if exist "%%x" RMDIR /Q /S "%%x" 75 | goto :end 76 | 77 | :end 78 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | # NOTE: you have to use single-quoted strings in TOML for regular expressions. 2 | # It's the equivalent of r-strings in Python. Multiline strings are treated as 3 | # verbose regular expressions by Black. Use [ ] to denote a significant space 4 | # character. 5 | 6 | [tool.black] 7 | line-length = 100 8 | target-version = ['py310'] 9 | include = '\.pyi?$' 10 | exclude = ''' 11 | /( 12 | \.eggs 13 | | \.git 14 | | \.hg 15 | | \.mypy_cache 16 | | \.tox 17 | | \.venv 18 | | _build 19 | | buck-out 20 | | build 21 | | dist 22 | | env 23 | | venv 24 | )/ 25 | ''' 26 | -------------------------------------------------------------------------------- /quicktest.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # examples: 4 | # ./quicktest.sh 5 | # ./quicktest.sh tests/.../mytest.py 6 | 7 | # run pytest but bail out on first error and suppress coverage. 8 | # useful for dev workflow 9 | pytest -q --no-cov -s --instafail -x --disable-warnings "$@" 10 | -------------------------------------------------------------------------------- /requirements-docs.txt: -------------------------------------------------------------------------------- 1 | sphinx==4.5.0 2 | recommonmark==0.6.0 3 | sphinx_rtd_theme==0.5.2 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | license_files = LICENSE 3 | 4 | [aliases] 5 | test = pytest 6 | 7 | [flake8] 8 | extend-ignore = E203 9 | max-line-length = 100 10 | exclude = 11 | venv* 12 | .tox 13 | docs 14 | build 15 | per-file-ignores = 16 | */__init__.py: F401 17 | 18 | [tool:isort] 19 | force_grid_wrap = 0 20 | include_trailing_comma = True 21 | known_first_party = vyper 22 | multi_line_output = 3 23 | use_parentheses = True 24 | ensure_newline_before_comments = True 25 | line_length = 100 26 | 27 | [tool:pytest] 28 | addopts = -n auto 29 | --cov-branch 30 | --cov-report term 31 | --cov-report html 32 | --cov-report xml 33 | --cov=vyper 34 | python_files = test_*.py 35 | testpaths = tests 36 | markers = 37 | fuzzing: Run Hypothesis fuzz test suite (deselect with '-m "not fuzzing"') 38 | 39 | [tool:mypy] 40 | ignore_missing_imports = True 41 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/tests/__init__.py -------------------------------------------------------------------------------- /tests/ast/nodes/test_binary.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import ast as vy_ast 4 | from vyper.exceptions import SyntaxException 5 | 6 | 7 | def test_binary_becomes_bytes(): 8 | expected = vy_ast.parse_to_ast("foo: Bytes[1] = b'\x01'") 9 | mutated = vy_ast.parse_to_ast("foo: Bytes[1] = 0b00000001") 10 | 11 | assert vy_ast.compare_nodes(expected, mutated) 12 | 13 | 14 | def test_binary_length(): 15 | with pytest.raises(SyntaxException): 16 | vy_ast.parse_to_ast("foo: Bytes[1] = 0b01") 17 | -------------------------------------------------------------------------------- /tests/ast/nodes/test_compare_nodes.py: -------------------------------------------------------------------------------- 1 | from vyper import ast as vy_ast 2 | 3 | 4 | def test_compare_different_node_clases(): 5 | vyper_ast = vy_ast.parse_to_ast("foo = 42") 6 | left = vyper_ast.body[0].target 7 | right = vyper_ast.body[0].value 8 | 9 | assert left != right 10 | assert not vy_ast.compare_nodes(left, right) 11 | 12 | 13 | def test_compare_different_nodes_same_class(): 14 | vyper_ast = vy_ast.parse_to_ast("[1, 2]") 15 | left, right = vyper_ast.body[0].value.elements 16 | 17 | assert left != right 18 | assert not vy_ast.compare_nodes(left, right) 19 | 20 | 21 | def test_compare_different_nodes_same_value(): 22 | vyper_ast = vy_ast.parse_to_ast("[1, 1]") 23 | left, right = vyper_ast.body[0].value.elements 24 | 25 | assert left != right 26 | assert vy_ast.compare_nodes(left, right) 27 | 28 | 29 | def test_compare_complex_nodes_same_value(): 30 | vyper_ast = vy_ast.parse_to_ast("[{'foo':'bar', 43:[1,2,3]}, {'foo':'bar', 43:[1,2,3]}]") 31 | left, right = vyper_ast.body[0].value.elements 32 | 33 | assert left != right 34 | assert vy_ast.compare_nodes(left, right) 35 | 36 | 37 | def test_compare_same_node(): 38 | vyper_ast = vy_ast.parse_to_ast("42") 39 | node = vyper_ast.body[0].value 40 | 41 | assert node == node 42 | assert vy_ast.compare_nodes(node, node) 43 | -------------------------------------------------------------------------------- /tests/ast/nodes/test_evaluate_boolop.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import given, settings 3 | from hypothesis import strategies as st 4 | 5 | from vyper import ast as vy_ast 6 | 7 | variables = "abcdefghij" 8 | 9 | 10 | @pytest.mark.fuzzing 11 | @settings(max_examples=50, deadline=1000) 12 | @given(values=st.lists(st.booleans(), min_size=2, max_size=10)) 13 | @pytest.mark.parametrize("comparator", ["and", "or"]) 14 | def test_boolop_simple(get_contract, values, comparator): 15 | input_value = ",".join(f"{i}: bool" for i in variables[: len(values)]) 16 | return_value = f" {comparator} ".join(variables[: len(values)]) 17 | 18 | source = f""" 19 | @external 20 | def foo({input_value}) -> bool: 21 | return {return_value} 22 | """ 23 | contract = get_contract(source) 24 | 25 | literal_op = f" {comparator} ".join(str(i) for i in values) 26 | 27 | vyper_ast = vy_ast.parse_to_ast(literal_op) 28 | old_node = vyper_ast.body[0].value 29 | new_node = old_node.evaluate() 30 | 31 | assert contract.foo(*values) == new_node.value 32 | 33 | 34 | @pytest.mark.fuzzing 35 | @settings(max_examples=50, deadline=1000) 36 | @given( 37 | values=st.lists(st.booleans(), min_size=2, max_size=10), 38 | comparators=st.lists(st.sampled_from(["and", "or"]), min_size=11, max_size=11), 39 | ) 40 | def test_boolop_nested(get_contract, values, comparators): 41 | input_value = ",".join(f"{i}: bool" for i in variables[: len(values)]) 42 | return_value = " ".join(f"{a} {b}" for a, b in zip(variables[: len(values)], comparators)) 43 | return_value = return_value.rsplit(maxsplit=1)[0] 44 | 45 | source = f""" 46 | @external 47 | def foo({input_value}) -> bool: 48 | return {return_value} 49 | """ 50 | contract = get_contract(source) 51 | 52 | literal_op = " ".join(f"{a} {b}" for a, b in zip(values, comparators)) 53 | literal_op = literal_op.rsplit(maxsplit=1)[0] 54 | 55 | vyper_ast = vy_ast.parse_to_ast(literal_op) 56 | vy_ast.folding.replace_literal_ops(vyper_ast) 57 | expected = vyper_ast.body[0].value.value 58 | 59 | assert contract.foo(*values) == expected 60 | -------------------------------------------------------------------------------- /tests/ast/nodes/test_evaluate_subscript.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import given, settings 3 | from hypothesis import strategies as st 4 | 5 | from vyper import ast as vy_ast 6 | 7 | 8 | @pytest.mark.fuzzing 9 | @settings(max_examples=50, deadline=1000) 10 | @given( 11 | idx=st.integers(min_value=0, max_value=9), 12 | array=st.lists(st.integers(), min_size=10, max_size=10), 13 | ) 14 | def test_subscript(get_contract, array, idx): 15 | source = """ 16 | @external 17 | def foo(array: int128[10], idx: uint256) -> int128: 18 | return array[idx] 19 | """ 20 | contract = get_contract(source) 21 | 22 | vyper_ast = vy_ast.parse_to_ast(f"{array}[{idx}]") 23 | old_node = vyper_ast.body[0].value 24 | new_node = old_node.evaluate() 25 | 26 | assert contract.foo(array, idx) == new_node.value 27 | -------------------------------------------------------------------------------- /tests/ast/nodes/test_evaluate_unaryop.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import ast as vy_ast 4 | 5 | 6 | @pytest.mark.parametrize("bool_cond", [True, False]) 7 | def test_unaryop(get_contract, bool_cond): 8 | source = """ 9 | @external 10 | def foo(a: bool) -> bool: 11 | return not a 12 | """ 13 | contract = get_contract(source) 14 | 15 | vyper_ast = vy_ast.parse_to_ast(f"not {bool_cond}") 16 | old_node = vyper_ast.body[0].value 17 | new_node = old_node.evaluate() 18 | 19 | assert contract.foo(bool_cond) == new_node.value 20 | 21 | 22 | @pytest.mark.parametrize("count", range(2, 11)) 23 | @pytest.mark.parametrize("bool_cond", [True, False]) 24 | def test_unaryop_nested(get_contract, bool_cond, count): 25 | source = f""" 26 | @external 27 | def foo(a: bool) -> bool: 28 | return {'not ' * count} a 29 | """ 30 | contract = get_contract(source) 31 | 32 | literal_op = f"{'not ' * count}{bool_cond}" 33 | vyper_ast = vy_ast.parse_to_ast(literal_op) 34 | vy_ast.folding.replace_literal_ops(vyper_ast) 35 | expected = vyper_ast.body[0].value.value 36 | 37 | assert contract.foo(bool_cond) == expected 38 | -------------------------------------------------------------------------------- /tests/ast/nodes/test_from_node.py: -------------------------------------------------------------------------------- 1 | from vyper import ast as vy_ast 2 | 3 | 4 | def test_output_class(): 5 | old_node = vy_ast.parse_to_ast("foo = 42") 6 | new_node = vy_ast.Int.from_node(old_node, value=666) 7 | 8 | assert isinstance(new_node, vy_ast.Int) 9 | 10 | 11 | def test_source(): 12 | old_node = vy_ast.parse_to_ast("foo = 42") 13 | new_node = vy_ast.Int.from_node(old_node, value=666) 14 | 15 | assert old_node.src == new_node.src 16 | assert old_node.node_source_code == new_node.node_source_code 17 | 18 | 19 | def test_kwargs(): 20 | old_node = vy_ast.parse_to_ast("42").body[0].value 21 | new_node = vy_ast.Int.from_node(old_node, value=666) 22 | 23 | assert old_node.value == 42 24 | assert new_node.value == 666 25 | 26 | 27 | def test_compare_nodes(): 28 | old_node = vy_ast.parse_to_ast("foo = 42") 29 | new_node = vy_ast.Int.from_node(old_node, value=666) 30 | 31 | assert not vy_ast.compare_nodes(old_node, new_node) 32 | 33 | 34 | def test_new_node_has_no_parent(): 35 | old_node = vy_ast.parse_to_ast("foo = 42") 36 | new_node = vy_ast.Int.from_node(old_node, value=666) 37 | 38 | assert new_node._parent is None 39 | assert new_node._depth == 0 40 | -------------------------------------------------------------------------------- /tests/ast/nodes/test_get_children.py: -------------------------------------------------------------------------------- 1 | from vyper import ast as vy_ast 2 | 3 | 4 | def test_order(): 5 | node = vy_ast.parse_to_ast("1 + 2").body[0].value 6 | assert node.get_children() == [node.left, node.op, node.right] 7 | 8 | 9 | def test_order_reversed(): 10 | node = vy_ast.parse_to_ast("1 + 2").body[0].value 11 | assert node.get_children(reverse=True) == [node.right, node.op, node.left] 12 | 13 | 14 | def test_type_filter(): 15 | node = vy_ast.parse_to_ast("[1, 2.0, 'three', 4, 0x05]").body[0].value 16 | assert node.get_children(vy_ast.Int) == [node.elements[0], node.elements[3]] 17 | 18 | 19 | def test_dict_filter(): 20 | node = vy_ast.parse_to_ast("[foo, foo(), bar, bar()]").body[0].value 21 | assert node.get_children(filters={"func.id": "foo"}) == [node.elements[1]] 22 | 23 | 24 | def test_only_returns_children(): 25 | node = vy_ast.parse_to_ast("[1, 2, (3, 4), 5]").body[0].value 26 | assert node.get_children() == node.elements 27 | -------------------------------------------------------------------------------- /tests/ast/nodes/test_hex.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import ast as vy_ast 4 | from vyper import semantics 5 | from vyper.exceptions import InvalidLiteral 6 | 7 | code_invalid_checksum = [ 8 | """ 9 | foo: constant(address) = 0x6b175474e89094c44da98b954eedeac495271d0F 10 | """, 11 | """ 12 | foo: constant(address[1]) = [0x6b175474e89094c44da98b954eedeac495271d0F] 13 | """, 14 | """ 15 | @external 16 | def foo(): 17 | bar: address = 0x6b175474e89094c44da98b954eedeac495271d0F 18 | """, 19 | """ 20 | @external 21 | def foo(): 22 | bar: address[1] = [0x6b175474e89094c44da98b954eedeac495271d0F] 23 | """, 24 | """ 25 | @external 26 | def foo(): 27 | for i in [0x6b175474e89094c44da98b954eedeac495271d0F]: 28 | pass 29 | """, 30 | """ 31 | foo: constant(bytes20) = 0x6b175474e89094c44da98b954eedeac495271d0F 32 | """, 33 | """ 34 | foo: constant(bytes4) = 0x12_34_56 35 | """, 36 | ] 37 | 38 | 39 | @pytest.mark.parametrize("code", code_invalid_checksum) 40 | def test_invalid_checksum(code): 41 | vyper_module = vy_ast.parse_to_ast(code) 42 | 43 | with pytest.raises(InvalidLiteral): 44 | vy_ast.validation.validate_literal_nodes(vyper_module) 45 | semantics.validate_semantics(vyper_module, {}) 46 | -------------------------------------------------------------------------------- /tests/ast/test_metadata_journal.py: -------------------------------------------------------------------------------- 1 | from vyper.ast.metadata import NodeMetadata 2 | from vyper.exceptions import VyperException 3 | 4 | 5 | def test_metadata_journal_basic(): 6 | m = NodeMetadata() 7 | 8 | m["x"] = 1 9 | assert m["x"] == 1 10 | 11 | 12 | def test_metadata_journal_commit(): 13 | m = NodeMetadata() 14 | 15 | with m.enter_typechecker_speculation(): 16 | m["x"] = 1 17 | 18 | assert m["x"] == 1 19 | 20 | 21 | def test_metadata_journal_exception(): 22 | m = NodeMetadata() 23 | 24 | m["x"] = 1 25 | try: 26 | with m.enter_typechecker_speculation(): 27 | m["x"] = 2 28 | m["x"] = 3 29 | 30 | assert m["x"] == 3 31 | raise VyperException("dummy exception") 32 | 33 | except VyperException: 34 | pass 35 | 36 | # rollback upon exception 37 | assert m["x"] == 1 38 | 39 | 40 | def test_metadata_journal_rollback_inner(): 41 | m = NodeMetadata() 42 | 43 | m["x"] = 1 44 | with m.enter_typechecker_speculation(): 45 | m["x"] = 2 46 | 47 | try: 48 | with m.enter_typechecker_speculation(): 49 | m["x"] = 3 50 | m["x"] = 4 # test multiple writes 51 | 52 | assert m["x"] == 4 53 | raise VyperException("dummy exception") 54 | 55 | except VyperException: 56 | pass 57 | 58 | assert m["x"] == 2 59 | 60 | 61 | def test_metadata_journal_rollback_outer(): 62 | m = NodeMetadata() 63 | 64 | m["x"] = 1 65 | try: 66 | with m.enter_typechecker_speculation(): 67 | m["x"] = 2 68 | 69 | with m.enter_typechecker_speculation(): 70 | m["x"] = 3 71 | m["x"] = 4 # test multiple writes 72 | 73 | assert m["x"] == 4 74 | 75 | m["x"] = 5 76 | 77 | raise VyperException("dummy exception") 78 | 79 | except VyperException: 80 | pass 81 | 82 | assert m["x"] == 1 83 | -------------------------------------------------------------------------------- /tests/builtins/folding/test_abs.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import example, given, settings 3 | from hypothesis import strategies as st 4 | 5 | from vyper import ast as vy_ast 6 | from vyper.builtins import functions as vy_fn 7 | from vyper.exceptions import OverflowException 8 | 9 | 10 | @pytest.mark.fuzzing 11 | @settings(max_examples=50, deadline=1000) 12 | @given(a=st.integers(min_value=-(2**255) + 1, max_value=2**255 - 1)) 13 | @example(a=0) 14 | def test_abs(get_contract, a): 15 | source = """ 16 | @external 17 | def foo(a: int256) -> int256: 18 | return abs(a) 19 | """ 20 | contract = get_contract(source) 21 | 22 | vyper_ast = vy_ast.parse_to_ast(f"abs({a})") 23 | old_node = vyper_ast.body[0].value 24 | new_node = vy_fn.DISPATCH_TABLE["abs"].evaluate(old_node) 25 | 26 | assert contract.foo(a) == new_node.value == abs(a) 27 | 28 | 29 | @pytest.mark.fuzzing 30 | @settings(max_examples=50, deadline=1000) 31 | @given(a=st.integers(min_value=2**255, max_value=2**256 - 1)) 32 | def test_abs_upper_bound_folding(get_contract, a): 33 | source = f""" 34 | @external 35 | def foo(a: int256) -> int256: 36 | return abs({a}) 37 | """ 38 | with pytest.raises(OverflowException): 39 | get_contract(source) 40 | 41 | 42 | def test_abs_lower_bound(get_contract, assert_tx_failed): 43 | source = """ 44 | @external 45 | def foo(a: int256) -> int256: 46 | return abs(a) 47 | """ 48 | contract = get_contract(source) 49 | 50 | assert_tx_failed(lambda: contract.foo(-(2**255))) 51 | 52 | 53 | def test_abs_lower_bound_folded(get_contract, assert_tx_failed): 54 | source = """ 55 | @external 56 | def foo() -> int256: 57 | return abs(-2**255) 58 | """ 59 | with pytest.raises(OverflowException): 60 | get_contract(source) 61 | -------------------------------------------------------------------------------- /tests/builtins/folding/test_addmod_mulmod.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import assume, given, settings 3 | from hypothesis import strategies as st 4 | 5 | from vyper import ast as vy_ast 6 | from vyper.builtins import functions as vy_fn 7 | 8 | st_uint256 = st.integers(min_value=0, max_value=2**256 - 1) 9 | 10 | 11 | @pytest.mark.fuzzing 12 | @settings(max_examples=50, deadline=1000) 13 | @given(a=st_uint256, b=st_uint256, c=st_uint256) 14 | @pytest.mark.parametrize("fn_name", ["uint256_addmod", "uint256_mulmod"]) 15 | def test_modmath(get_contract, a, b, c, fn_name): 16 | assume(c > 0) 17 | 18 | source = f""" 19 | @external 20 | def foo(a: uint256, b: uint256, c: uint256) -> uint256: 21 | return {fn_name}(a, b, c) 22 | """ 23 | contract = get_contract(source) 24 | 25 | vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({a}, {b}, {c})") 26 | old_node = vyper_ast.body[0].value 27 | new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node) 28 | 29 | assert contract.foo(a, b, c) == new_node.value 30 | -------------------------------------------------------------------------------- /tests/builtins/folding/test_epsilon.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import ast as vy_ast 4 | from vyper.builtins import functions as vy_fn 5 | 6 | 7 | @pytest.mark.parametrize("typ_name", ["decimal"]) 8 | def test_epsilon(get_contract, typ_name): 9 | source = f""" 10 | @external 11 | def foo() -> {typ_name}: 12 | return epsilon({typ_name}) 13 | """ 14 | contract = get_contract(source) 15 | 16 | vyper_ast = vy_ast.parse_to_ast(f"epsilon({typ_name})") 17 | old_node = vyper_ast.body[0].value 18 | new_node = vy_fn.DISPATCH_TABLE["epsilon"].evaluate(old_node) 19 | 20 | assert contract.foo() == new_node.value 21 | -------------------------------------------------------------------------------- /tests/builtins/folding/test_floor_ceil.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | 3 | import pytest 4 | from hypothesis import example, given, settings 5 | from hypothesis import strategies as st 6 | 7 | from vyper import ast as vy_ast 8 | from vyper.builtins import functions as vy_fn 9 | 10 | st_decimals = st.decimals( 11 | min_value=-(2**32), max_value=2**32, allow_nan=False, allow_infinity=False, places=10 12 | ) 13 | 14 | 15 | @pytest.mark.fuzzing 16 | @settings(max_examples=50, deadline=1000) 17 | @given(value=st_decimals) 18 | @example(value=Decimal("0.9999999999")) 19 | @example(value=Decimal("0.0000000001")) 20 | @example(value=Decimal("-0.9999999999")) 21 | @example(value=Decimal("-0.0000000001")) 22 | @pytest.mark.parametrize("fn_name", ["floor", "ceil"]) 23 | def test_floor_ceil(get_contract, value, fn_name): 24 | source = f""" 25 | @external 26 | def foo(a: decimal) -> int256: 27 | return {fn_name}(a) 28 | """ 29 | contract = get_contract(source) 30 | 31 | vyper_ast = vy_ast.parse_to_ast(f"{fn_name}({value})") 32 | old_node = vyper_ast.body[0].value 33 | new_node = vy_fn.DISPATCH_TABLE[fn_name].evaluate(old_node) 34 | 35 | assert contract.foo(value) == new_node.value 36 | -------------------------------------------------------------------------------- /tests/builtins/folding/test_fold_as_wei_value.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import given, settings 3 | from hypothesis import strategies as st 4 | 5 | from vyper import ast as vy_ast 6 | from vyper.builtins import functions as vy_fn 7 | from vyper.utils import SizeLimits 8 | 9 | denoms = [x for k in vy_fn.AsWeiValue.wei_denoms.keys() for x in k] 10 | 11 | 12 | st_decimals = st.decimals( 13 | min_value=0, 14 | max_value=SizeLimits.MAX_AST_DECIMAL, 15 | allow_nan=False, 16 | allow_infinity=False, 17 | places=10, 18 | ) 19 | 20 | 21 | @pytest.mark.fuzzing 22 | @settings(max_examples=10, deadline=1000) 23 | @given(value=st_decimals) 24 | @pytest.mark.parametrize("denom", denoms) 25 | def test_decimal(get_contract, value, denom): 26 | source = f""" 27 | @external 28 | def foo(a: decimal) -> uint256: 29 | return as_wei_value(a, '{denom}') 30 | """ 31 | contract = get_contract(source) 32 | 33 | vyper_ast = vy_ast.parse_to_ast(f"as_wei_value({value:.10f}, '{denom}')") 34 | old_node = vyper_ast.body[0].value 35 | new_node = vy_fn.AsWeiValue().evaluate(old_node) 36 | 37 | assert contract.foo(value) == new_node.value 38 | 39 | 40 | @pytest.mark.fuzzing 41 | @settings(max_examples=10, deadline=1000) 42 | @given(value=st.integers(min_value=0, max_value=2**128)) 43 | @pytest.mark.parametrize("denom", denoms) 44 | def test_integer(get_contract, value, denom): 45 | source = f""" 46 | @external 47 | def foo(a: uint256) -> uint256: 48 | return as_wei_value(a, '{denom}') 49 | """ 50 | contract = get_contract(source) 51 | 52 | vyper_ast = vy_ast.parse_to_ast(f"as_wei_value({value}, '{denom}')") 53 | old_node = vyper_ast.body[0].value 54 | new_node = vy_fn.AsWeiValue().evaluate(old_node) 55 | 56 | assert contract.foo(value) == new_node.value 57 | -------------------------------------------------------------------------------- /tests/builtins/folding/test_len.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import ast as vy_ast 4 | from vyper.builtins import functions as vy_fn 5 | 6 | 7 | @pytest.mark.parametrize("length", [0, 1, 32, 33, 64, 65, 1024]) 8 | def test_len_string(get_contract, length): 9 | source = """ 10 | @external 11 | def foo(a: String[1024]) -> uint256: 12 | return len(a) 13 | """ 14 | contract = get_contract(source) 15 | 16 | value = "a" * length 17 | 18 | vyper_ast = vy_ast.parse_to_ast(f"len('{value}')") 19 | old_node = vyper_ast.body[0].value 20 | new_node = vy_fn.Len().evaluate(old_node) 21 | 22 | assert contract.foo(value) == new_node.value 23 | 24 | 25 | @pytest.mark.parametrize("length", [0, 1, 32, 33, 64, 65, 1024]) 26 | def test_len_bytes(get_contract, length): 27 | source = """ 28 | @external 29 | def foo(a: Bytes[1024]) -> uint256: 30 | return len(a) 31 | """ 32 | contract = get_contract(source) 33 | 34 | value = "a" * length 35 | 36 | vyper_ast = vy_ast.parse_to_ast(f"len(b'{value}')") 37 | old_node = vyper_ast.body[0].value 38 | new_node = vy_fn.Len().evaluate(old_node) 39 | 40 | assert contract.foo(value.encode()) == new_node.value 41 | 42 | 43 | @pytest.mark.parametrize("length", [1, 32, 33, 64, 65, 1024]) 44 | def test_len_hex(get_contract, length): 45 | source = """ 46 | @external 47 | def foo(a: Bytes[1024]) -> uint256: 48 | return len(a) 49 | """ 50 | contract = get_contract(source) 51 | 52 | value = f"0x{'00' * length}" 53 | 54 | vyper_ast = vy_ast.parse_to_ast(f"len({value})") 55 | old_node = vyper_ast.body[0].value 56 | new_node = vy_fn.Len().evaluate(old_node) 57 | 58 | assert contract.foo(value) == new_node.value 59 | -------------------------------------------------------------------------------- /tests/builtins/folding/test_powmod.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import given, settings 3 | from hypothesis import strategies as st 4 | 5 | from vyper import ast as vy_ast 6 | from vyper.builtins import functions as vy_fn 7 | 8 | st_uint256 = st.integers(min_value=0, max_value=2**256) 9 | 10 | 11 | @pytest.mark.fuzzing 12 | @settings(max_examples=100, deadline=1000) 13 | @given(a=st_uint256, b=st_uint256) 14 | def test_powmod_uint256(get_contract, a, b): 15 | source = """ 16 | @external 17 | def foo(a: uint256, b: uint256) -> uint256: 18 | return pow_mod256(a, b) 19 | """ 20 | contract = get_contract(source) 21 | 22 | vyper_ast = vy_ast.parse_to_ast(f"pow_mod256({a}, {b})") 23 | old_node = vyper_ast.body[0].value 24 | new_node = vy_fn.PowMod256().evaluate(old_node) 25 | 26 | assert contract.foo(a, b) == new_node.value 27 | -------------------------------------------------------------------------------- /tests/cli/outputs/test_storage_layout.py: -------------------------------------------------------------------------------- 1 | from vyper.compiler import compile_code 2 | 3 | 4 | def test_storage_layout(): 5 | code = """ 6 | foo: HashMap[address, uint256] 7 | 8 | @external 9 | @nonreentrant("foo") 10 | def public_foo1(): 11 | pass 12 | 13 | @external 14 | @nonreentrant("foo") 15 | def public_foo2(): 16 | pass 17 | 18 | 19 | @internal 20 | @nonreentrant("bar") 21 | def _bar(): 22 | pass 23 | 24 | arr: DynArray[uint256, 3] 25 | 26 | # mix it up a little 27 | baz: Bytes[65] 28 | bar: uint256 29 | 30 | @external 31 | @nonreentrant("bar") 32 | def public_bar(): 33 | pass 34 | 35 | @external 36 | @nonreentrant("foo") 37 | def public_foo3(): 38 | pass 39 | """ 40 | 41 | out = compile_code(code, output_formats=["layout"]) 42 | 43 | assert out["layout"]["storage_layout"] == { 44 | "nonreentrant.foo": {"type": "nonreentrant lock", "slot": 0}, 45 | "nonreentrant.bar": {"type": "nonreentrant lock", "slot": 1}, 46 | "foo": {"type": "HashMap[address, uint256]", "slot": 2}, 47 | "arr": {"type": "DynArray[uint256, 3]", "slot": 3}, 48 | "baz": {"type": "Bytes[65]", "slot": 7}, 49 | "bar": {"type": "uint256", "slot": 11}, 50 | } 51 | 52 | 53 | def test_storage_and_immutables_layout(): 54 | code = """ 55 | name: String[32] 56 | SYMBOL: immutable(String[32]) 57 | DECIMALS: immutable(uint8) 58 | 59 | @external 60 | def __init__(): 61 | SYMBOL = "VYPR" 62 | DECIMALS = 18 63 | """ 64 | 65 | expected_layout = { 66 | "code_layout": { 67 | "DECIMALS": {"length": 32, "offset": 64, "type": "uint8"}, 68 | "SYMBOL": {"length": 64, "offset": 0, "type": "String[32]"}, 69 | }, 70 | "storage_layout": {"name": {"slot": 0, "type": "String[32]"}}, 71 | } 72 | 73 | out = compile_code(code, output_formats=["layout"]) 74 | assert out["layout"] == expected_layout 75 | -------------------------------------------------------------------------------- /tests/cli/vyper_compile/test_compile_files.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.cli.vyper_compile import compile_files 4 | 5 | 6 | def test_combined_json_keys(tmp_path): 7 | bar_path = tmp_path.joinpath("bar.vy") 8 | with bar_path.open("w") as fp: 9 | fp.write("") 10 | 11 | combined_keys = { 12 | "bytecode", 13 | "bytecode_runtime", 14 | "blueprint_bytecode", 15 | "abi", 16 | "source_map", 17 | "layout", 18 | "method_identifiers", 19 | "userdoc", 20 | "devdoc", 21 | } 22 | compile_data = compile_files([bar_path], ["combined_json"], root_folder=tmp_path) 23 | 24 | assert set(compile_data.keys()) == {"bar.vy", "version"} 25 | assert set(compile_data["bar.vy"].keys()) == combined_keys 26 | 27 | 28 | def test_invalid_root_path(): 29 | with pytest.raises(FileNotFoundError): 30 | compile_files([], [], root_folder="path/that/does/not/exist") 31 | -------------------------------------------------------------------------------- /tests/cli/vyper_compile/test_parse_args.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import pytest 4 | 5 | from vyper.cli.vyper_compile import _parse_args 6 | 7 | 8 | @pytest.fixture 9 | def chdir_path(tmp_path): 10 | orig_path = os.getcwd() 11 | yield tmp_path 12 | os.chdir(orig_path) 13 | 14 | 15 | def test_paths(chdir_path): 16 | code = """ 17 | @external 18 | def foo() -> bool: 19 | return True 20 | """ 21 | bar_path = chdir_path.joinpath("bar.vy") 22 | with bar_path.open("w") as fp: 23 | fp.write(code) 24 | _parse_args([str(bar_path)]) # absolute path 25 | os.chdir(chdir_path.parent) 26 | _parse_args([str(bar_path)]) # absolute path, subfolder of cwd 27 | _parse_args([str(bar_path.relative_to(chdir_path.parent))]) # relative path 28 | -------------------------------------------------------------------------------- /tests/cli/vyper_json/test_compile_json.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | from copy import deepcopy 5 | 6 | import pytest 7 | 8 | from vyper.cli.vyper_json import compile_from_input_dict, compile_json 9 | from vyper.exceptions import JSONError 10 | 11 | FOO_CODE = """ 12 | import contracts.bar as Bar 13 | 14 | @external 15 | def foo(a: address) -> bool: 16 | return Bar(a).bar(1) 17 | """ 18 | 19 | BAR_CODE = """ 20 | @external 21 | def bar(a: uint256) -> bool: 22 | return True 23 | """ 24 | 25 | BAR_ABI = [ 26 | { 27 | "name": "bar", 28 | "outputs": [{"type": "bool", "name": "out"}], 29 | "inputs": [{"type": "uint256", "name": "a"}], 30 | "stateMutability": "nonpayable", 31 | "type": "function", 32 | "gas": 313, 33 | } 34 | ] 35 | 36 | INPUT_JSON = { 37 | "language": "Vyper", 38 | "sources": { 39 | "contracts/foo.vy": {"content": FOO_CODE}, 40 | "contracts/bar.vy": {"content": BAR_CODE}, 41 | }, 42 | "interfaces": {"contracts/bar.json": {"abi": BAR_ABI}}, 43 | "settings": {"outputSelection": {"*": ["*"]}}, 44 | } 45 | 46 | 47 | def test_input_formats(): 48 | assert compile_json(INPUT_JSON) == compile_json(json.dumps(INPUT_JSON)) 49 | 50 | 51 | def test_bad_json(): 52 | with pytest.raises(JSONError): 53 | compile_json("this probably isn't valid JSON, is it") 54 | 55 | 56 | def test_keyerror_becomes_jsonerror(): 57 | input_json = deepcopy(INPUT_JSON) 58 | del input_json["sources"] 59 | with pytest.raises(KeyError): 60 | compile_from_input_dict(input_json) 61 | with pytest.raises(JSONError): 62 | compile_json(input_json) 63 | -------------------------------------------------------------------------------- /tests/cli/vyper_json/test_get_settings.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pytest 4 | 5 | from vyper.cli.vyper_json import get_evm_version 6 | from vyper.exceptions import JSONError 7 | 8 | 9 | def test_unknown_evm(): 10 | with pytest.raises(JSONError): 11 | get_evm_version({"settings": {"evmVersion": "foo"}}) 12 | 13 | 14 | @pytest.mark.parametrize( 15 | "evm_version", 16 | [ 17 | "homestead", 18 | "tangerineWhistle", 19 | "spuriousDragon", 20 | "byzantium", 21 | "constantinople", 22 | "petersburg", 23 | ], 24 | ) 25 | def test_early_evm(evm_version): 26 | with pytest.raises(JSONError): 27 | get_evm_version({"settings": {"evmVersion": evm_version}}) 28 | 29 | 30 | @pytest.mark.parametrize("evm_version", ["istanbul", "berlin", "paris", "shanghai", "cancun"]) 31 | def test_valid_evm(evm_version): 32 | assert evm_version == get_evm_version({"settings": {"evmVersion": evm_version}}) 33 | -------------------------------------------------------------------------------- /tests/cli/vyper_json/test_output_dict.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import vyper 4 | from vyper.cli.vyper_json import format_to_output_dict 5 | from vyper.compiler import OUTPUT_FORMATS, compile_codes 6 | 7 | FOO_CODE = """ 8 | @external 9 | def foo() -> bool: 10 | return True 11 | """ 12 | 13 | 14 | def test_keys(): 15 | compiler_data = compile_codes({"foo.vy": FOO_CODE}, output_formats=list(OUTPUT_FORMATS.keys())) 16 | output_json = format_to_output_dict(compiler_data) 17 | assert sorted(output_json.keys()) == ["compiler", "contracts", "sources"] 18 | assert output_json["compiler"] == f"vyper-{vyper.__version__}" 19 | data = compiler_data["foo.vy"] 20 | assert output_json["sources"]["foo.vy"] == {"id": 0, "ast": data["ast_dict"]["ast"]} 21 | assert output_json["contracts"]["foo.vy"]["foo"] == { 22 | "abi": data["abi"], 23 | "devdoc": data["devdoc"], 24 | "interface": data["interface"], 25 | "ir": data["ir_dict"], 26 | "userdoc": data["userdoc"], 27 | "metadata": data["metadata"], 28 | "evm": { 29 | "bytecode": {"object": data["bytecode"], "opcodes": data["opcodes"]}, 30 | "deployedBytecode": { 31 | "object": data["bytecode_runtime"], 32 | "opcodes": data["opcodes_runtime"], 33 | "sourceMap": data["source_map"]["pc_pos_map_compressed"], 34 | "sourceMapFull": data["source_map_full"], 35 | }, 36 | "methodIdentifiers": data["method_identifiers"], 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /tests/cli/vyper_json/test_output_selection.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pytest 4 | 5 | from vyper.cli.vyper_json import TRANSLATE_MAP, get_input_dict_output_formats 6 | from vyper.exceptions import JSONError 7 | 8 | 9 | def test_no_outputs(): 10 | with pytest.raises(KeyError): 11 | get_input_dict_output_formats({}, {}) 12 | 13 | 14 | def test_invalid_output(): 15 | input_json = {"settings": {"outputSelection": {"foo.vy": ["abi", "foobar"]}}} 16 | sources = {"foo.vy": ""} 17 | with pytest.raises(JSONError): 18 | get_input_dict_output_formats(input_json, sources) 19 | 20 | 21 | def test_unknown_contract(): 22 | input_json = {"settings": {"outputSelection": {"bar.vy": ["abi"]}}} 23 | sources = {"foo.vy": ""} 24 | with pytest.raises(JSONError): 25 | get_input_dict_output_formats(input_json, sources) 26 | 27 | 28 | @pytest.mark.parametrize("output", TRANSLATE_MAP.items()) 29 | def test_translate_map(output): 30 | input_json = {"settings": {"outputSelection": {"foo.vy": [output[0]]}}} 31 | sources = {"foo.vy": ""} 32 | assert get_input_dict_output_formats(input_json, sources) == {"foo.vy": [output[1]]} 33 | 34 | 35 | def test_star(): 36 | input_json = {"settings": {"outputSelection": {"*": ["*"]}}} 37 | sources = {"foo.vy": "", "bar.vy": ""} 38 | expected = sorted(set(TRANSLATE_MAP.values())) 39 | result = get_input_dict_output_formats(input_json, sources) 40 | assert result == {"foo.vy": expected, "bar.vy": expected} 41 | 42 | 43 | def test_evm(): 44 | input_json = {"settings": {"outputSelection": {"foo.vy": ["abi", "evm"]}}} 45 | sources = {"foo.vy": ""} 46 | expected = ["abi"] + sorted(v for k, v in TRANSLATE_MAP.items() if k.startswith("evm")) 47 | result = get_input_dict_output_formats(input_json, sources) 48 | assert result == {"foo.vy": expected} 49 | 50 | 51 | def test_solc_style(): 52 | input_json = {"settings": {"outputSelection": {"foo.vy": {"": ["abi"], "foo.vy": ["ir"]}}}} 53 | sources = {"foo.vy": ""} 54 | assert get_input_dict_output_formats(input_json, sources) == {"foo.vy": ["abi", "ir_dict"]} 55 | -------------------------------------------------------------------------------- /tests/compiler/__init__.py: -------------------------------------------------------------------------------- 1 | # prevent module name collision between tests/compiler/test_pre_parser.py 2 | # and tests/ast/test_pre_parser.py 3 | -------------------------------------------------------------------------------- /tests/compiler/ir/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/tests/compiler/ir/__init__.py -------------------------------------------------------------------------------- /tests/compiler/ir/test_compile_ir.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.codegen.ir_node import IRnode 4 | from vyper.ir import compile_ir 5 | from vyper.ir.s_expressions import parse_s_exp 6 | 7 | fail_list = [ 8 | [-(2**255) - 3], 9 | [2**256 + 3], 10 | ["set", "_poz"], 11 | [["set", "var_1", 0, 0]], 12 | ["with", "var_1", 0, ["set", 1, 1]], 13 | ["break"], # invalid break 14 | ["continue"], # invalid continue 15 | ["invalidllelement"], 16 | ] 17 | 18 | 19 | @pytest.mark.parametrize("bad_ir", fail_list) 20 | def test_ir_compile_fail(bad_ir, get_contract_from_ir, assert_compile_failed): 21 | assert_compile_failed(lambda: get_contract_from_ir(IRnode.from_list(bad_ir)), Exception) 22 | 23 | 24 | valid_list = [ 25 | ["pass"], 26 | ["assert", ["slt", ["mload", 0], 300]], 27 | ["assert", ["sgt", ["mload", 0], -1]], 28 | ["assert", ["gt", 1, ["mload", 0]]], 29 | ["assert", ["ge", ["mload", 0], 0]], 30 | ] 31 | 32 | 33 | @pytest.mark.parametrize("good_ir", valid_list) 34 | def test_compile_ir_good(good_ir, get_contract_from_ir): 35 | get_contract_from_ir(IRnode.from_list(good_ir)) 36 | 37 | 38 | def test_ir_from_s_expression(get_contract_from_ir): 39 | code = """ 40 | (seq 41 | (deploy 42 | 0 43 | (seq ; just return 32 byte of calldata back 44 | (calldatacopy 0 4 32) 45 | (return 0 32) 46 | stop 47 | ) 48 | 0)) 49 | """ 50 | abi = [ 51 | { 52 | "name": "test", 53 | "outputs": [{"type": "int128", "name": "out"}], 54 | "inputs": [{"type": "int128", "name": "a"}], 55 | "stateMutability": "nonpayable", 56 | "type": "function", 57 | "gas": 394, 58 | } 59 | ] 60 | 61 | s_expressions = parse_s_exp(code) 62 | ir = IRnode.from_list(s_expressions[0]) 63 | c = get_contract_from_ir(ir, abi=abi) 64 | assert c.test(-123456) == -123456 65 | 66 | 67 | def test_pc_debugger(): 68 | debugger_ir = ["seq", ["mstore", 0, 32], ["pc_debugger"]] 69 | ir_nodes = IRnode.from_list(debugger_ir) 70 | _, line_number_map = compile_ir.assembly_to_evm(compile_ir.compile_to_assembly(ir_nodes)) 71 | assert line_number_map["pc_breakpoints"][0] == 4 72 | -------------------------------------------------------------------------------- /tests/compiler/ir/test_repeat.py: -------------------------------------------------------------------------------- 1 | def test_repeat(get_contract_from_ir, assert_compile_failed): 2 | good_ir = ["repeat", 0, 0, 1, 1, ["seq"]] 3 | bad_ir_1 = ["repeat", 0, 0, 0, 0, ["seq"]] 4 | bad_ir_2 = ["repeat", 0, 0, -1, -1, ["seq"]] 5 | get_contract_from_ir(good_ir) 6 | assert_compile_failed(lambda: get_contract_from_ir(bad_ir_1), Exception) 7 | assert_compile_failed(lambda: get_contract_from_ir(bad_ir_2), Exception) 8 | -------------------------------------------------------------------------------- /tests/compiler/test_calldatacopy.py: -------------------------------------------------------------------------------- 1 | def test_calldatacopy(get_contract_from_ir): 2 | ir = ["calldatacopy", 32, 0, ["calldatasize"]] 3 | get_contract_from_ir(ir) 4 | -------------------------------------------------------------------------------- /tests/compiler/test_default_settings.py: -------------------------------------------------------------------------------- 1 | from vyper.codegen import core 2 | from vyper.compiler.phases import CompilerData 3 | from vyper.compiler.settings import OptimizationLevel, _is_debug_mode 4 | 5 | 6 | def test_default_settings(): 7 | source_code = "" 8 | compiler_data = CompilerData(source_code) 9 | _ = compiler_data.vyper_module # force settings to be computed 10 | 11 | assert compiler_data.settings.optimize == OptimizationLevel.GAS 12 | 13 | 14 | def test_default_opt_level(): 15 | assert OptimizationLevel.default() == OptimizationLevel.GAS 16 | 17 | 18 | def test_codegen_opt_level(): 19 | assert core._opt_level == OptimizationLevel.GAS 20 | assert core._opt_gas() is True 21 | assert core._opt_none() is False 22 | assert core._opt_codesize() is False 23 | 24 | 25 | def test_debug_mode(pytestconfig): 26 | debug_mode = pytestconfig.getoption("enable_compiler_debug_mode") 27 | assert _is_debug_mode() == debug_mode 28 | -------------------------------------------------------------------------------- /tests/compiler/test_opcodes.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import vyper 4 | from vyper.evm import opcodes 5 | from vyper.exceptions import CompilerPanic 6 | 7 | 8 | @pytest.fixture(params=list(opcodes.EVM_VERSIONS)) 9 | def evm_version(request): 10 | default = opcodes.active_evm_version 11 | try: 12 | opcodes.active_evm_version = opcodes.EVM_VERSIONS[request.param] 13 | yield request.param 14 | finally: 15 | opcodes.active_evm_version = default 16 | 17 | 18 | def test_opcodes(): 19 | code = """ 20 | @external 21 | def a() -> bool: 22 | return True 23 | """ 24 | 25 | out = vyper.compile_code(code, ["opcodes_runtime", "opcodes"]) 26 | 27 | assert len(out["opcodes"]) > len(out["opcodes_runtime"]) 28 | assert out["opcodes_runtime"] in out["opcodes"] 29 | 30 | 31 | def test_version_check_no_begin_or_end(): 32 | with pytest.raises(CompilerPanic): 33 | opcodes.version_check() 34 | 35 | 36 | def test_version_check(evm_version): 37 | assert opcodes.version_check(begin=evm_version) 38 | assert opcodes.version_check(end=evm_version) 39 | assert opcodes.version_check(begin=evm_version, end=evm_version) 40 | if evm_version not in ("istanbul"): 41 | assert not opcodes.version_check(end="istanbul") 42 | istanbul_check = opcodes.version_check(begin="istanbul") 43 | assert istanbul_check == (opcodes.EVM_VERSIONS[evm_version] >= opcodes.EVM_VERSIONS["istanbul"]) 44 | 45 | 46 | def test_get_opcodes(evm_version): 47 | ops = opcodes.get_opcodes() 48 | 49 | assert "CHAINID" in ops 50 | assert ops["CREATE2"][-1] == 32000 51 | 52 | if evm_version in ("london", "berlin", "paris", "shanghai", "cancun"): 53 | assert ops["SLOAD"][-1] == 2100 54 | else: 55 | assert evm_version == "istanbul" 56 | assert ops["SLOAD"][-1] == 800 57 | 58 | if evm_version in ("shanghai", "cancun"): 59 | assert "PUSH0" in ops 60 | 61 | if evm_version in ("cancun",): 62 | for op in ("TLOAD", "TSTORE", "MCOPY"): 63 | assert op in ops 64 | else: 65 | for op in ("TLOAD", "TSTORE", "MCOPY"): 66 | assert op not in ops 67 | -------------------------------------------------------------------------------- /tests/compiler/test_sha3_32.py: -------------------------------------------------------------------------------- 1 | from vyper.codegen.ir_node import IRnode 2 | from vyper.evm.opcodes import version_check 3 | from vyper.ir import compile_ir, optimizer 4 | 5 | 6 | def test_sha3_32(): 7 | ir = ["sha3_32", 0] 8 | evm = ["PUSH1", 0, "PUSH1", 0, "MSTORE", "PUSH1", 32, "PUSH1", 0, "SHA3"] 9 | if version_check(begin="shanghai"): 10 | evm = ["PUSH0", "PUSH0", "MSTORE", "PUSH1", 32, "PUSH0", "SHA3"] 11 | assert compile_ir.compile_to_assembly(IRnode.from_list(ir)) == evm 12 | assert compile_ir.compile_to_assembly(optimizer.optimize(IRnode.from_list(ir))) == evm 13 | -------------------------------------------------------------------------------- /tests/examples/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture(autouse=True) 5 | def setup(memory_mocker): 6 | pass 7 | -------------------------------------------------------------------------------- /tests/examples/crowdfund/test_crowdfund_example.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture 5 | def c(w3, get_contract): 6 | with open("examples/crowdfund.vy") as f: 7 | contract_code = f.read() 8 | contract = get_contract(contract_code, *[w3.eth.accounts[1], 50, 60]) 9 | return contract 10 | 11 | 12 | def test_crowdfund_example(c, w3): 13 | a0, a1, a2, a3, a4, a5, a6 = w3.eth.accounts[:7] 14 | c.participate(transact={"value": 5}) 15 | 16 | assert c.timelimit() == 60 17 | assert c.deadline() - w3.eth.get_block("latest").timestamp == 59 18 | assert not w3.eth.get_block("latest").timestamp >= c.deadline() # expired 19 | assert not w3.eth.get_balance(c.address) >= c.goal() # not reached 20 | c.participate(transact={"value": 49}) 21 | # assert c.reached() 22 | pre_bal = w3.eth.get_balance(a1) 23 | w3.testing.mine(100) 24 | assert not w3.eth.get_block("latest").number >= c.deadline() # expired 25 | c.finalize(transact={}) 26 | post_bal = w3.eth.get_balance(a1) 27 | assert post_bal - pre_bal == 54 28 | 29 | 30 | def test_crowdfund_example2(c, w3, assert_tx_failed): 31 | a0, a1, a2, a3, a4, a5, a6 = w3.eth.accounts[:7] 32 | c.participate(transact={"value": 1, "from": a3}) 33 | c.participate(transact={"value": 2, "from": a4}) 34 | c.participate(transact={"value": 3, "from": a5}) 35 | c.participate(transact={"value": 4, "from": a6}) 36 | 37 | assert c.timelimit() == 60 38 | w3.testing.mine(100) 39 | # assert c.expired() 40 | # assert not c.reached() 41 | pre_bals = [w3.eth.get_balance(x) for x in [a3, a4, a5, a6]] 42 | assert_tx_failed(lambda: c.refund(transact={"from": a0})) 43 | c.refund(transact={"from": a3}) 44 | assert_tx_failed(lambda: c.refund(transact={"from": a3})) 45 | c.refund(transact={"from": a4}) 46 | c.refund(transact={"from": a5}) 47 | c.refund(transact={"from": a6}) 48 | post_bals = [w3.eth.get_balance(x) for x in [a3, a4, a5, a6]] 49 | assert [y - x for x, y in zip(pre_bals, post_bals)] == [1, 2, 3, 4] 50 | -------------------------------------------------------------------------------- /tests/examples/name_registry/test_name_registry.py: -------------------------------------------------------------------------------- 1 | def test_name_registry(w3, get_contract, assert_tx_failed): 2 | a0, a1 = w3.eth.accounts[:2] 3 | with open("examples/name_registry/name_registry.vy") as f: 4 | code = f.read() 5 | c = get_contract(code) 6 | c.register(b"jacques", a0, transact={}) 7 | assert c.lookup(b"jacques") == a0 8 | assert_tx_failed(lambda: c.register(b"jacques", a1)) 9 | -------------------------------------------------------------------------------- /tests/examples/storage/test_storage.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | INITIAL_VALUE = 4 4 | 5 | 6 | @pytest.fixture 7 | def storage_contract(w3, get_contract): 8 | with open("examples/storage/storage.vy") as f: 9 | contract_code = f.read() 10 | # Pass constructor variables directly to the contract 11 | contract = get_contract(contract_code, INITIAL_VALUE) 12 | return contract 13 | 14 | 15 | def test_initial_state(storage_contract): 16 | # Check if the constructor of the contract is set up properly 17 | assert storage_contract.storedData() == INITIAL_VALUE 18 | 19 | 20 | def test_set(w3, storage_contract): 21 | k0 = w3.eth.accounts[0] 22 | 23 | # Let k0 try to set the value to 10 24 | storage_contract.set(10, transact={"from": k0}) 25 | assert storage_contract.storedData() == 10 # Directly access storedData 26 | 27 | # Let k0 try to set the value to -5 28 | storage_contract.set(-5, transact={"from": k0}) 29 | assert storage_contract.storedData() == -5 30 | -------------------------------------------------------------------------------- /tests/fixtures/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/tests/fixtures/__init__.py -------------------------------------------------------------------------------- /tests/fixtures/memorymock.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.codegen.context import Context 4 | from vyper.codegen.core import get_type_for_exact_size 5 | 6 | 7 | class ContextMock(Context): 8 | def __init__(self, *args, **kwargs): 9 | super().__init__(*args, **kwargs) 10 | self._mock_vars = False 11 | self._size = 0 12 | 13 | def internal_memory_scope(self): 14 | if not self._mock_vars: 15 | for i in range(20): 16 | self._new_variable( 17 | f"#mock{i}", get_type_for_exact_size(self._size), self._size, bool(i % 2) 18 | ) 19 | self._mock_vars = True 20 | return super().internal_memory_scope() 21 | 22 | @classmethod 23 | def set_mock_var_size(cls, size): 24 | cls._size = size * 32 25 | 26 | 27 | def pytest_addoption(parser): 28 | parser.addoption("--memorymock", action="store_true", help="Run tests with mock allocated vars") 29 | 30 | 31 | def pytest_generate_tests(metafunc): 32 | if "memory_mocker" in metafunc.fixturenames: 33 | params = range(1, 11, 2) if metafunc.config.getoption("memorymock") else [False] 34 | metafunc.parametrize("memory_mocker", params, indirect=True) 35 | 36 | 37 | def pytest_collection_modifyitems(items, config): 38 | if config.getoption("memorymock"): 39 | for item in list(items): 40 | if "memory_mocker" not in item.fixturenames: 41 | items.remove(item) 42 | 43 | # hacky magic to ensure the correct number of tests is shown in collection report 44 | config.pluginmanager.get_plugin("terminalreporter")._numcollected = len(items) 45 | 46 | 47 | @pytest.fixture 48 | def memory_mocker(monkeypatch, request): 49 | if request.param: 50 | monkeypatch.setattr("vyper.codegen.context.Context", ContextMock) 51 | ContextMock.set_mock_var_size(request.param) 52 | -------------------------------------------------------------------------------- /tests/functional/codegen/test_tuple_return.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.mark.parametrize("string", ["a", "abc", "abcde", "potato"]) 5 | def test_string_inside_tuple(get_contract, string): 6 | code = f""" 7 | @external 8 | def test_return() -> (String[6], uint256): 9 | return "{string}", 42 10 | """ 11 | c1 = get_contract(code) 12 | 13 | code = """ 14 | interface jsonabi: 15 | def test_return() -> (String[6], uint256): view 16 | 17 | @external 18 | def test_values(a: address) -> (String[6], uint256): 19 | return jsonabi(a).test_return() 20 | """ 21 | 22 | c2 = get_contract(code) 23 | assert c2.test_values(c1.address) == [string, 42] 24 | 25 | 26 | @pytest.mark.parametrize("string", ["a", "abc", "abcde", "potato"]) 27 | def test_bytes_inside_tuple(get_contract, string): 28 | code = f""" 29 | @external 30 | def test_return() -> (Bytes[6], uint256): 31 | return b"{string}", 42 32 | """ 33 | c1 = get_contract(code) 34 | 35 | code = """ 36 | interface jsonabi: 37 | def test_return() -> (Bytes[6], uint256): view 38 | 39 | @external 40 | def test_values(a: address) -> (Bytes[6], uint256): 41 | return jsonabi(a).test_return() 42 | """ 43 | 44 | c2 = get_contract(code) 45 | assert c2.test_values(c1.address) == [bytes(string, "utf-8"), 42] 46 | -------------------------------------------------------------------------------- /tests/functional/semantics/analysis/test_cyclic_function_calls.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.ast import parse_to_ast 4 | from vyper.exceptions import CallViolation, StructureException 5 | from vyper.semantics.analysis import validate_semantics 6 | from vyper.semantics.analysis.module import ModuleAnalyzer 7 | 8 | 9 | def test_self_function_call(namespace): 10 | code = """ 11 | @internal 12 | def foo(): 13 | self.foo() 14 | """ 15 | vyper_module = parse_to_ast(code) 16 | with namespace.enter_scope(): 17 | with pytest.raises(CallViolation): 18 | ModuleAnalyzer(vyper_module, {}, namespace) 19 | 20 | 21 | def test_cyclic_function_call(namespace): 22 | code = """ 23 | @internal 24 | def foo(): 25 | self.bar() 26 | 27 | @internal 28 | def bar(): 29 | self.foo() 30 | """ 31 | vyper_module = parse_to_ast(code) 32 | with namespace.enter_scope(): 33 | with pytest.raises(CallViolation): 34 | ModuleAnalyzer(vyper_module, {}, namespace) 35 | 36 | 37 | def test_multi_cyclic_function_call(namespace): 38 | code = """ 39 | @internal 40 | def foo(): 41 | self.bar() 42 | 43 | @internal 44 | def bar(): 45 | self.baz() 46 | 47 | @internal 48 | def baz(): 49 | self.potato() 50 | 51 | @internal 52 | def potato(): 53 | self.foo() 54 | """ 55 | vyper_module = parse_to_ast(code) 56 | with namespace.enter_scope(): 57 | with pytest.raises(CallViolation): 58 | ModuleAnalyzer(vyper_module, {}, namespace) 59 | 60 | 61 | def test_global_ann_assign_callable_no_crash(): 62 | code = """ 63 | balanceOf: public(HashMap[address, uint256]) 64 | 65 | @internal 66 | def foo(to : address): 67 | self.balanceOf(to) 68 | """ 69 | vyper_module = parse_to_ast(code) 70 | with pytest.raises(StructureException) as excinfo: 71 | validate_semantics(vyper_module, {}) 72 | assert excinfo.value.message == "Value is not callable" 73 | -------------------------------------------------------------------------------- /tests/functional/semantics/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import ast as vy_ast 4 | from vyper.semantics.namespace import get_namespace 5 | 6 | 7 | @pytest.fixture(scope="session") 8 | def build_node(): 9 | """ 10 | Yields a helper function for generating a single Vyper AST node. 11 | """ 12 | 13 | def _build_node(source): 14 | # docstring ensures string nodes are properly generated, not turned into docstrings 15 | source = f"""'I am a docstring.'\n{source}""" 16 | ast = vy_ast.parse_to_ast(source).body[0] 17 | if isinstance(ast, vy_ast.Expr): 18 | ast = ast.value 19 | return ast 20 | 21 | yield _build_node 22 | 23 | 24 | @pytest.fixture 25 | def namespace(): 26 | """ 27 | Yields a clean `Namespace` object. 28 | """ 29 | obj = get_namespace() 30 | obj.clear() 31 | yield obj 32 | obj.clear() 33 | -------------------------------------------------------------------------------- /tests/functional/semantics/types/test_size_in_bytes.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.semantics.types.utils import type_from_annotation 4 | 5 | BASE_TYPES = ["int128", "uint256", "bool", "address", "bytes32"] 6 | BYTESTRING_TYPES = ["String", "Bytes"] 7 | 8 | 9 | @pytest.mark.parametrize("type_str", BASE_TYPES) 10 | def test_base_types(build_node, type_str): 11 | node = build_node(type_str) 12 | type_definition = type_from_annotation(node) 13 | 14 | assert type_definition.size_in_bytes == 32 15 | 16 | 17 | @pytest.mark.parametrize("type_str", BYTESTRING_TYPES) 18 | @pytest.mark.parametrize("length,size", [(1, 64), (32, 64), (33, 96), (86, 128)]) 19 | def test_array_value_types(build_node, type_str, length, size): 20 | node = build_node(f"{type_str}[{length}]") 21 | type_definition = type_from_annotation(node) 22 | 23 | assert type_definition.size_in_bytes == size 24 | 25 | 26 | @pytest.mark.parametrize("type_str", BASE_TYPES) 27 | @pytest.mark.parametrize("length", range(1, 4)) 28 | def test_dynamic_array_lengths(build_node, type_str, length): 29 | node = build_node(f"DynArray[{type_str}, {length}]") 30 | type_definition = type_from_annotation(node) 31 | 32 | assert type_definition.size_in_bytes == 32 + length * 32 33 | 34 | 35 | @pytest.mark.parametrize("type_str", BASE_TYPES) 36 | @pytest.mark.parametrize("length", range(1, 4)) 37 | def test_base_types_as_arrays(build_node, type_str, length): 38 | node = build_node(f"{type_str}[{length}]") 39 | type_definition = type_from_annotation(node) 40 | 41 | assert type_definition.size_in_bytes == length * 32 42 | 43 | 44 | @pytest.mark.parametrize("type_str", BASE_TYPES) 45 | @pytest.mark.parametrize("first", range(1, 4)) 46 | @pytest.mark.parametrize("second", range(1, 4)) 47 | def test_base_types_as_multidimensional_arrays(build_node, type_str, first, second): 48 | node = build_node(f"{type_str}[{first}][{second}]") 49 | 50 | type_definition = type_from_annotation(node) 51 | 52 | assert type_definition.size_in_bytes == first * second * 32 53 | -------------------------------------------------------------------------------- /tests/functional/semantics/types/test_type_from_abi.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import UnknownType 4 | from vyper.semantics.types import PRIMITIVE_TYPES, SArrayT 5 | from vyper.semantics.types.utils import type_from_abi 6 | 7 | BASE_TYPES = ["int128", "uint256", "bool", "address", "bytes32"] 8 | 9 | 10 | @pytest.mark.parametrize("type_str", BASE_TYPES) 11 | def test_base_types(type_str): 12 | base_t = PRIMITIVE_TYPES[type_str] 13 | type_t = type_from_abi({"type": type_str}) 14 | 15 | assert base_t == type_t 16 | 17 | 18 | @pytest.mark.parametrize("type_str", BASE_TYPES) 19 | def test_base_types_as_arrays(type_str): 20 | base_t = PRIMITIVE_TYPES[type_str] 21 | type_t = type_from_abi({"type": f"{type_str}[3]"}) 22 | 23 | assert type_t == SArrayT(base_t, 3) 24 | 25 | 26 | @pytest.mark.parametrize("type_str", BASE_TYPES) 27 | def test_base_types_as_multidimensional_arrays(type_str): 28 | base_t = PRIMITIVE_TYPES[type_str] 29 | 30 | type_t = type_from_abi({"type": f"{type_str}[3][5]"}) 31 | 32 | assert type_t == SArrayT(SArrayT(base_t, 3), 5) 33 | 34 | 35 | @pytest.mark.parametrize("idx", ["0", "-1", "0x00", "'1'", "foo", "[1]", "(1,)"]) 36 | def test_invalid_index(idx): 37 | with pytest.raises(UnknownType): 38 | type_from_abi({"type": f"int128[{idx}]"}) 39 | -------------------------------------------------------------------------------- /tests/parser/ast_utils/test_ast.py: -------------------------------------------------------------------------------- 1 | from vyper.ast.utils import parse_to_ast 2 | 3 | 4 | def test_ast_equal(): 5 | code = """ 6 | @external 7 | def test() -> int128: 8 | a: uint256 = 100 9 | return 123 10 | """ 11 | 12 | ast1 = parse_to_ast(code) 13 | ast2 = parse_to_ast("\n \n" + code + "\n\n") 14 | 15 | assert ast1 == ast2 16 | 17 | 18 | def test_ast_unequal(): 19 | code1 = """ 20 | @external 21 | def test() -> int128: 22 | a: uint256 = 100 23 | return 123 24 | """ 25 | code2 = """ 26 | @external 27 | def test() -> int128: 28 | a: uint256 = 100 29 | return 121 30 | """ 31 | 32 | ast1 = parse_to_ast(code1) 33 | ast2 = parse_to_ast(code2) 34 | 35 | assert ast1 != ast2 36 | -------------------------------------------------------------------------------- /tests/parser/exceptions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/tests/parser/exceptions/__init__.py -------------------------------------------------------------------------------- /tests/parser/exceptions/test_argument_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import ArgumentException 5 | 6 | fail_list = [ 7 | """ 8 | @external 9 | def foo(): 10 | x = as_wei_value(5, "vader") 11 | """, 12 | """ 13 | @external 14 | def foo(x: int128, x: int128): pass 15 | """, 16 | """ 17 | @external 18 | def foo(x): pass 19 | """, 20 | """ 21 | @external 22 | def foo() -> int128: 23 | return as_wei_value(10) 24 | """, 25 | """ 26 | @external 27 | def foo(): 28 | x: bytes32 = keccak256("moose", 3) 29 | """, 30 | """ 31 | @external 32 | def foo(): 33 | x: Bytes[4] = raw_call(0x1234567890123456789012345678901234567890, outsize=4) 34 | """, 35 | """ 36 | @external 37 | def foo(): 38 | x: Bytes[4] = raw_call( 39 | 0x1234567890123456789012345678901234567890, b"cow", gas=111111, outsize=4, moose=9 40 | ) 41 | """, 42 | """ 43 | @external 44 | def foo(): 45 | x: Bytes[4] = create_minimal_proxy_to(0x1234567890123456789012345678901234567890, outsize=4) 46 | """, 47 | """ 48 | x: public() 49 | """, 50 | """ 51 | @external 52 | def foo(): 53 | raw_log([], b"cow", "dog") 54 | """, 55 | """ 56 | @external 57 | def foo(): 58 | x: Bytes[10] = concat(b"") 59 | """, 60 | """ 61 | @external 62 | def foo(): 63 | x: Bytes[4] = create_minimal_proxy_to(0x1234567890123456789012345678901234567890, b"cow") 64 | """, 65 | """ 66 | @external 67 | def foo(): 68 | a: uint256 = min() 69 | """, 70 | """ 71 | @external 72 | def foo(): 73 | a: uint256 = min(1) 74 | """, 75 | """ 76 | @external 77 | def foo(): 78 | a: uint256 = min(1, 2, 3) 79 | """, 80 | """ 81 | @external 82 | def foo(): 83 | for i in range(): 84 | pass 85 | """, 86 | """ 87 | @external 88 | def foo(): 89 | for i in range(1, 2, 3, 4): 90 | pass 91 | """, 92 | ] 93 | 94 | 95 | @pytest.mark.parametrize("bad_code", fail_list) 96 | def test_function_declaration_exception(bad_code): 97 | with pytest.raises(ArgumentException): 98 | compiler.compile_code(bad_code) 99 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_call_violation.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import raises 3 | 4 | from vyper import compiler 5 | from vyper.exceptions import CallViolation 6 | 7 | call_violation_list = [ 8 | """ 9 | f:int128 10 | 11 | @external 12 | def a (x:int128)->int128: 13 | self.f = 100 14 | return x+5 15 | 16 | @view 17 | @external 18 | def b(): 19 | p: int128 = self.a(10) 20 | """, 21 | """ 22 | @external 23 | def goo(): 24 | pass 25 | 26 | @internal 27 | def foo(): 28 | self.goo() 29 | """, 30 | ] 31 | 32 | 33 | @pytest.mark.parametrize("bad_code", call_violation_list) 34 | def test_call_violation_exception(bad_code): 35 | with raises(CallViolation): 36 | compiler.compile_code(bad_code) 37 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_function_declaration_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import FunctionDeclarationException 5 | 6 | fail_list = [ 7 | """ 8 | x: int128 9 | @external 10 | @const 11 | def foo() -> int128: 12 | pass 13 | """, 14 | """ 15 | x: int128 16 | @external 17 | @monkeydoodledoo 18 | def foo() -> int128: 19 | pass 20 | """, 21 | """ 22 | def foo() -> int128: 23 | q: int128 = 111 24 | return q 25 | """, 26 | """ 27 | q: int128 28 | def foo() -> int128: 29 | return self.q 30 | """, 31 | """ 32 | @external 33 | def test_func() -> int128: 34 | return (1, 2) 35 | """, 36 | """ 37 | @external 38 | def __init__(a: int128 = 12): 39 | pass 40 | """, 41 | """ 42 | @external 43 | def __init__() -> uint256: 44 | return 1 45 | """, 46 | """ 47 | @external 48 | def __init__() -> bool: 49 | pass 50 | """, 51 | """ 52 | a: immutable(uint256) 53 | 54 | @internal 55 | def __init__(): 56 | a = 1 57 | """, 58 | """ 59 | a: immutable(uint256) 60 | 61 | @external 62 | @pure 63 | def __init__(): 64 | a = 1 65 | """, 66 | """ 67 | a: immutable(uint256) 68 | 69 | @external 70 | @view 71 | def __init__(): 72 | a = 1 73 | """, 74 | ] 75 | 76 | 77 | @pytest.mark.parametrize("bad_code", fail_list) 78 | def test_function_declaration_exception(bad_code): 79 | with pytest.raises(FunctionDeclarationException): 80 | compiler.compile_code(bad_code) 81 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_instantiation_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import InstantiationException 4 | 5 | invalid_list = [ 6 | """ 7 | event Foo: 8 | a: uint256 9 | 10 | @external 11 | def foo() -> Foo: 12 | return Foo(2) 13 | """, 14 | """ 15 | event Foo: 16 | a: uint256 17 | 18 | @external 19 | def foo() -> (uint256, Foo): 20 | return 1, Foo(2) 21 | """, 22 | """ 23 | a: HashMap[uint256, uint256] 24 | 25 | @external 26 | def foo() -> HashMap[uint256, uint256]: 27 | return self.a 28 | """, 29 | """ 30 | event Foo: 31 | a: uint256 32 | 33 | @external 34 | def foo(x: Foo): 35 | pass 36 | """, 37 | """ 38 | @external 39 | def foo(x: HashMap[uint256, uint256]): 40 | pass 41 | """, 42 | """ 43 | event Foo: 44 | a: uint256 45 | 46 | foo: Foo 47 | """, 48 | """ 49 | event Foo: 50 | a: uint256 51 | 52 | @external 53 | def foo(): 54 | f: Foo = Foo(1) 55 | pass 56 | """, 57 | """ 58 | event Foo: 59 | a: uint256 60 | 61 | b: HashMap[uint256, Foo] 62 | """, 63 | """ 64 | event Foo: 65 | a: uint256 66 | 67 | b: HashMap[Foo, uint256] 68 | """, 69 | """ 70 | b: immutable(HashMap[uint256, uint256]) 71 | 72 | @external 73 | def __init__(): 74 | b = empty(HashMap[uint256, uint256]) 75 | """, 76 | ] 77 | 78 | 79 | @pytest.mark.parametrize("bad_code", invalid_list) 80 | def test_instantiation_exception(bad_code, get_contract, assert_compile_failed): 81 | assert_compile_failed(lambda: get_contract(bad_code), InstantiationException) 82 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_invalid_literal_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import raises 3 | 4 | from vyper import compiler 5 | from vyper.exceptions import InvalidLiteral 6 | 7 | fail_list = [ 8 | """ 9 | b: decimal 10 | @external 11 | def foo(): 12 | self.b = 7.5178246872145875217495129745982164981654986129846 13 | """, 14 | """ 15 | @external 16 | def foo(): 17 | x: uint256 = convert(-(-(-1)), uint256) 18 | """, 19 | """ 20 | @external 21 | def foo(x: int128): 22 | y: int128 = 7 23 | for i in range(x, x + y): 24 | pass 25 | """, 26 | """ 27 | @external 28 | def foo(): 29 | x: String[100] = "these bytes are nо gооd because the o's are from the Russian alphabet" 30 | """, 31 | """ 32 | @external 33 | def foo(): 34 | x: String[100] = "这个傻老外不懂中文" 35 | """, 36 | """ 37 | @external 38 | def foo(): 39 | a: Bytes[100] = "ѓtest" 40 | """, 41 | """ 42 | @external 43 | def foo(): 44 | a: bytes32 = keccak256("ѓtest") 45 | """, 46 | ] 47 | 48 | 49 | @pytest.mark.parametrize("bad_code", fail_list) 50 | def test_invalid_literal_exception(bad_code): 51 | with raises(InvalidLiteral): 52 | compiler.compile_code(bad_code) 53 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_invalid_payable.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import raises 3 | 4 | from vyper import compiler 5 | from vyper.exceptions import NonPayableViolation 6 | 7 | fail_list = [ 8 | """ 9 | @external 10 | def foo(): 11 | x: uint256 = msg.value 12 | """ 13 | ] 14 | 15 | 16 | @pytest.mark.parametrize("bad_code", fail_list) 17 | def test_variable_decleration_exception(bad_code): 18 | with raises(NonPayableViolation): 19 | compiler.compile_code(bad_code) 20 | 21 | 22 | valid_list = [ 23 | """ 24 | x: int128 25 | @external 26 | @payable 27 | def foo() -> int128: 28 | self.x = 5 29 | return self.x 30 | """, 31 | """ 32 | @external 33 | @payable 34 | def foo(): 35 | x: uint256 = msg.value 36 | """, 37 | ] 38 | 39 | 40 | @pytest.mark.parametrize("good_code", valid_list) 41 | def test_block_success(good_code): 42 | assert compiler.compile_code(good_code) is not None 43 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_invalid_reference.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidReference 5 | 6 | fail_list = [ 7 | """ 8 | x: uint256 9 | 10 | @external 11 | def foo(): 12 | send(0x1234567890123456789012345678901234567890, x) 13 | """, 14 | """ 15 | @external 16 | def bar(x: int128) -> int128: 17 | return 3 * x 18 | 19 | @external 20 | def foo() -> int128: 21 | return bar(20) 22 | """, 23 | """ 24 | b: int128 25 | @external 26 | def foo(): 27 | b = 7 28 | """, 29 | """ 30 | x: int128 31 | @external 32 | def foo(): 33 | x = 5 34 | """, 35 | """ 36 | @external 37 | def foo(): 38 | int128 = 5 39 | """, 40 | """ 41 | a: public(constant(uint256)) = 1 42 | 43 | @external 44 | def foo(): 45 | b: uint256 = self.a 46 | """, 47 | """ 48 | a: public(immutable(uint256)) 49 | 50 | @external 51 | def __init__(): 52 | a = 123 53 | 54 | @external 55 | def foo(): 56 | b: uint256 = self.a 57 | """, 58 | ] 59 | 60 | 61 | @pytest.mark.parametrize("bad_code", fail_list) 62 | def test_invalid_reference_exception(bad_code): 63 | with pytest.raises(InvalidReference): 64 | compiler.compile_code(bad_code) 65 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_invalid_type_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import InvalidType, UnknownType 4 | 5 | fail_list = [ 6 | """ 7 | x: bat 8 | """, 9 | """ 10 | x: HashMap[int, int128] 11 | """, 12 | """ 13 | struct A: 14 | b: B 15 | """, 16 | ] 17 | 18 | 19 | @pytest.mark.parametrize("bad_code", fail_list) 20 | def test_unknown_type_exception(bad_code, get_contract, assert_compile_failed): 21 | assert_compile_failed(lambda: get_contract(bad_code), UnknownType) 22 | 23 | 24 | invalid_list = [ 25 | """ 26 | @external 27 | def foo(): 28 | raw_log(b"cow", b"dog") 29 | """, 30 | """ 31 | @external 32 | def foo(): 33 | xs: uint256[1] = [] 34 | """, 35 | # Must be a literal string. 36 | """ 37 | @external 38 | def mint(_to: address, _value: uint256): 39 | assert msg.sender == self,msg.sender 40 | """, 41 | # literal longer than event member 42 | """ 43 | event Foo: 44 | message: String[1] 45 | @external 46 | def foo(): 47 | log Foo("abcd") 48 | """, 49 | # Raise reason must be string 50 | """ 51 | @external 52 | def mint(_to: address, _value: uint256): 53 | raise 1 54 | """, 55 | """ 56 | x: int128[3.5] 57 | """, 58 | # Key of mapping must be a base type 59 | """ 60 | b: HashMap[(int128, decimal), int128] 61 | """, 62 | # Address literal must be checksummed 63 | """ 64 | a: constant(address) = 0x3cd751e6b0078be393132286c442345e5dc49699 65 | """, 66 | """ 67 | x: String <= 33 68 | """, 69 | """ 70 | x: Bytes <= wei 71 | """, 72 | """ 73 | x: 5 74 | """, 75 | ] 76 | 77 | 78 | @pytest.mark.parametrize("bad_code", invalid_list) 79 | def test_invalid_type_exception(bad_code, get_contract, assert_compile_failed): 80 | assert_compile_failed(lambda: get_contract(bad_code), InvalidType) 81 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_namespace_collision.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import NamespaceCollision 5 | 6 | fail_list = [ 7 | """ 8 | @external 9 | def foo(int128: int128): 10 | pass 11 | """, 12 | """ 13 | @external 14 | def foo(): 15 | x: int128 = 12 16 | @external 17 | def foo(): 18 | y: int128 = 12 19 | """, 20 | """ 21 | foo: int128 22 | 23 | @external 24 | def foo(): 25 | pass 26 | """, 27 | """ 28 | x: int128 29 | x: int128 30 | """, 31 | """ 32 | @external 33 | def foo(): 34 | x: int128 = 0 35 | x: int128 = 0 36 | """, 37 | """ 38 | @external 39 | def foo(): 40 | msg: bool = True 41 | """, 42 | """ 43 | int128: Bytes[3] 44 | """, 45 | ] 46 | 47 | 48 | @pytest.mark.parametrize("bad_code", fail_list) 49 | def test_insufficient_arguments(bad_code): 50 | with pytest.raises(NamespaceCollision): 51 | compiler.compile_code(bad_code) 52 | 53 | 54 | pass_list = [ 55 | """ 56 | x: int128 57 | 58 | @external 59 | def foo(x: int128): pass 60 | """, 61 | """ 62 | x: int128 63 | 64 | @external 65 | def foo(): 66 | x: int128 = 1234 67 | """, 68 | ] 69 | 70 | 71 | @pytest.mark.parametrize("code", pass_list) 72 | def test_valid(code): 73 | compiler.compile_code(code) 74 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_overflow_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import OverflowException 5 | 6 | fail_list = [ 7 | """ 8 | @external 9 | def foo(): 10 | x: int256 = -57896044618658097711785492504343953926634992332820282019728792003956564819969 # -2**255 - 1 # noqa: E501 11 | """, 12 | """ 13 | @external 14 | def foo(): 15 | x: decimal = 18707220957835557353007165858768422651595.9365500928 16 | """, 17 | """ 18 | @external 19 | def foo(): 20 | x: decimal = -18707220957835557353007165858768422651595.9365500929 21 | """, 22 | """ 23 | @external 24 | def foo(): 25 | x: uint256 = convert(821649876217461872458712528745872158745214187264875632587324658732648753245328764872135671285218762145, uint256) # noqa: E501 26 | """, 27 | """ 28 | @external 29 | def overflow2() -> uint256: 30 | a: uint256 = 2**256 31 | return a 32 | """, 33 | ] 34 | 35 | 36 | @pytest.mark.parametrize("bad_code", fail_list) 37 | def test_invalid_literal_exception(bad_code): 38 | with pytest.raises(OverflowException): 39 | compiler.compile_code(bad_code) 40 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_syntax_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import SyntaxException 4 | 5 | fail_list = [ 6 | """ 7 | x: Bytes[1:3] 8 | """, 9 | """ 10 | b: int128[int128: address] 11 | """, 12 | """ 13 | x: int128[5] 14 | @external 15 | def foo(): 16 | self.x[2:4] = 3 17 | """, 18 | """ 19 | x: int128[5] 20 | @external 21 | def foo(): 22 | z = self.x[2:4] 23 | """, 24 | """ 25 | @external 26 | def foo(): 27 | x: int128[5] 28 | z = x[2:4] 29 | """, 30 | """ 31 | Transfer: event({_rom&: indexed(address)}) 32 | """, 33 | """ 34 | @external 35 | def test() -> uint256: 36 | for i in range(0, 4): 37 | return 0 38 | else: 39 | return 1 40 | return 1 41 | """, 42 | """ 43 | @external 44 | def foo(): 45 | x = y = 3 46 | """, 47 | """ 48 | @external 49 | def foo(): 50 | x: address = create_minimal_proxy_to(0x123456789012345678901234567890123456789) 51 | """, 52 | """ 53 | @external 54 | def foo(): 55 | x: Bytes[4] = raw_call(0x123456789012345678901234567890123456789, "cow", max_outsize=4) 56 | """, 57 | """ 58 | @external 59 | def foo(): 60 | x: address = 0x12345678901234567890123456789012345678901 61 | """, 62 | """ 63 | @external 64 | def foo(): 65 | x: address = 0x01234567890123456789012345678901234567890 66 | """, 67 | """ 68 | @external 69 | def foo(): 70 | x: address = 0x123456789012345678901234567890123456789 71 | """, 72 | """ 73 | a: internal(uint256) 74 | """, 75 | """ 76 | @external 77 | def foo(): 78 | x: uint256 = +1 # test UAdd ast blocked 79 | """, 80 | """ 81 | @internal 82 | def f(a:uint256,/): # test posonlyargs blocked 83 | return 84 | 85 | @external 86 | def g(): 87 | self.f() 88 | """, 89 | ] 90 | 91 | 92 | @pytest.mark.parametrize("bad_code", fail_list) 93 | def test_syntax_exception(assert_compile_failed, get_contract, bad_code): 94 | assert_compile_failed(lambda: get_contract(bad_code), SyntaxException) 95 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_type_mismatch_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import raises 3 | 4 | from vyper import compiler 5 | from vyper.exceptions import TypeMismatch 6 | 7 | fail_list = [ 8 | """ 9 | @external 10 | def foo(): 11 | a: uint256 = 3 12 | b: int128 = 4 13 | c: uint256 = min(a, b) 14 | """, 15 | """ 16 | @external 17 | def broken(): 18 | a : uint256 = 3 19 | b : int128 = 4 20 | c : uint256 = unsafe_add(a, b) 21 | """, 22 | """ 23 | @external 24 | def foo(): 25 | b: Bytes[1] = b"\x05" 26 | x: uint256 = as_wei_value(b, "babbage") 27 | """, 28 | ] 29 | 30 | 31 | @pytest.mark.parametrize("bad_code", fail_list) 32 | def test_type_mismatch_exception(bad_code): 33 | with raises(TypeMismatch): 34 | compiler.compile_code(bad_code) 35 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_undeclared_definition.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import UndeclaredDefinition 5 | 6 | fail_list = [ 7 | """ 8 | @external 9 | def test1(b: uint256) -> uint256: 10 | a: uint256 = a + b 11 | return a 12 | """, 13 | """ 14 | @external 15 | def test2(b: uint256, c: uint256) -> uint256: 16 | a: uint256 = a + b + c 17 | return a 18 | """, 19 | """ 20 | @external 21 | def test3(b: int128, c: int128) -> int128: 22 | a: int128 = - a 23 | return a 24 | """, 25 | """ 26 | @external 27 | def test4(b: bool) -> bool: 28 | a: bool = b or a 29 | return a 30 | """, 31 | """ 32 | @external 33 | def test5(b: bool) -> bool: 34 | a: bool = a != b 35 | return a 36 | """, 37 | """ 38 | @external 39 | def test6(b:bool, c: bool) -> bool: 40 | a: bool = (a and b) and c 41 | return a 42 | """, 43 | """ 44 | @external 45 | def foo(): 46 | throe = 2 47 | """, 48 | """ 49 | @external 50 | def foo(): 51 | x: int128 = bar(55) 52 | """, 53 | """ 54 | @external 55 | def foo(): 56 | x = 5 57 | x: int128 = 0 58 | """, 59 | """ 60 | @external 61 | def foo(): 62 | bork = zork 63 | """, 64 | ] 65 | 66 | 67 | @pytest.mark.parametrize("bad_code", fail_list) 68 | def test_undeclared_def_exception(bad_code): 69 | with pytest.raises(UndeclaredDefinition): 70 | compiler.compile_code(bad_code) 71 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_variable_declaration_exception.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import VariableDeclarationException 5 | 6 | fail_list = [ 7 | """ 8 | q: int128 = 12 9 | @external 10 | def foo() -> int128: 11 | return self.q 12 | """, 13 | """ 14 | struct S: 15 | x: int128 16 | s: S = S({x: int128}, 1) 17 | """, 18 | """ 19 | struct S: 20 | x: int128 21 | s: S = S() 22 | """, 23 | """ 24 | foo.a: int128 25 | """, 26 | """ 27 | @external 28 | def foo(): 29 | bar.x: int128 = 0 30 | """, 31 | ] 32 | 33 | 34 | @pytest.mark.parametrize("bad_code", fail_list) 35 | def test_variable_declaration_exception(bad_code): 36 | with pytest.raises(VariableDeclarationException): 37 | compiler.compile_code(bad_code) 38 | -------------------------------------------------------------------------------- /tests/parser/exceptions/test_vyper_exception_pos.py: -------------------------------------------------------------------------------- 1 | from pytest import raises 2 | 3 | from vyper.exceptions import VyperException 4 | 5 | 6 | def test_type_exception_pos(): 7 | pos = (1, 2) 8 | 9 | with raises(VyperException) as e: 10 | raise VyperException("Fail!", pos) 11 | 12 | assert e.value.lineno == 1 13 | assert e.value.col_offset == 2 14 | assert str(e.value) == "line 1:2 Fail!" 15 | 16 | 17 | # multiple exceptions in file 18 | def test_multiple_exceptions(get_contract, assert_compile_failed): 19 | code = """ 20 | struct A: 21 | b: B # unknown type 22 | 23 | foo: immutable(uint256) 24 | bar: immutable(uint256) 25 | @external 26 | def __init__(): 27 | self.foo = 1 # SyntaxException 28 | self.bar = 2 # SyntaxException 29 | 30 | """ 31 | assert_compile_failed(lambda: get_contract(code), VyperException) 32 | -------------------------------------------------------------------------------- /tests/parser/features/arithmetic/test_division.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import ZeroDivisionException 4 | 5 | BAD_CODE = [ 6 | """ 7 | @external 8 | def foo() -> uint256: 9 | return 2 / 0 10 | """, 11 | """ 12 | @external 13 | def foo() -> int128: 14 | return -2 / 0 15 | """, 16 | """ 17 | @external 18 | def foo() -> decimal: 19 | return 2.22 / 0.0 20 | """, 21 | """ 22 | @external 23 | def foo(a: uint256) -> uint256: 24 | return a / 0 25 | """, 26 | """ 27 | @external 28 | def foo(a: int128) -> int128: 29 | return a / 0 30 | """, 31 | """ 32 | @external 33 | def foo(a: decimal) -> decimal: 34 | return a / 0.0 35 | """, 36 | ] 37 | 38 | 39 | @pytest.mark.parametrize("code", BAD_CODE) 40 | def test_divide_by_zero(code, assert_compile_failed, get_contract): 41 | assert_compile_failed(lambda: get_contract(code), ZeroDivisionException) 42 | -------------------------------------------------------------------------------- /tests/parser/features/decorators/test_public.py: -------------------------------------------------------------------------------- 1 | from vyper.exceptions import FunctionDeclarationException 2 | 3 | 4 | def test_invalid_if_both_public_and_internal( 5 | assert_compile_failed, get_contract_with_gas_estimation 6 | ): 7 | code = """ 8 | @external 9 | @internal 10 | def foo(): 11 | x: uint256 = 1 12 | """ 13 | 14 | assert_compile_failed( 15 | lambda: get_contract_with_gas_estimation(code), FunctionDeclarationException 16 | ) 17 | 18 | 19 | def test_invalid_if_visibility_isnt_declared( 20 | assert_compile_failed, get_contract_with_gas_estimation 21 | ): 22 | code = """ 23 | def foo(): 24 | x: uint256 = 1 25 | """ 26 | 27 | assert_compile_failed( 28 | lambda: get_contract_with_gas_estimation(code), FunctionDeclarationException 29 | ) 30 | -------------------------------------------------------------------------------- /tests/parser/features/decorators/test_view.py: -------------------------------------------------------------------------------- 1 | from vyper.exceptions import FunctionDeclarationException 2 | 3 | 4 | def test_constant_test(get_contract_with_gas_estimation_for_constants): 5 | constant_test = """ 6 | @external 7 | @view 8 | def foo() -> int128: 9 | return 5 10 | """ 11 | 12 | c = get_contract_with_gas_estimation_for_constants(constant_test) 13 | assert c.foo() == 5 14 | 15 | print("Passed constant function test") 16 | 17 | 18 | def test_invalid_constant_and_payable( 19 | get_contract_with_gas_estimation_for_constants, assert_compile_failed 20 | ): 21 | code = """ 22 | @external 23 | @payable 24 | @view 25 | def foo() -> num: 26 | return 5 27 | """ 28 | assert_compile_failed( 29 | lambda: get_contract_with_gas_estimation_for_constants(code), FunctionDeclarationException 30 | ) 31 | -------------------------------------------------------------------------------- /tests/parser/features/external_contracts/test_self_call_struct.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | 3 | 4 | def test_call_to_self_struct(w3, get_contract): 5 | code = """ 6 | struct MyStruct: 7 | e1: decimal 8 | e2: uint256 9 | 10 | @internal 11 | @view 12 | def get_my_struct(_e1: decimal, _e2: uint256) -> MyStruct: 13 | return MyStruct({e1: _e1, e2: _e2}) 14 | 15 | @external 16 | @view 17 | def wrap_get_my_struct_WORKING(_e1: decimal) -> MyStruct: 18 | testing: MyStruct = self.get_my_struct(_e1, block.timestamp) 19 | return testing 20 | 21 | @external 22 | @view 23 | def wrap_get_my_struct_BROKEN(_e1: decimal) -> MyStruct: 24 | return self.get_my_struct(_e1, block.timestamp) 25 | """ 26 | c = get_contract(code) 27 | assert c.wrap_get_my_struct_WORKING(Decimal("0.1")) == ( 28 | Decimal("0.1"), 29 | w3.eth.get_block(w3.eth.block_number)["timestamp"], 30 | ) 31 | assert c.wrap_get_my_struct_BROKEN(Decimal("0.1")) == ( 32 | Decimal("0.1"), 33 | w3.eth.get_block(w3.eth.block_number)["timestamp"], 34 | ) 35 | 36 | 37 | def test_call_to_self_struct_2(get_contract): 38 | code = """ 39 | struct MyStruct: 40 | e1: decimal 41 | 42 | @internal 43 | @view 44 | def get_my_struct(_e1: decimal) -> MyStruct: 45 | return MyStruct({e1: _e1}) 46 | 47 | @external 48 | @view 49 | def wrap_get_my_struct_WORKING(_e1: decimal) -> MyStruct: 50 | testing: MyStruct = self.get_my_struct(_e1) 51 | return testing 52 | 53 | @external 54 | @view 55 | def wrap_get_my_struct_BROKEN(_e1: decimal) -> MyStruct: 56 | return self.get_my_struct(_e1) 57 | """ 58 | c = get_contract(code) 59 | assert c.wrap_get_my_struct_WORKING(Decimal("0.1")) == (Decimal("0.1"),) 60 | assert c.wrap_get_my_struct_BROKEN(Decimal("0.1")) == (Decimal("0.1"),) 61 | -------------------------------------------------------------------------------- /tests/parser/features/test_address_balance.py: -------------------------------------------------------------------------------- 1 | def test_constant_address_balance(w3, get_contract_with_gas_estimation): 2 | code = """ 3 | a: constant(address) = 0x776Ba14735FF84789320718cf0aa43e91F7A8Ce1 4 | 5 | @external 6 | def foo() -> uint256: 7 | x: uint256 = a.balance 8 | return x 9 | """ 10 | address = "0x776Ba14735FF84789320718cf0aa43e91F7A8Ce1" 11 | 12 | c = get_contract_with_gas_estimation(code) 13 | 14 | assert c.foo() == 0 15 | 16 | w3.eth.send_transaction({"to": address, "value": 1337}) 17 | 18 | assert c.foo() == 1337 19 | -------------------------------------------------------------------------------- /tests/parser/features/test_assert_unreachable.py: -------------------------------------------------------------------------------- 1 | def test_unreachable_refund(w3, get_contract): 2 | code = """ 3 | @external 4 | def foo(): 5 | assert msg.sender != msg.sender, UNREACHABLE 6 | """ 7 | 8 | c = get_contract(code) 9 | a0 = w3.eth.accounts[0] 10 | gas_sent = 10**6 11 | tx_hash = c.foo(transact={"from": a0, "gas": gas_sent, "gasPrice": 10}) 12 | tx_receipt = w3.eth.get_transaction_receipt(tx_hash) 13 | 14 | assert tx_receipt["status"] == 0 15 | assert tx_receipt["gasUsed"] == gas_sent # Drains all gains sent 16 | 17 | 18 | def test_basic_unreachable(w3, get_contract, assert_tx_failed): 19 | code = """ 20 | @external 21 | def foo(val: int128) -> bool: 22 | assert val > 0, UNREACHABLE 23 | assert val == 2, UNREACHABLE 24 | return True 25 | """ 26 | 27 | c = get_contract(code) 28 | 29 | assert c.foo(2) is True 30 | 31 | assert_tx_failed(lambda: c.foo(1), exc_text="Invalid opcode 0xfe") 32 | assert_tx_failed(lambda: c.foo(-1), exc_text="Invalid opcode 0xfe") 33 | assert_tx_failed(lambda: c.foo(-2), exc_text="Invalid opcode 0xfe") 34 | 35 | 36 | def test_basic_call_unreachable(w3, get_contract, assert_tx_failed): 37 | code = """ 38 | 39 | @view 40 | @internal 41 | def _test_me(val: int128) -> bool: 42 | return val == 33 43 | 44 | @external 45 | def foo(val: int128) -> int128: 46 | assert self._test_me(val), UNREACHABLE 47 | return -123 48 | """ 49 | 50 | c = get_contract(code) 51 | 52 | assert c.foo(33) == -123 53 | 54 | assert_tx_failed(lambda: c.foo(1), exc_text="Invalid opcode 0xfe") 55 | assert_tx_failed(lambda: c.foo(-1), exc_text="Invalid opcode 0xfe") 56 | 57 | 58 | def test_raise_unreachable(w3, get_contract, assert_tx_failed): 59 | code = """ 60 | @external 61 | def foo(): 62 | raise UNREACHABLE 63 | """ 64 | 65 | c = get_contract(code) 66 | 67 | assert_tx_failed(lambda: c.foo(), exc_text="Invalid opcode 0xfe") 68 | -------------------------------------------------------------------------------- /tests/parser/features/test_comments.py: -------------------------------------------------------------------------------- 1 | def test_comment_test(get_contract_with_gas_estimation): 2 | comment_test = """ 3 | @external 4 | def foo() -> int128: 5 | # Returns 3 6 | return 3 7 | """ 8 | 9 | c = get_contract_with_gas_estimation(comment_test) 10 | assert c.foo() == 3 11 | print("Passed comment test") 12 | -------------------------------------------------------------------------------- /tests/parser/features/test_comparison.py: -------------------------------------------------------------------------------- 1 | # test syntactic comparisons 2 | # most tests under tests/ast/nodes/test_evaluate_compare.py 3 | import pytest 4 | 5 | 6 | def test_3034_verbatim(get_contract): 7 | # test GH issue 3034 exactly 8 | code = """ 9 | @view 10 | @external 11 | def showError(): 12 | adr1: address = 0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20 13 | adr2: address = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 14 | adr3: address = 0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20 15 | assert adr1 in [adr2,adr3], "error in comparison with in statement!" 16 | """ 17 | c = get_contract(code) 18 | c.showError() 19 | 20 | 21 | @pytest.mark.parametrize("invert", (True, False)) 22 | def test_in_list(get_contract, invert): 23 | # test slightly more complicated variations of #3034 24 | INVERT = "not" if invert else "" 25 | code = f""" 26 | SOME_ADDRESS: constant(address) = 0x22cb70ba2EC32347D9e32740fc14b2f3d038Ce8E 27 | @view 28 | @external 29 | def test_in(addr: address) -> bool: 30 | x: address = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 31 | y: address = 0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20 32 | # in list which 33 | return addr {INVERT} in [x, y, SOME_ADDRESS] 34 | """ 35 | c = get_contract(code) 36 | should_in = [ 37 | "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", 38 | "0xFbEEa1C75E4c4465CB2FCCc9c6d6afe984558E20", 39 | "0x22cb70ba2EC32347D9e32740fc14b2f3d038Ce8E", 40 | ] 41 | should_not_in = [ 42 | "0x" + "00" * 20, 43 | "0xfBeeA1C75E4C4465CB2fccC9C6d6AFe984558e21", # y but last bit flipped 44 | ] 45 | for t in should_in: 46 | assert c.test_in(t) is (True if not invert else False) 47 | for t in should_not_in: 48 | assert c.test_in(t) is (True if invert else False) 49 | -------------------------------------------------------------------------------- /tests/parser/features/test_conditionals.py: -------------------------------------------------------------------------------- 1 | def test_conditional_return_code(get_contract_with_gas_estimation): 2 | conditional_return_code = """ 3 | @external 4 | def foo(i: bool) -> int128: 5 | if i: 6 | return 5 7 | else: 8 | assert 2 != 0 9 | return 7 10 | return 11 11 | """ 12 | 13 | c = get_contract_with_gas_estimation(conditional_return_code) 14 | assert c.foo(True) == 5 15 | assert c.foo(False) == 7 16 | 17 | print("Passed conditional return tests") 18 | 19 | 20 | def test_single_branch_underflow_public(get_contract_with_gas_estimation): 21 | code = """ 22 | @external 23 | def doit(): 24 | if False: 25 | raw_call(msg.sender, b"", max_outsize=0, value=0, gas=msg.gas) 26 | """ 27 | c = get_contract_with_gas_estimation(code) 28 | c.doit() 29 | 30 | 31 | def test_single_branch_underflow_private(get_contract_with_gas_estimation): 32 | code = """ 33 | @internal 34 | def priv() -> uint256: 35 | return 1 36 | 37 | @external 38 | def dont_doit(): 39 | if False: 40 | self.priv() 41 | """ 42 | c = get_contract_with_gas_estimation(code) 43 | c.dont_doit() 44 | -------------------------------------------------------------------------------- /tests/parser/features/test_gas.py: -------------------------------------------------------------------------------- 1 | def test_gas_call(get_contract_with_gas_estimation): 2 | gas_call = """ 3 | @external 4 | def foo() -> uint256: 5 | return msg.gas 6 | """ 7 | 8 | c = get_contract_with_gas_estimation(gas_call) 9 | 10 | assert c.foo(call={"gas": 50000}) < 50000 11 | assert c.foo(call={"gas": 50000}) > 25000 12 | -------------------------------------------------------------------------------- /tests/parser/features/test_init.py: -------------------------------------------------------------------------------- 1 | import vyper 2 | 3 | 4 | def test_basic_init_function(get_contract): 5 | code = """ 6 | val: public(uint256) 7 | 8 | @external 9 | def __init__(a: uint256): 10 | self.val = a 11 | """ 12 | 13 | c = get_contract(code, *[123]) 14 | 15 | assert c.val() == 123 16 | 17 | # Make sure the init code does not access calldata 18 | assembly = vyper.compile_code(code, ["asm"])["asm"].split(" ") 19 | ir_return_idx_start = assembly.index("{") 20 | ir_return_idx_end = assembly.index("}") 21 | 22 | assert "CALLDATALOAD" in assembly 23 | assert "CALLDATACOPY" not in assembly[:ir_return_idx_start] + assembly[ir_return_idx_end:] 24 | assert "CALLDATALOAD" not in assembly[:ir_return_idx_start] + assembly[ir_return_idx_end:] 25 | 26 | 27 | def test_init_calls_internal(get_contract, assert_compile_failed, assert_tx_failed): 28 | code = """ 29 | foo: public(uint8) 30 | @internal 31 | def bar(x: uint256) -> uint8: 32 | return convert(x, uint8) * 7 33 | @external 34 | def __init__(a: uint256): 35 | self.foo = self.bar(a) 36 | 37 | @external 38 | def baz() -> uint8: 39 | return self.bar(convert(self.foo, uint256)) 40 | """ 41 | n = 5 42 | c = get_contract(code, n) 43 | assert c.foo() == n * 7 44 | assert c.baz() == 245 # 5*7*7 45 | 46 | n = 6 47 | c = get_contract(code, n) 48 | assert c.foo() == n * 7 49 | assert_tx_failed(lambda: c.baz()) 50 | 51 | n = 255 52 | assert_compile_failed(lambda: get_contract(code, n)) 53 | 54 | n = 256 55 | assert_compile_failed(lambda: get_contract(code, n)) 56 | 57 | 58 | # GH issue 3206 59 | def test_nested_internal_call_from_ctor(get_contract): 60 | code = """ 61 | x: uint256 62 | 63 | @external 64 | def __init__(): 65 | self.a() 66 | 67 | @internal 68 | def a(): 69 | self.x += 1 70 | self.b() 71 | 72 | @internal 73 | def b(): 74 | self.x += 2 75 | 76 | @external 77 | def test() -> uint256: 78 | return self.x 79 | """ 80 | c = get_contract(code) 81 | assert c.test() == 3 82 | -------------------------------------------------------------------------------- /tests/parser/features/test_memory_dealloc.py: -------------------------------------------------------------------------------- 1 | def test_memory_deallocation(get_contract): 2 | code = """ 3 | event Shimmy: 4 | a: indexed(address) 5 | b: uint256 6 | 7 | interface Other: 8 | def sendit(): nonpayable 9 | 10 | @external 11 | def foo(target: address) -> uint256[2]: 12 | log Shimmy(ZERO_ADDRESS, 3) 13 | amount: uint256 = 1 14 | flargen: uint256 = 42 15 | Other(target).sendit() 16 | return [amount, flargen] 17 | """ 18 | 19 | code2 = """ 20 | 21 | @external 22 | def sendit() -> bool: 23 | return True 24 | """ 25 | 26 | c = get_contract(code) 27 | c2 = get_contract(code2) 28 | 29 | assert c.foo(c2.address) == [1, 42] 30 | -------------------------------------------------------------------------------- /tests/parser/features/test_packing.py: -------------------------------------------------------------------------------- 1 | def test_packing_test(get_contract_with_gas_estimation, memory_mocker): 2 | packing_test = """ 3 | struct Bar: 4 | a: int128 5 | b: int128 6 | struct Z: 7 | foo: int128[3] 8 | bar: Bar[2] 9 | x: int128 10 | y: int128[5] 11 | z: Z 12 | a: int128 13 | 14 | @external 15 | def foo() -> int128: 16 | self.x = 1 17 | self.y[0] = 2 18 | self.y[4] = 4 19 | self.z.foo[0] = 8 20 | self.z.foo[2] = 16 21 | self.z.bar[0].a = 32 22 | self.z.bar[0].b = 64 23 | self.z.bar[1].a = 128 24 | self.z.bar[1].b = 256 25 | self.a = 512 26 | return self.x + self.y[0] + self.y[4] + self.z.foo[0] + self.z.foo[2] + \ 27 | self.z.bar[0].a + self.z.bar[0].b + self.z.bar[1].a + self.z.bar[1].b + self.a 28 | 29 | @external 30 | def fop() -> int128: 31 | _x: int128 = 0 32 | _y: int128[5] = [0, 0, 0, 0, 0] 33 | _z: Z = Z({foo: [0, 0, 0], bar: [Bar({a: 0, b: 0}), Bar({a: 0, b: 0})]}) 34 | _a: int128 = 0 35 | _x = 1 36 | _y[0] = 2 37 | _y[4] = 4 38 | _z.foo[0] = 8 39 | _z.foo[2] = 16 40 | _z.bar[0].a = 32 41 | _z.bar[0].b = 64 42 | _z.bar[1].a = 128 43 | _z.bar[1].b = 256 44 | _a = 512 45 | return _x + _y[0] + _y[4] + _z.foo[0] + _z.foo[2] + \ 46 | _z.bar[0].a + _z.bar[0].b + _z.bar[1].a + _z.bar[1].b + _a 47 | """ 48 | 49 | c = get_contract_with_gas_estimation(packing_test) 50 | assert c.foo() == 1023, c.foo() 51 | assert c.fop() == 1023, c.fop() 52 | print("Passed packing test") 53 | -------------------------------------------------------------------------------- /tests/parser/features/test_reverting.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from eth.codecs import abi 3 | from eth_tester.exceptions import TransactionFailed 4 | 5 | from vyper.utils import method_id 6 | 7 | pytestmark = pytest.mark.usefixtures("memory_mocker") 8 | 9 | 10 | def test_revert_reason(w3, assert_tx_failed, get_contract_with_gas_estimation): 11 | reverty_code = """ 12 | @external 13 | def foo(): 14 | data: Bytes[4] = method_id("NoFives()") 15 | raw_revert(data) 16 | """ 17 | 18 | revert_bytes = method_id("NoFives()") 19 | 20 | assert_tx_failed( 21 | lambda: get_contract_with_gas_estimation(reverty_code).foo(transact={}), 22 | TransactionFailed, 23 | exc_text=f"execution reverted: {revert_bytes}", 24 | ) 25 | 26 | 27 | def test_revert_reason_typed(w3, assert_tx_failed, get_contract_with_gas_estimation): 28 | reverty_code = """ 29 | @external 30 | def foo(): 31 | val: uint256 = 5 32 | data: Bytes[100] = _abi_encode(val, method_id=method_id("NoFives(uint256)")) 33 | raw_revert(data) 34 | """ 35 | 36 | revert_bytes = method_id("NoFives(uint256)") + abi.encode("(uint256)", (5,)) 37 | 38 | assert_tx_failed( 39 | lambda: get_contract_with_gas_estimation(reverty_code).foo(transact={}), 40 | TransactionFailed, 41 | exc_text=f"execution reverted: {revert_bytes}", 42 | ) 43 | 44 | 45 | def test_revert_reason_typed_no_variable(w3, assert_tx_failed, get_contract_with_gas_estimation): 46 | reverty_code = """ 47 | @external 48 | def foo(): 49 | val: uint256 = 5 50 | raw_revert(_abi_encode(val, method_id=method_id("NoFives(uint256)"))) 51 | """ 52 | 53 | revert_bytes = method_id("NoFives(uint256)") + abi.encode("(uint256)", (5,)) 54 | 55 | assert_tx_failed( 56 | lambda: get_contract_with_gas_estimation(reverty_code).foo(transact={}), 57 | TransactionFailed, 58 | exc_text=f"execution reverted: {revert_bytes}", 59 | ) 60 | -------------------------------------------------------------------------------- /tests/parser/features/test_string_map_keys.py: -------------------------------------------------------------------------------- 1 | def test_string_map_keys(get_contract): 2 | code = """ 3 | f:HashMap[String[1], bool] 4 | @external 5 | def test() -> bool: 6 | a:String[1] = "a" 7 | b:String[1] = "b" 8 | self.f[a] = True 9 | return self.f[b] # should return False 10 | """ 11 | c = get_contract(code) 12 | c.test() 13 | assert c.test() is False 14 | 15 | 16 | def test_string_map_keys_literals(get_contract): 17 | code = """ 18 | f:HashMap[String[1], bool] 19 | @external 20 | def test() -> bool: 21 | self.f["a"] = True 22 | return self.f["b"] # should return False 23 | """ 24 | c = get_contract(code) 25 | assert c.test() is False 26 | -------------------------------------------------------------------------------- /tests/parser/functions/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/tests/parser/functions/__init__.py -------------------------------------------------------------------------------- /tests/parser/functions/test_as_wei_value.py: -------------------------------------------------------------------------------- 1 | def test_ext_call(w3, side_effects_contract, assert_side_effects_invoked, get_contract): 2 | code = """ 3 | @external 4 | def foo(a: Foo) -> uint256: 5 | return as_wei_value(a.foo(7), "ether") 6 | 7 | interface Foo: 8 | def foo(x: uint8) -> uint8: nonpayable 9 | """ 10 | 11 | c1 = side_effects_contract("uint8") 12 | c2 = get_contract(code) 13 | 14 | assert c2.foo(c1.address) == w3.to_wei(7, "ether") 15 | assert_side_effects_invoked(c1, lambda: c2.foo(c1.address, transact={})) 16 | 17 | 18 | def test_internal_call(w3, get_contract_with_gas_estimation): 19 | code = """ 20 | @external 21 | def foo() -> uint256: 22 | return as_wei_value(self.bar(), "ether") 23 | 24 | @internal 25 | def bar() -> uint8: 26 | return 7 27 | """ 28 | 29 | c = get_contract_with_gas_estimation(code) 30 | 31 | assert c.foo() == w3.to_wei(7, "ether") 32 | -------------------------------------------------------------------------------- /tests/parser/functions/test_block.py: -------------------------------------------------------------------------------- 1 | def test_blockhash(get_contract_with_gas_estimation, w3): 2 | w3.testing.mine(1) 3 | 4 | block_number_code = """ 5 | @external 6 | def prev() -> bytes32: 7 | return block.prevhash 8 | 9 | @external 10 | def previous_blockhash() -> bytes32: 11 | return blockhash(block.number - 1) 12 | """ 13 | c = get_contract_with_gas_estimation(block_number_code) 14 | assert c.prev() == c.previous_blockhash() 15 | 16 | 17 | def test_negative_blockhash(assert_compile_failed, get_contract_with_gas_estimation): 18 | code = """ 19 | @external 20 | def foo() -> bytes32: 21 | return blockhash(-1) 22 | """ 23 | assert_compile_failed(lambda: get_contract_with_gas_estimation(code)) 24 | 25 | 26 | def test_too_old_blockhash(assert_tx_failed, get_contract_with_gas_estimation, w3): 27 | w3.testing.mine(257) 28 | code = """ 29 | @external 30 | def get_50_blockhash() -> bytes32: 31 | return blockhash(block.number - 257) 32 | """ 33 | c = get_contract_with_gas_estimation(code) 34 | assert_tx_failed(lambda: c.get_50_blockhash()) 35 | 36 | 37 | def test_non_existing_blockhash(assert_tx_failed, get_contract_with_gas_estimation): 38 | code = """ 39 | @external 40 | def get_future_blockhash() -> bytes32: 41 | return blockhash(block.number + 1) 42 | """ 43 | c = get_contract_with_gas_estimation(code) 44 | assert_tx_failed(lambda: c.get_future_blockhash()) 45 | -------------------------------------------------------------------------------- /tests/parser/functions/test_block_number.py: -------------------------------------------------------------------------------- 1 | def test_block_number(get_contract_with_gas_estimation, w3): 2 | block_number_code = """ 3 | @external 4 | def block_number() -> uint256: 5 | return block.number 6 | """ 7 | c = get_contract_with_gas_estimation(block_number_code) 8 | 9 | assert c.block_number() == 1 10 | w3.testing.mine(1) 11 | assert c.block_number() == 2 12 | -------------------------------------------------------------------------------- /tests/parser/functions/test_is_contract.py: -------------------------------------------------------------------------------- 1 | def test_is_contract(w3, get_contract_with_gas_estimation): 2 | contract_1 = """ 3 | @external 4 | def foo(arg1: address) -> bool: 5 | result: bool = arg1.is_contract 6 | return result 7 | """ 8 | 9 | contract_2 = """ 10 | @external 11 | def foo(arg1: address) -> bool: 12 | return arg1.is_contract 13 | """ 14 | a0, a1 = w3.eth.accounts[:2] 15 | c1 = get_contract_with_gas_estimation(contract_1) 16 | c2 = get_contract_with_gas_estimation(contract_2) 17 | 18 | assert c1.foo(c1.address) is True 19 | assert c1.foo(c2.address) is True 20 | assert c1.foo(a1) is False 21 | assert c1.foo(a0) is False 22 | assert c2.foo(c1.address) is True 23 | assert c2.foo(c2.address) is True 24 | assert c2.foo(a1) is False 25 | assert c2.foo(a0) is False 26 | -------------------------------------------------------------------------------- /tests/parser/functions/test_length.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | def test_test_length(get_contract_with_gas_estimation): 5 | test_length = """ 6 | y: Bytes[10] 7 | 8 | @external 9 | def foo(inp: Bytes[10]) -> uint256: 10 | x: Bytes[5] = slice(inp,1, 5) 11 | self.y = slice(inp, 2, 4) 12 | return len(inp) * 100 + len(x) * 10 + len(self.y) 13 | """ 14 | 15 | c = get_contract_with_gas_estimation(test_length) 16 | assert c.foo(b"badminton") == 954, c.foo(b"badminton") 17 | print("Passed length test") 18 | 19 | 20 | @pytest.mark.parametrize("typ", ["DynArray[uint256, 50]", "Bytes[50]", "String[50]"]) 21 | def test_zero_length(get_contract_with_gas_estimation, typ): 22 | code = f""" 23 | @external 24 | def boo() -> uint256: 25 | e: uint256 = len(empty({typ})) 26 | return e 27 | """ 28 | 29 | c = get_contract_with_gas_estimation(code) 30 | assert c.boo() == 0 31 | -------------------------------------------------------------------------------- /tests/parser/functions/test_method_id.py: -------------------------------------------------------------------------------- 1 | def test_method_id_test(get_contract_with_gas_estimation): 2 | method_id_test = """ 3 | @external 4 | def double(x: int128) -> int128: 5 | return x * 2 6 | 7 | @external 8 | def returnten() -> int128: 9 | ans: Bytes[32] = raw_call(self, concat(method_id("double(int128)"), convert(5, bytes32)), gas=50000, max_outsize=32) # noqa: E501 10 | return convert(convert(ans, bytes32), int128) 11 | """ 12 | c = get_contract_with_gas_estimation(method_id_test) 13 | assert c.returnten() == 10 14 | 15 | 16 | def test_method_id_bytes4(get_contract): 17 | code = """ 18 | @external 19 | def sig() -> bytes4: 20 | return method_id('transfer(address,uint256)', output_type=bytes4) 21 | """ 22 | c = get_contract(code) 23 | sig = c.sig() 24 | 25 | assert sig == b"\xa9\x05\x9c\xbb" 26 | 27 | 28 | def test_method_id_Bytes4(get_contract): 29 | code = """ 30 | @external 31 | def sig() -> Bytes[4]: 32 | return method_id('transfer(address,uint256)', output_type=Bytes[4]) 33 | """ 34 | c = get_contract(code) 35 | sig = c.sig() 36 | 37 | # assert len(sig) == 4 38 | assert sig == b"\xa9\x05\x9c\xbb" 39 | 40 | 41 | def test_method_id_bytes4_default(get_contract): 42 | code = """ 43 | @external 44 | def sig() -> Bytes[4]: 45 | return method_id('transfer(address,uint256)') 46 | """ 47 | c = get_contract(code) 48 | sig = c.sig() 49 | 50 | # assert len(sig) == 4 51 | assert sig == b"\xa9\x05\x9c\xbb" 52 | 53 | 54 | def test_method_id_invalid_space(get_contract, assert_compile_failed): 55 | code = """ 56 | @external 57 | def sig() -> bytes4: 58 | return method_id('transfer(address, uint256)', output_type=bytes4) 59 | """ 60 | assert_compile_failed(lambda: get_contract(code)) 61 | 62 | 63 | def test_method_id_invalid_type(get_contract, assert_compile_failed): 64 | code = """ 65 | @external 66 | def sig() -> bytes32: 67 | return method_id('transfer(address,uint256)', output_type=bytes32) 68 | """ 69 | assert_compile_failed(lambda: get_contract(code)) 70 | -------------------------------------------------------------------------------- /tests/parser/functions/test_minmax_value.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import InvalidType, OverflowException 4 | from vyper.semantics.types import DecimalT, IntegerT 5 | from vyper.semantics.types.shortcuts import INT256_T, UINT256_T 6 | 7 | 8 | @pytest.mark.parametrize("typ", sorted(IntegerT.all() + (DecimalT(),))) 9 | @pytest.mark.parametrize("op", ("min_value", "max_value")) 10 | def test_minmax_value(get_contract, op, typ): 11 | code = f""" 12 | @external 13 | def foo() -> {typ}: 14 | return {op}({typ}) 15 | """ 16 | c = get_contract(code) 17 | 18 | lo, hi = typ.ast_bounds 19 | if op == "min_value": 20 | assert c.foo() == lo 21 | elif op == "max_value": 22 | assert c.foo() == hi 23 | 24 | 25 | @pytest.mark.parametrize("typ", sorted(IntegerT.all())) 26 | def test_minmax_value_int_oob(get_contract, assert_compile_failed, typ): 27 | upper = f""" 28 | @external 29 | def foo(): 30 | a: {typ} = max_value({typ}) + 1 31 | """ 32 | 33 | lower = f""" 34 | @external 35 | def foo(): 36 | a: {typ} = min_value({typ}) - 1 37 | """ 38 | 39 | if typ == UINT256_T: 40 | assert_compile_failed(lambda: get_contract(upper), OverflowException) 41 | else: 42 | assert_compile_failed(lambda: get_contract(upper), InvalidType) 43 | 44 | if typ == INT256_T: 45 | assert_compile_failed(lambda: get_contract(lower), OverflowException) 46 | else: 47 | assert_compile_failed(lambda: get_contract(lower), InvalidType) 48 | 49 | 50 | @pytest.mark.parametrize("typ", [DecimalT()]) 51 | def test_minmax_value_decimal_oob(get_contract, assert_compile_failed, typ): 52 | upper = f""" 53 | @external 54 | def foo(): 55 | a: {typ} = max_value({typ}) + 1e-10 56 | """ 57 | 58 | lower = f""" 59 | @external 60 | def foo(): 61 | a: {typ} = min_value({typ}) - 1e-10 62 | """ 63 | 64 | assert_compile_failed(lambda: get_contract(upper), OverflowException) 65 | assert_compile_failed(lambda: get_contract(lower), OverflowException) 66 | -------------------------------------------------------------------------------- /tests/parser/functions/test_mkstr.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import pytest 4 | 5 | VALID_BITS = list(range(8, 257, 8)) 6 | 7 | 8 | @pytest.mark.parametrize("bits", VALID_BITS) 9 | def test_mkstr(get_contract_with_gas_estimation, bits): 10 | n_digits = math.ceil(bits * math.log(2) / math.log(10)) 11 | code = f""" 12 | @external 13 | def foo(inp: uint{bits}) -> String[{n_digits}]: 14 | return uint2str(inp) 15 | """ 16 | 17 | c = get_contract_with_gas_estimation(code) 18 | for i in [1, 2, 2**bits - 1, 0]: 19 | assert c.foo(i) == str(i), (i, c.foo(i)) 20 | 21 | 22 | # test for buffer overflow 23 | @pytest.mark.parametrize("bits", VALID_BITS) 24 | def test_mkstr_buffer(get_contract, bits): 25 | n_digits = math.ceil(bits * math.log(2) / math.log(10)) 26 | code = f""" 27 | some_string: String[{n_digits}] 28 | @internal 29 | def _foo(x: uint{bits}): 30 | self.some_string = uint2str(x) 31 | 32 | @external 33 | def foo(x: uint{bits}) -> uint256: 34 | y: uint256 = 0 35 | self._foo(x) 36 | return y 37 | """ 38 | c = get_contract(code) 39 | assert c.foo(2**bits - 1) == 0, bits 40 | -------------------------------------------------------------------------------- /tests/parser/functions/test_return.py: -------------------------------------------------------------------------------- 1 | def test_correct_abi_right_padding(tester, w3, get_contract_with_gas_estimation): 2 | selfcall_code_6 = """ 3 | @external 4 | def hardtest(arg1: Bytes[64], arg2: Bytes[64]) -> Bytes[128]: 5 | return concat(arg1, arg2) 6 | """ 7 | 8 | c = get_contract_with_gas_estimation(selfcall_code_6) 9 | 10 | assert c.hardtest(b"hello" * 5, b"hello" * 10) == b"hello" * 15 11 | 12 | # Make sure underlying structe is correctly right padded 13 | classic_contract = c._classic_contract 14 | func = classic_contract.functions.hardtest(b"hello" * 5, b"hello" * 10) 15 | tx = func.build_transaction({"gasPrice": 0}) 16 | del tx["chainId"] 17 | del tx["gasPrice"] 18 | 19 | tx["from"] = w3.eth.accounts[0] 20 | res = w3.to_bytes(hexstr=tester.call(tx)) 21 | 22 | static_offset = int.from_bytes(res[:32], "big") 23 | assert static_offset == 32 24 | 25 | dyn_section = res[static_offset:] 26 | assert len(dyn_section) % 32 == 0 # first right pad assert 27 | 28 | len_value = int.from_bytes(dyn_section[:32], "big") 29 | 30 | assert len_value == len(b"hello" * 15) 31 | assert dyn_section[32 : 32 + len_value] == b"hello" * 15 32 | # second right pad assert 33 | assert dyn_section[32 + len_value :] == b"\x00" * (len(dyn_section) - 32 - len_value) 34 | -------------------------------------------------------------------------------- /tests/parser/functions/test_sha256.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | 3 | import pytest 4 | 5 | pytestmark = pytest.mark.usefixtures("memory_mocker") 6 | 7 | 8 | def test_sha256_string_literal(get_contract_with_gas_estimation): 9 | code = """ 10 | @external 11 | def bar() -> bytes32: 12 | return sha256("test") 13 | """ 14 | 15 | c = get_contract_with_gas_estimation(code) 16 | 17 | assert c.bar() == hashlib.sha256(b"test").digest() 18 | 19 | 20 | def test_sha256_literal_bytes(get_contract_with_gas_estimation): 21 | code = """ 22 | @external 23 | def bar() -> (bytes32 , bytes32): 24 | x: bytes32 = sha256("test") 25 | y: bytes32 = sha256(b"test") 26 | return x, y 27 | """ 28 | c = get_contract_with_gas_estimation(code) 29 | h = hashlib.sha256(b"test").digest() 30 | assert c.bar() == [h, h] 31 | 32 | 33 | def test_sha256_bytes32(get_contract_with_gas_estimation): 34 | code = """ 35 | @external 36 | def bar(a: bytes32) -> bytes32: 37 | return sha256(a) 38 | """ 39 | 40 | c = get_contract_with_gas_estimation(code) 41 | 42 | test_val = 8 * b"bBaA" 43 | assert c.bar(test_val) == hashlib.sha256(test_val).digest() 44 | 45 | 46 | def test_sha256_bytearraylike(get_contract_with_gas_estimation): 47 | code = """ 48 | @external 49 | def bar(a: String[100]) -> bytes32: 50 | return sha256(a) 51 | """ 52 | 53 | c = get_contract_with_gas_estimation(code) 54 | 55 | test_val = "test me! test me!" 56 | assert c.bar(test_val) == hashlib.sha256(test_val.encode()).digest() 57 | test_val = "fun" 58 | assert c.bar(test_val) == hashlib.sha256(test_val.encode()).digest() 59 | 60 | 61 | def test_sha256_bytearraylike_storage(get_contract_with_gas_estimation): 62 | code = """ 63 | a: public(Bytes[100]) 64 | 65 | @external 66 | def set(b: Bytes[100]): 67 | self.a = b 68 | 69 | @external 70 | def bar() -> bytes32: 71 | return sha256(self.a) 72 | """ 73 | 74 | c = get_contract_with_gas_estimation(code) 75 | 76 | test_val = b"test me! test me!" 77 | c.set(test_val, transact={}) 78 | assert c.a() == test_val 79 | assert c.bar() == hashlib.sha256(test_val).digest() 80 | -------------------------------------------------------------------------------- /tests/parser/functions/test_tx.py: -------------------------------------------------------------------------------- 1 | def test_tx_gasprice(get_contract): 2 | code = """ 3 | @external 4 | def tx_gasprice() -> uint256: 5 | return tx.gasprice 6 | """ 7 | c = get_contract(code) 8 | for i in range(10): 9 | assert c.tx_gasprice(call={"gasPrice": 10**i}) == 10**i 10 | -------------------------------------------------------------------------------- /tests/parser/globals/test_globals.py: -------------------------------------------------------------------------------- 1 | from pytest import raises 2 | 3 | from vyper.exceptions import UndeclaredDefinition 4 | 5 | 6 | def test_permanent_variables_test(get_contract_with_gas_estimation): 7 | permanent_variables_test = """ 8 | struct Var: 9 | a: int128 10 | b: int128 11 | var: Var 12 | 13 | @external 14 | def __init__(a: int128, b: int128): 15 | self.var.a = a 16 | self.var.b = b 17 | 18 | @external 19 | def returnMoose() -> int128: 20 | return self.var.a * 10 + self.var.b 21 | """ 22 | 23 | c = get_contract_with_gas_estimation(permanent_variables_test, *[5, 7]) 24 | assert c.returnMoose() == 57 25 | print("Passed init argument and variable member test") 26 | 27 | 28 | def test_missing_global(get_contract): 29 | code = """ 30 | @external 31 | def a() -> int128: 32 | return self.b 33 | """ 34 | 35 | with raises(UndeclaredDefinition): 36 | get_contract(code) 37 | -------------------------------------------------------------------------------- /tests/parser/integration/test_basics.py: -------------------------------------------------------------------------------- 1 | def test_null_code(get_contract_with_gas_estimation): 2 | null_code = """ 3 | @external 4 | def foo(): 5 | pass 6 | """ 7 | c = get_contract_with_gas_estimation(null_code) 8 | c.foo() 9 | 10 | 11 | def test_basic_code(get_contract_with_gas_estimation): 12 | basic_code = """ 13 | @external 14 | def foo(x: int128) -> int128: 15 | return x * 2 16 | 17 | """ 18 | c = get_contract_with_gas_estimation(basic_code) 19 | assert c.foo(9) == 18 20 | -------------------------------------------------------------------------------- /tests/parser/integration/test_escrow.py: -------------------------------------------------------------------------------- 1 | # from ethereum.tools import tester 2 | 3 | 4 | def test_arbitration_code(w3, get_contract_with_gas_estimation, assert_tx_failed): 5 | arbitration_code = """ 6 | buyer: address 7 | seller: address 8 | arbitrator: address 9 | 10 | @external 11 | def setup(_seller: address, _arbitrator: address): 12 | if self.buyer == ZERO_ADDRESS: 13 | self.buyer = msg.sender 14 | self.seller = _seller 15 | self.arbitrator = _arbitrator 16 | 17 | @external 18 | def finalize(): 19 | assert msg.sender == self.buyer or msg.sender == self.arbitrator 20 | send(self.seller, self.balance) 21 | 22 | @external 23 | def refund(): 24 | assert msg.sender == self.seller or msg.sender == self.arbitrator 25 | send(self.buyer, self.balance) 26 | 27 | """ 28 | a0, a1, a2 = w3.eth.accounts[:3] 29 | c = get_contract_with_gas_estimation(arbitration_code, value=1) 30 | c.setup(a1, a2, transact={}) 31 | assert_tx_failed(lambda: c.finalize(transact={"from": a1})) 32 | c.finalize(transact={}) 33 | 34 | print("Passed escrow test") 35 | 36 | 37 | def test_arbitration_code_with_init(w3, assert_tx_failed, get_contract_with_gas_estimation): 38 | arbitration_code_with_init = """ 39 | buyer: address 40 | seller: address 41 | arbitrator: address 42 | 43 | @external 44 | @payable 45 | def __init__(_seller: address, _arbitrator: address): 46 | if self.buyer == ZERO_ADDRESS: 47 | self.buyer = msg.sender 48 | self.seller = _seller 49 | self.arbitrator = _arbitrator 50 | 51 | @external 52 | def finalize(): 53 | assert msg.sender == self.buyer or msg.sender == self.arbitrator 54 | send(self.seller, self.balance) 55 | 56 | @external 57 | def refund(): 58 | assert msg.sender == self.seller or msg.sender == self.arbitrator 59 | send(self.buyer, self.balance) 60 | """ 61 | a0, a1, a2 = w3.eth.accounts[:3] 62 | c = get_contract_with_gas_estimation(arbitration_code_with_init, *[a1, a2], value=1) 63 | assert_tx_failed(lambda: c.finalize(transact={"from": a1})) 64 | c.finalize(transact={"from": a0}) 65 | 66 | print("Passed escrow test with initializer") 67 | -------------------------------------------------------------------------------- /tests/parser/parser_utils/test_annotate_and_optimize_ast.py: -------------------------------------------------------------------------------- 1 | import ast as python_ast 2 | 3 | from vyper.ast.annotation import annotate_python_ast 4 | from vyper.ast.pre_parser import pre_parse 5 | 6 | 7 | class AssertionVisitor(python_ast.NodeVisitor): 8 | def assert_about_node(self, node): 9 | raise AssertionError() 10 | 11 | def generic_visit(self, node): 12 | self.assert_about_node(node) 13 | 14 | super().generic_visit(node) 15 | 16 | 17 | TEST_CONTRACT_SOURCE_CODE = """ 18 | struct S: 19 | a: bool 20 | b: int128 21 | 22 | interface ERC20Contract: 23 | def name() -> String[64]: view 24 | 25 | @external 26 | def foo() -> int128: 27 | return -(-(-1)) 28 | """ 29 | 30 | 31 | def get_contract_info(source_code): 32 | _, class_types, reformatted_code = pre_parse(source_code) 33 | py_ast = python_ast.parse(reformatted_code) 34 | 35 | annotate_python_ast(py_ast, reformatted_code, class_types) 36 | 37 | return py_ast, reformatted_code 38 | 39 | 40 | def test_it_annotates_ast_with_source_code(): 41 | contract_ast, reformatted_code = get_contract_info(TEST_CONTRACT_SOURCE_CODE) 42 | 43 | class AssertSourceCodePresent(AssertionVisitor): 44 | def assert_about_node(self, node): 45 | assert node.full_source_code is reformatted_code 46 | 47 | AssertSourceCodePresent().visit(contract_ast) 48 | 49 | 50 | def test_it_annotates_ast_with_class_types(): 51 | contract_ast, _ = get_contract_info(TEST_CONTRACT_SOURCE_CODE) 52 | 53 | struct_def = contract_ast.body[0] 54 | contract_def = contract_ast.body[1] 55 | 56 | assert struct_def.ast_type == "StructDef" 57 | assert contract_def.ast_type == "InterfaceDef" 58 | 59 | 60 | def test_it_rewrites_unary_subtractions(): 61 | contract_ast, _ = get_contract_info(TEST_CONTRACT_SOURCE_CODE) 62 | 63 | function_def = contract_ast.body[2] 64 | return_stmt = function_def.body[0] 65 | 66 | assert isinstance(return_stmt.value, python_ast.Num) 67 | assert return_stmt.value.n == -1 68 | -------------------------------------------------------------------------------- /tests/parser/syntax/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/tests/parser/syntax/__init__.py -------------------------------------------------------------------------------- /tests/parser/syntax/test_addmulmod.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import InvalidType 4 | 5 | fail_list = [ 6 | ( # bad AST nodes given as arguments 7 | """ 8 | @external 9 | def foo() -> uint256: 10 | return uint256_addmod(1.1, 1.2, 3.0) 11 | """, 12 | InvalidType, 13 | ), 14 | ( # bad AST nodes given as arguments 15 | """ 16 | @external 17 | def foo() -> uint256: 18 | return uint256_mulmod(1.1, 1.2, 3.0) 19 | """, 20 | InvalidType, 21 | ), 22 | ] 23 | 24 | 25 | @pytest.mark.parametrize("code,exc", fail_list) 26 | def test_add_mod_fail(assert_compile_failed, get_contract, code, exc): 27 | assert_compile_failed(lambda: get_contract(code), exc) 28 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_as_uint256.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidType, TypeMismatch 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def convert2(inp: uint256) -> uint256: 11 | return convert(inp, bytes32) 12 | """, 13 | TypeMismatch, 14 | ), 15 | ( 16 | """ 17 | @external 18 | def modtest(x: uint256, y: int128) -> uint256: 19 | return x % y 20 | """, 21 | TypeMismatch, 22 | ), 23 | ( 24 | """ 25 | @internal 26 | def ret_non(): 27 | pass 28 | 29 | @external 30 | def test(): 31 | a: uint256 = 100 * self.ret_non() 32 | """, 33 | InvalidType, 34 | ), 35 | ] 36 | 37 | 38 | @pytest.mark.parametrize("bad_code,exc", fail_list) 39 | def test_as_uint256_fail(bad_code, exc): 40 | with pytest.raises(exc): 41 | compiler.compile_code(bad_code) 42 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_as_wei_value.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import ArgumentException, InvalidType, StructureException 4 | 5 | fail_list = [ 6 | ( 7 | """ 8 | @external 9 | def foo(): 10 | x: int128 = as_wei_value(5, szabo) 11 | """, 12 | ArgumentException, 13 | ), 14 | ( 15 | """ 16 | @external 17 | def foo() -> int128: 18 | x: int128 = 45 19 | return x.balance 20 | """, 21 | StructureException, 22 | ), 23 | ( 24 | """ 25 | @external 26 | def foo(): 27 | x: int128 = as_wei_value(0xf5, "szabo") 28 | """, 29 | InvalidType, 30 | ), 31 | ] 32 | 33 | 34 | @pytest.mark.parametrize("bad_code,exc", fail_list) 35 | def test_as_wei_fail(get_contract_with_gas_estimation, bad_code, exc, assert_compile_failed): 36 | assert_compile_failed(lambda: get_contract_with_gas_estimation(bad_code), exc) 37 | 38 | 39 | valid_list = [ 40 | """ 41 | @external 42 | def foo(): 43 | x: uint256 = as_wei_value(5, "finney") + as_wei_value(2, "babbage") + as_wei_value(8, "shannon") # noqa: E501 44 | """, 45 | """ 46 | @external 47 | def foo(): 48 | z: int128 = 2 + 3 49 | x: uint256 = as_wei_value(2 + 3, "finney") 50 | """, 51 | """ 52 | @external 53 | def foo(): 54 | x: uint256 = as_wei_value(5.182, "babbage") 55 | """, 56 | """ 57 | @external 58 | def foo() -> uint256: 59 | x: address = 0x1234567890123456789012345678901234567890 60 | return x.balance 61 | """, 62 | ] 63 | 64 | 65 | @pytest.mark.parametrize("good_code", valid_list) 66 | def test_as_wei_success(good_code, get_contract_with_gas_estimation): 67 | assert get_contract_with_gas_estimation(good_code) is not None 68 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_bool_ops.py: -------------------------------------------------------------------------------- 1 | def test_convert_from_bool(get_contract_with_gas_estimation): 2 | code = """ 3 | @external 4 | def foo() -> bool: 5 | val: bool = True and True and False 6 | return val 7 | 8 | @external 9 | def bar() -> bool: 10 | val: bool = True or True or False 11 | return val 12 | 13 | @external 14 | def foobar() -> bool: 15 | val: bool = False and True or False 16 | return val 17 | 18 | @external 19 | def oof() -> bool: 20 | val: bool = False or False or False or False or False or True 21 | return val 22 | 23 | @external 24 | def rab() -> bool: 25 | val: bool = True and True and True and True and True and False 26 | return val 27 | 28 | @external 29 | def oofrab() -> bool: 30 | val: bool = False and True or False and True or False and False or True 31 | return val 32 | """ 33 | 34 | c = get_contract_with_gas_estimation(code) 35 | assert c.foo() is False 36 | assert c.bar() is True 37 | assert c.foobar() is False 38 | assert c.oof() is True 39 | assert c.rab() is False 40 | assert c.oofrab() is True 41 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_code_size.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import StructureException 5 | 6 | fail_list = [ 7 | """ 8 | @external 9 | def foo() -> int128: 10 | x: int128 = 45 11 | return x.codesize 12 | """ 13 | ] 14 | 15 | 16 | @pytest.mark.parametrize("bad_code", fail_list) 17 | def test_block_fail(bad_code): 18 | with pytest.raises(StructureException): 19 | compiler.compile_code(bad_code) 20 | 21 | 22 | valid_list = [ 23 | """ 24 | @external 25 | def foo() -> uint256: 26 | x: address = 0x1234567890123456789012345678901234567890 27 | return x.codesize 28 | """, 29 | """ 30 | @external 31 | def foo() -> uint256: 32 | return self.codesize 33 | """, 34 | """ 35 | struct Foo: 36 | t: address 37 | foo: Foo 38 | 39 | @external 40 | def bar() -> uint256: 41 | return self.foo.t.codesize 42 | """, 43 | ] 44 | 45 | 46 | @pytest.mark.parametrize("good_code", valid_list) 47 | def test_block_success(good_code): 48 | assert compiler.compile_code(good_code) is not None 49 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_codehash.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.compiler import compile_code 4 | from vyper.compiler.settings import Settings 5 | from vyper.evm.opcodes import EVM_VERSIONS 6 | from vyper.utils import keccak256 7 | 8 | 9 | @pytest.mark.parametrize("evm_version", list(EVM_VERSIONS)) 10 | def test_get_extcodehash(get_contract, evm_version, optimize): 11 | code = """ 12 | a: address 13 | 14 | @external 15 | def __init__(): 16 | self.a = self 17 | 18 | @external 19 | def foo(x: address) -> bytes32: 20 | return x.codehash 21 | 22 | @external 23 | def foo2(x: address) -> bytes32: 24 | b: address = x 25 | return b.codehash 26 | 27 | @external 28 | def foo3() -> bytes32: 29 | return self.codehash 30 | 31 | @external 32 | def foo4() -> bytes32: 33 | return self.a.codehash 34 | """ 35 | settings = Settings(evm_version=evm_version, optimize=optimize) 36 | compiled = compile_code(code, ["bytecode_runtime"], settings=settings) 37 | bytecode = bytes.fromhex(compiled["bytecode_runtime"][2:]) 38 | hash_ = keccak256(bytecode) 39 | 40 | c = get_contract(code, evm_version=evm_version) 41 | 42 | assert c.foo(c.address) == hash_ 43 | assert not int(c.foo("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF").hex(), 16) 44 | 45 | assert c.foo2(c.address) == hash_ 46 | assert not int(c.foo2("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF").hex(), 16) 47 | 48 | assert c.foo3() == hash_ 49 | assert c.foo4() == hash_ 50 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_conditionals.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | 5 | valid_list = [ 6 | """ 7 | @internal 8 | def mkint() -> int128: 9 | return 1 10 | 11 | @external 12 | def test_zerovalent(): 13 | if True: 14 | self.mkint() 15 | 16 | @external 17 | def test_valency_mismatch(): 18 | if True: 19 | self.mkint() 20 | else: 21 | pass 22 | """ 23 | ] 24 | 25 | 26 | @pytest.mark.parametrize("good_code", valid_list) 27 | def test_conditional_return_code(good_code): 28 | assert compiler.compile_code(good_code) is not None 29 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_create_with_code_of.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import raises 3 | 4 | from vyper import compiler 5 | from vyper.exceptions import ArgumentException, SyntaxException 6 | 7 | fail_list = [ 8 | """ 9 | @external 10 | def foo(): 11 | x: address = create_minimal_proxy_to( 12 | 0x1234567890123456789012345678901234567890, 13 | value=4, 14 | value=9 15 | ) 16 | """, 17 | """ 18 | @external 19 | def foo(_salt: bytes32): 20 | x: address = create_minimal_proxy_to( 21 | 0x1234567890123456789012345678901234567890, salt=keccak256(b"Vyper Rocks!"), salt=_salt 22 | ) 23 | """, 24 | ] 25 | 26 | 27 | @pytest.mark.parametrize("bad_code", fail_list) 28 | def test_type_mismatch_exception(bad_code): 29 | with raises((SyntaxException, ArgumentException)): 30 | compiler.compile_code(bad_code) 31 | 32 | 33 | valid_list = [ 34 | """ 35 | @external 36 | def foo(): 37 | # test that create_forwarder_to is valid syntax; the name is deprecated 38 | x: address = create_forwarder_to(0x1234567890123456789012345678901234567890) 39 | """, 40 | """ 41 | @external 42 | def foo(): 43 | x: address = create_minimal_proxy_to(0x1234567890123456789012345678901234567890) 44 | """, 45 | """ 46 | @external 47 | def foo(): 48 | x: address = create_minimal_proxy_to( 49 | 0x1234567890123456789012345678901234567890, 50 | value=as_wei_value(9, "wei") 51 | ) 52 | """, 53 | """ 54 | @external 55 | def foo(): 56 | x: address = create_minimal_proxy_to(0x1234567890123456789012345678901234567890, value=9) 57 | """, 58 | """ 59 | @external 60 | def foo(): 61 | x: address = create_minimal_proxy_to( 62 | 0x1234567890123456789012345678901234567890, 63 | salt=keccak256(b"Vyper Rocks!") 64 | ) 65 | """, 66 | """ 67 | @external 68 | def foo(_salt: bytes32): 69 | x: address = create_minimal_proxy_to(0x1234567890123456789012345678901234567890, salt=_salt) 70 | """, 71 | ] 72 | 73 | 74 | @pytest.mark.parametrize("good_code", valid_list) 75 | def test_rlp_success(good_code): 76 | assert compiler.compile_code(good_code) is not None 77 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_dynamic_array.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import StructureException 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | foo: DynArray[HashMap[uint8, uint8], 2] 10 | """, 11 | StructureException, 12 | ), 13 | ( 14 | """ 15 | foo: public(DynArray[HashMap[uint8, uint8], 2]) 16 | """, 17 | StructureException, 18 | ), 19 | ( 20 | """ 21 | @external 22 | def foo(): 23 | a: DynArray = [1, 2, 3] 24 | """, 25 | StructureException, 26 | ), 27 | ] 28 | 29 | 30 | @pytest.mark.parametrize("bad_code,exc", fail_list) 31 | def test_block_fail(assert_compile_failed, get_contract, bad_code, exc): 32 | assert_compile_failed(lambda: get_contract(bad_code), exc) 33 | 34 | 35 | valid_list = [ 36 | """ 37 | enum Foo: 38 | FE 39 | FI 40 | 41 | bar: DynArray[Foo, 10] 42 | """, # dynamic arrays of enums are allowed, but not static arrays 43 | """ 44 | bar: DynArray[Bytes[30], 10] 45 | """, # dynamic arrays of bytestrings are allowed, but not static arrays 46 | ] 47 | 48 | 49 | @pytest.mark.parametrize("good_code", valid_list) 50 | def test_dynarray_pass(good_code): 51 | assert compiler.compile_code(good_code) is not None 52 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_for_range.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import StructureException 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo(): 11 | for a[1] in range(10): 12 | pass 13 | """, 14 | StructureException, 15 | ), 16 | ( 17 | """ 18 | @external 19 | def bar(): 20 | for i in range(1,2,bound=2): 21 | pass 22 | """, 23 | StructureException, 24 | ), 25 | ( 26 | """ 27 | @external 28 | def bar(): 29 | x:uint256 = 1 30 | for i in range(x,x+1,bound=2): 31 | pass 32 | """, 33 | StructureException, 34 | ), 35 | ] 36 | 37 | 38 | @pytest.mark.parametrize("bad_code", fail_list) 39 | def test_range_fail(bad_code): 40 | with pytest.raises(bad_code[1]): 41 | compiler.compile_code(bad_code[0]) 42 | 43 | 44 | valid_list = [ 45 | """ 46 | @external 47 | def foo(): 48 | for i in range(10): 49 | pass 50 | """, 51 | """ 52 | @external 53 | def foo(): 54 | for i in range(10, 20): 55 | pass 56 | """, 57 | """ 58 | @external 59 | def foo(): 60 | x: int128 = 5 61 | for i in range(x, x + 10): 62 | pass 63 | """, 64 | """ 65 | interface Foo: 66 | def kick(): nonpayable 67 | foos: Foo[3] 68 | @external 69 | def kick_foos(): 70 | for foo in self.foos: 71 | foo.kick() 72 | """, 73 | ] 74 | 75 | 76 | @pytest.mark.parametrize("good_code", valid_list) 77 | def test_range_success(good_code): 78 | assert compiler.compile_code(good_code) is not None 79 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_functions_call.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import StructureException, UndeclaredDefinition, UnknownAttribute 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo() -> uint256: 11 | doesnotexist(2, uint256) 12 | return convert(2, uint256) 13 | """, 14 | UndeclaredDefinition, 15 | ), 16 | ( 17 | """ 18 | @external 19 | def foo(x: int256) -> uint256: 20 | convert(x, uint256) 21 | return convert(x, uint256) 22 | 23 | """, 24 | StructureException, 25 | ), 26 | ( 27 | """ 28 | @internal 29 | def test(a : uint256): 30 | pass 31 | 32 | 33 | @external 34 | def burn(_value: uint256): 35 | self.test(msg.sender._value) 36 | """, 37 | UnknownAttribute, 38 | ), 39 | ] 40 | 41 | 42 | @pytest.mark.parametrize("bad_code,exc", fail_list) 43 | def test_functions_call_fail(bad_code, exc): 44 | with pytest.raises(exc): 45 | compiler.compile_code(bad_code) 46 | 47 | 48 | valid_list = [ 49 | """ 50 | @external 51 | def foo(x: int128) -> uint256: 52 | return convert(x, uint256) 53 | """, 54 | """ 55 | from vyper.interfaces import ERC20 56 | 57 | interface Factory: 58 | def getExchange(token_addr: address) -> address: view 59 | 60 | token: ERC20 61 | factory: Factory 62 | 63 | @external 64 | def setup(token_addr: address): 65 | self.token = ERC20(token_addr) 66 | assert self.factory.getExchange(self.token.address) == self 67 | """, 68 | ] 69 | 70 | 71 | @pytest.mark.parametrize("good_code", valid_list) 72 | def test_functions_call_success(good_code): 73 | assert compiler.compile_code(good_code) is not None 74 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_keccak256.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidType, UndeclaredDefinition 5 | 6 | type_fail_list = [ 7 | """ 8 | @external 9 | def foo(): 10 | x: bytes32 = keccak256(3) 11 | """ 12 | ] 13 | 14 | 15 | @pytest.mark.parametrize("bad_code", type_fail_list) 16 | def test_block_type_fail(bad_code): 17 | with pytest.raises(InvalidType): 18 | compiler.compile_code(bad_code) 19 | 20 | 21 | structure_fail_list = [ 22 | """ 23 | @external 24 | def foo(): 25 | x: bytes32 = sha3("moose") 26 | """, 27 | """ 28 | @external 29 | def foo(): 30 | x: bytes32 = sha3(0x1234567890123456789012345678901234567890123456789012345678901234) 31 | """, 32 | ] 33 | 34 | 35 | @pytest.mark.parametrize("bad_code", structure_fail_list) 36 | def test_block_structure_fail(bad_code): 37 | with pytest.raises(UndeclaredDefinition): 38 | compiler.compile_code(bad_code) 39 | 40 | 41 | valid_list = [ 42 | """ 43 | @external 44 | def foo(): 45 | x: bytes32 = keccak256("moose") 46 | """, 47 | """ 48 | @external 49 | def foo(): 50 | x: bytes32 = keccak256(0x1234567890123456789012345678901234567890123456789012345678901234) 51 | """, 52 | ] 53 | 54 | 55 | @pytest.mark.parametrize("good_code", valid_list) 56 | def test_block_success(good_code): 57 | assert compiler.compile_code(good_code) is not None 58 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_len.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import raises 3 | 4 | from vyper import compiler 5 | from vyper.exceptions import TypeMismatch 6 | 7 | fail_list = [ 8 | """ 9 | @external 10 | def foo(inp: Bytes[4]) -> int128: 11 | return len(inp) 12 | """, 13 | """ 14 | @external 15 | def foo(inp: int128) -> uint256: 16 | return len(inp) 17 | """, 18 | ] 19 | 20 | 21 | @pytest.mark.parametrize("bad_code", fail_list) 22 | def test_block_fail(bad_code): 23 | if isinstance(bad_code, tuple): 24 | with raises(bad_code[1]): 25 | compiler.compile_code(bad_code[0]) 26 | else: 27 | with raises(TypeMismatch): 28 | compiler.compile_code(bad_code) 29 | 30 | 31 | valid_list = [ 32 | """ 33 | @external 34 | def foo(inp: Bytes[10]) -> uint256: 35 | return len(inp) 36 | """, 37 | """ 38 | @external 39 | def foo(inp: String[10]) -> uint256: 40 | return len(inp) 41 | """, 42 | ] 43 | 44 | 45 | @pytest.mark.parametrize("good_code", valid_list) 46 | def test_list_success(good_code): 47 | assert compiler.compile_code(good_code) is not None 48 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_logging.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidType, StructureException, TypeMismatch 5 | 6 | fail_list = [ 7 | """ 8 | event Bar: 9 | _value: int128[4] 10 | 11 | x: decimal[4] 12 | 13 | @external 14 | def foo(): 15 | log Bar(self.x) 16 | """, 17 | """ 18 | event Bar: 19 | _value: int128[4] 20 | 21 | @external 22 | def foo(): 23 | x: decimal[4] = [0.0, 0.0, 0.0, 0.0] 24 | log Bar(x) 25 | """, 26 | ( 27 | """ 28 | event Test: 29 | n: uint256 30 | 31 | @external 32 | def test(): 33 | log Test(-7) 34 | """, 35 | InvalidType, 36 | ), 37 | ] 38 | 39 | 40 | @pytest.mark.parametrize("bad_code", fail_list) 41 | def test_logging_fail(bad_code): 42 | if isinstance(bad_code, tuple): 43 | with pytest.raises(bad_code[1]): 44 | compiler.compile_code(bad_code[0]) 45 | else: 46 | with pytest.raises(TypeMismatch): 47 | compiler.compile_code(bad_code) 48 | 49 | 50 | @pytest.mark.parametrize("mutability", ["@pure", "@view"]) 51 | @pytest.mark.parametrize("visibility", ["@internal", "@external"]) 52 | def test_logging_from_non_mutable(mutability, visibility): 53 | code = f""" 54 | event Test: 55 | n: uint256 56 | 57 | {visibility} 58 | {mutability} 59 | def test(): 60 | log Test(1) 61 | """ 62 | with pytest.raises(StructureException): 63 | compiler.compile_code(code) 64 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_minmax.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import InvalidType, TypeMismatch 4 | 5 | fail_list = [ 6 | ( 7 | """ 8 | @external 9 | def foo(): 10 | y: int128 = min(7, 0x1234567890123456789012345678901234567890) 11 | """, 12 | InvalidType, 13 | ), 14 | ( 15 | """ 16 | @external 17 | def foo(): 18 | a: int16 = min(min_value(int16), max_value(int8)) 19 | """, 20 | TypeMismatch, 21 | ), 22 | ] 23 | 24 | 25 | @pytest.mark.parametrize("bad_code,exc", fail_list) 26 | def test_block_fail(assert_compile_failed, get_contract_with_gas_estimation, bad_code, exc): 27 | assert_compile_failed(lambda: get_contract_with_gas_estimation(bad_code), exc) 28 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_minmax_value.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import InvalidType 4 | 5 | fail_list = [ 6 | """ 7 | @external 8 | def foo(): 9 | a: address = min_value(address) 10 | """, 11 | """ 12 | @external 13 | def foo(): 14 | a: address = max_value(address) 15 | """, 16 | ] 17 | 18 | 19 | @pytest.mark.parametrize("bad_code", fail_list) 20 | def test_block_fail(assert_compile_failed, get_contract_with_gas_estimation, bad_code): 21 | assert_compile_failed(lambda: get_contract_with_gas_estimation(bad_code), InvalidType) 22 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_nested_list.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidLiteral, InvalidType, TypeMismatch 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | bar: int128[3][3] 10 | @external 11 | def foo(): 12 | self.bar = [[1, 2], [3, 4, 5], [6, 7, 8]] 13 | """, 14 | InvalidType, # casting darray to sarray 15 | ), 16 | ( 17 | """ 18 | bar: int128[3][3] 19 | @external 20 | def foo(): 21 | self.bar = [[1, 2, 3], [4, 5, 6], [7.0, 8.0, 9.0]] 22 | """, 23 | InvalidLiteral, 24 | ), 25 | ( 26 | """ 27 | @external 28 | def foo() -> int128[2]: 29 | return [[1,2],[3,4]] 30 | """, 31 | InvalidType, 32 | ), 33 | ( 34 | """ 35 | @external 36 | def foo() -> int128[2][2]: 37 | return [1,2] 38 | """, 39 | InvalidType, 40 | ), 41 | ( 42 | """ 43 | y: address[2][2] 44 | 45 | @external 46 | def foo(x: int128[2][2]) -> int128: 47 | self.y = x 48 | return 768 49 | """, 50 | TypeMismatch, 51 | ), 52 | ] 53 | 54 | 55 | @pytest.mark.parametrize("bad_code,exc", fail_list) 56 | def test_nested_list_fail(bad_code, exc): 57 | with pytest.raises(exc): 58 | compiler.compile_code(bad_code) 59 | 60 | 61 | valid_list = [ 62 | """ 63 | bar: int128[3][3] 64 | @external 65 | def foo(): 66 | self.bar = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 67 | """, 68 | """ 69 | bar: decimal[3][3] 70 | @external 71 | def foo(): 72 | self.bar = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]] 73 | """, 74 | ] 75 | 76 | 77 | @pytest.mark.parametrize("good_code", valid_list) 78 | def test_nested_list_sucess(good_code): 79 | assert compiler.compile_code(good_code) is not None 80 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_print.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | 5 | valid_list = [ 6 | """ 7 | @external 8 | def foo(x: uint256): 9 | print(x) 10 | """, 11 | """ 12 | @external 13 | def foo(x: Bytes[1]): 14 | print(x) 15 | """, 16 | """ 17 | struct Foo: 18 | x: Bytes[128] 19 | @external 20 | def foo(foo: Foo): 21 | print(foo) 22 | """, 23 | """ 24 | struct Foo: 25 | x: uint256 26 | @external 27 | def foo(foo: Foo): 28 | print(foo) 29 | """, 30 | """ 31 | BAR: constant(DynArray[uint256, 5]) = [1, 2, 3, 4, 5] 32 | 33 | @external 34 | def foo(): 35 | print(BAR) 36 | """, 37 | """ 38 | FOO: constant(uint256) = 1 39 | BAR: constant(DynArray[uint256, 5]) = [1, 2, 3, 4, 5] 40 | 41 | @external 42 | def foo(): 43 | print(FOO, BAR) 44 | """, 45 | ] 46 | 47 | 48 | @pytest.mark.parametrize("good_code", valid_list) 49 | def test_print_syntax(good_code): 50 | assert compiler.compile_code(good_code) is not None 51 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_public.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | 5 | valid_list = [ 6 | """ 7 | x: public(int128) 8 | """, 9 | """ 10 | x: public(constant(int128)) = 0 11 | y: public(immutable(int128)) 12 | 13 | @external 14 | def __init__(): 15 | y = 0 16 | """, 17 | """ 18 | x: public(int128) 19 | y: public(int128) 20 | z: public(int128) 21 | 22 | @external 23 | def foo() -> int128: 24 | return self.x / self.y / self.z 25 | """, 26 | # expansion of public user-defined struct 27 | """ 28 | struct Foo: 29 | a: uint256 30 | 31 | x: public(HashMap[uint256, Foo]) 32 | """, 33 | # expansion of public user-defined enum 34 | """ 35 | enum Foo: 36 | BAR 37 | 38 | x: public(HashMap[uint256, Foo]) 39 | """, 40 | # expansion of public user-defined interface 41 | """ 42 | interface Foo: 43 | def bar(): nonpayable 44 | 45 | x: public(HashMap[uint256, Foo]) 46 | """, 47 | ] 48 | 49 | 50 | @pytest.mark.parametrize("good_code", valid_list) 51 | def test_public_success(good_code): 52 | assert compiler.compile_code(good_code) is not None 53 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_self_balance.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.compiler.settings import Settings 5 | from vyper.evm.opcodes import EVM_VERSIONS 6 | 7 | 8 | @pytest.mark.parametrize("evm_version", list(EVM_VERSIONS)) 9 | def test_self_balance(w3, get_contract_with_gas_estimation, evm_version): 10 | code = """ 11 | @external 12 | @view 13 | def get_balance() -> uint256: 14 | a: uint256 = self.balance 15 | return a 16 | 17 | @external 18 | @payable 19 | def __default__(): 20 | pass 21 | """ 22 | settings = Settings(evm_version=evm_version) 23 | opcodes = compiler.compile_code(code, ["opcodes"], settings=settings)["opcodes"] 24 | if EVM_VERSIONS[evm_version] >= EVM_VERSIONS["istanbul"]: 25 | assert "SELFBALANCE" in opcodes 26 | else: 27 | assert "SELFBALANCE" not in opcodes 28 | 29 | c = get_contract_with_gas_estimation(code, evm_version=evm_version) 30 | w3.eth.send_transaction({"to": c.address, "value": 1337}) 31 | 32 | assert c.get_balance() == 1337 33 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_selfdestruct.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidType 5 | 6 | fail_list = [ 7 | """ 8 | @external 9 | def foo(): 10 | selfdestruct(7) 11 | """ 12 | ] 13 | 14 | 15 | @pytest.mark.parametrize("bad_code", fail_list) 16 | def test_block_fail(bad_code): 17 | with pytest.raises(InvalidType): 18 | compiler.compile_code(bad_code) 19 | 20 | 21 | valid_list = [ 22 | """ 23 | @external 24 | def foo(): 25 | selfdestruct(0x1234567890123456789012345678901234567890) 26 | """ 27 | ] 28 | 29 | 30 | @pytest.mark.parametrize("good_code", valid_list) 31 | def test_block_success(good_code): 32 | assert compiler.compile_code(good_code) is not None 33 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_slice.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidType, TypeMismatch 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo(inp: Bytes[10]) -> Bytes[2]: 11 | return slice(inp, 2, 3) 12 | """, 13 | TypeMismatch, 14 | ), 15 | ( 16 | """ 17 | @external 18 | def foo(inp: int128) -> Bytes[3]: 19 | return slice(inp, 2, 3) 20 | """, 21 | TypeMismatch, 22 | ), 23 | ( 24 | """ 25 | @external 26 | def foo(inp: Bytes[10]) -> Bytes[3]: 27 | return slice(inp, 4.0, 3) 28 | """, 29 | InvalidType, 30 | ), 31 | ] 32 | 33 | 34 | @pytest.mark.parametrize("bad_code,exc", fail_list) 35 | def test_slice_fail(bad_code, exc): 36 | with pytest.raises(exc): 37 | compiler.compile_code(bad_code) 38 | 39 | 40 | valid_list = [ 41 | """ 42 | @external 43 | def foo(inp: Bytes[10]) -> Bytes[3]: 44 | return slice(inp, 2, 3) 45 | """, 46 | """ 47 | @external 48 | def foo(inp: Bytes[10]) -> Bytes[4]: 49 | return slice(inp, 2, 3) 50 | """, 51 | """ 52 | @external 53 | def foo() -> Bytes[10]: 54 | return slice(b"badmintonzzz", 1, 10) 55 | """, 56 | ] 57 | 58 | 59 | @pytest.mark.parametrize("good_code", valid_list) 60 | def test_slice_success(good_code): 61 | assert compiler.compile_code(good_code) is not None 62 | -------------------------------------------------------------------------------- /tests/parser/syntax/test_string.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import StructureException 5 | 6 | valid_list = [ 7 | """ 8 | @external 9 | def foo() -> String[10]: 10 | return "badminton" 11 | """, 12 | """ 13 | @external 14 | def foo(): 15 | x: String[11] = "¡très bien!" 16 | """, 17 | """ 18 | @external 19 | def foo() -> bool: 20 | x: String[15] = "¡très bien!" 21 | y: String[15] = "test" 22 | return x != y 23 | """, 24 | """ 25 | @external 26 | def foo() -> bool: 27 | x: String[15] = "¡très bien!" 28 | y: String[12] = "test" 29 | return x != y 30 | """, 31 | """ 32 | @external 33 | def test() -> String[100]: 34 | return "hello world!" 35 | """, 36 | ] 37 | 38 | 39 | @pytest.mark.parametrize("good_code", valid_list) 40 | def test_string_success(good_code): 41 | assert compiler.compile_code(good_code) is not None 42 | 43 | 44 | invalid_list = [ 45 | ( 46 | """ 47 | @external 48 | def foo(): 49 | a: String = "abc" 50 | """, 51 | StructureException, 52 | ) 53 | ] 54 | 55 | 56 | @pytest.mark.parametrize("bad_code,exc", invalid_list) 57 | def test_string_fail(assert_compile_failed, get_contract_with_gas_estimation, bad_code, exc): 58 | assert_compile_failed(lambda: get_contract_with_gas_estimation(bad_code), exc) 59 | -------------------------------------------------------------------------------- /tests/parser/syntax/utils/test_variable_names.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest import raises 3 | 4 | from vyper import compiler 5 | from vyper.exceptions import NamespaceCollision, StructureException 6 | 7 | fail_list = [ # noqa: E122 8 | """ 9 | @external 10 | def foo(i: int128) -> int128: 11 | varő : int128 = i 12 | return varő 13 | """, 14 | """ 15 | @external 16 | def foo(i: int128) -> int128: 17 | wei : int128 = i 18 | return wei 19 | """, 20 | """ 21 | @external 22 | def foo(i: int128) -> int128: 23 | false : int128 = i 24 | return false 25 | """, 26 | ] 27 | 28 | 29 | @pytest.mark.parametrize("bad_code", fail_list) 30 | def test_varname_validity_fail(bad_code): 31 | with raises(StructureException): 32 | compiler.compile_code(bad_code) 33 | 34 | 35 | collision_fail_list = [ 36 | """ 37 | @external 38 | def foo(i: int128) -> int128: 39 | int128 : int128 = i 40 | return int128 41 | """, 42 | """ 43 | @external 44 | def foo(i: int128) -> int128: 45 | decimal : int128 = i 46 | return decimal 47 | """, 48 | ] 49 | 50 | 51 | @pytest.mark.parametrize("bad_code", collision_fail_list) 52 | def test_varname_collision_fail(bad_code): 53 | with raises(NamespaceCollision): 54 | compiler.compile_code(bad_code) 55 | 56 | 57 | valid_list = [ 58 | """ 59 | @external 60 | def foo(i: int128) -> int128: 61 | variable : int128 = i 62 | return variable 63 | """, 64 | """ 65 | @external 66 | def foo(i: int128) -> int128: 67 | var_123 : int128 = i 68 | return var_123 69 | """, 70 | """ 71 | @external 72 | def foo(i: int128) -> int128: 73 | _var123 : int128 = i 74 | return _var123 75 | """, 76 | ] 77 | 78 | 79 | @pytest.mark.parametrize("good_code", valid_list) 80 | def test_varname_validity_success(good_code): 81 | assert compiler.compile_code(good_code) is not None 82 | -------------------------------------------------------------------------------- /tests/parser/types/test_bytes_zero_padding.py: -------------------------------------------------------------------------------- 1 | import hypothesis 2 | import pytest 3 | 4 | 5 | @pytest.fixture(scope="module") 6 | def little_endian_contract(get_contract_module): 7 | code = """ 8 | @internal 9 | @view 10 | def to_little_endian_64(_value: uint256) -> Bytes[8]: 11 | y: uint256 = 0 12 | x: uint256 = _value 13 | for _ in range(8): 14 | y = (y << 8) | (x & 255) 15 | x >>= 8 16 | return slice(convert(y, bytes32), 24, 8) 17 | 18 | @external 19 | @view 20 | def get_count(counter: uint256) -> Bytes[24]: 21 | return self.to_little_endian_64(counter) 22 | """ 23 | c = get_contract_module(code) 24 | return c 25 | 26 | 27 | @pytest.mark.fuzzing 28 | @hypothesis.given(value=hypothesis.strategies.integers(min_value=0, max_value=2**64)) 29 | @hypothesis.settings(deadline=400) 30 | def test_zero_pad_range(little_endian_contract, value): 31 | actual_bytes = value.to_bytes(8, byteorder="little") 32 | contract_bytes = little_endian_contract.get_count(value) 33 | assert contract_bytes == actual_bytes 34 | -------------------------------------------------------------------------------- /tests/parser/types/test_node_types.py: -------------------------------------------------------------------------------- 1 | from pytest import raises 2 | 3 | from vyper.semantics.types import ( 4 | AddressT, 5 | BytesT, 6 | DecimalT, 7 | HashMapT, 8 | IntegerT, 9 | SArrayT, 10 | StructT, 11 | TupleT, 12 | ) 13 | 14 | # TODO: this module should be merged in with other tests/functional/semantics/types/ tests. 15 | 16 | 17 | def test_bytearray_node_type(): 18 | node1 = BytesT(12) 19 | node2 = BytesT(12) 20 | 21 | assert node1 == node2 22 | 23 | node3 = BytesT(13) 24 | node4 = IntegerT(True, 128) 25 | 26 | assert node1 != node3 27 | assert node1 != node4 28 | 29 | 30 | def test_mapping_node_types(): 31 | node1 = HashMapT(IntegerT(True, 128), IntegerT(True, 128)) 32 | node2 = HashMapT(IntegerT(True, 128), IntegerT(True, 128)) 33 | assert node1 == node2 34 | assert str(node1) == "HashMap[int128, int128]" 35 | 36 | 37 | def test_tuple_node_types(): 38 | node1 = TupleT([IntegerT(True, 128), DecimalT()]) 39 | node2 = TupleT([IntegerT(True, 128), DecimalT()]) 40 | 41 | assert node1 == node2 42 | assert str(node1) == "(int128, decimal)" 43 | 44 | 45 | def test_canonicalize_type(): 46 | # TODO add more types 47 | 48 | # Test ABI format of multiple args. 49 | c = TupleT([IntegerT(True, 128), AddressT()]) 50 | assert c.abi_type.selector_name() == "(int128,address)" 51 | 52 | 53 | def test_type_storage_sizes(): 54 | assert IntegerT(True, 128).storage_size_in_words == 1 55 | assert BytesT(12).storage_size_in_words == 2 56 | assert BytesT(33).storage_size_in_words == 3 57 | assert SArrayT(IntegerT(True, 128), 10).storage_size_in_words == 10 58 | 59 | tuple_ = TupleT([IntegerT(True, 128), DecimalT()]) 60 | assert tuple_.storage_size_in_words == 2 61 | 62 | struct_ = StructT("Foo", {"a": IntegerT(True, 128), "b": DecimalT()}) 63 | assert struct_.storage_size_in_words == 2 64 | 65 | # Don't allow unknown types. 66 | with raises(Exception): 67 | _ = int.storage_size_in_words 68 | -------------------------------------------------------------------------------- /tests/parser/types/test_string_literal.py: -------------------------------------------------------------------------------- 1 | def test_string_literal_return(get_contract_with_gas_estimation): 2 | code = """ 3 | @external 4 | def test() -> String[100]: 5 | return "hello world!" 6 | 7 | 8 | @external 9 | def testb() -> Bytes[100]: 10 | return b"hello world!" 11 | """ 12 | 13 | c = get_contract_with_gas_estimation(code) 14 | 15 | assert c.test() == "hello world!" 16 | assert c.testb() == b"hello world!" 17 | 18 | 19 | def test_string_convert(get_contract_with_gas_estimation): 20 | code = """ 21 | @external 22 | def testb() -> String[100]: 23 | return convert(b"hello world!", String[100]) 24 | 25 | @external 26 | def testbb() -> String[100]: 27 | return convert(convert("hello world!", Bytes[100]), String[100]) 28 | """ 29 | 30 | c = get_contract_with_gas_estimation(code) 31 | 32 | assert c.testb() == "hello world!" 33 | assert c.testbb() == "hello world!" 34 | 35 | 36 | def test_str_assign(get_contract_with_gas_estimation): 37 | code = """ 38 | @external 39 | def test() -> String[100]: 40 | a: String[100] = "baba black sheep" 41 | return a 42 | """ 43 | 44 | c = get_contract_with_gas_estimation(code) 45 | 46 | assert c.test() == "baba black sheep" 47 | -------------------------------------------------------------------------------- /tests/signatures/test_invalid_function_decorators.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import StructureException 5 | 6 | FAILING_CONTRACTS = [ 7 | """ 8 | @external 9 | @pure 10 | @nonreentrant('lock') 11 | def nonreentrant_foo() -> uint256: 12 | return 1 13 | """ 14 | ] 15 | 16 | 17 | @pytest.mark.parametrize("failing_contract_code", FAILING_CONTRACTS) 18 | def test_invalid_function_decorators(failing_contract_code): 19 | with pytest.raises(StructureException): 20 | compiler.compile_code(failing_contract_code) 21 | -------------------------------------------------------------------------------- /tests/signatures/test_method_id_conflicts.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import StructureException 5 | 6 | FAILING_CONTRACTS = [ 7 | """ 8 | @external 9 | @view 10 | def gsf(): 11 | pass 12 | 13 | @external 14 | @view 15 | def tgeo(): 16 | pass 17 | """, 18 | """ 19 | @external 20 | @view 21 | def withdraw(a: uint256): 22 | pass 23 | 24 | @external 25 | @view 26 | def OwnerTransferV7b711143(a: uint256): 27 | pass 28 | """, 29 | """ 30 | @external 31 | @view 32 | def withdraw(a: uint256): 33 | pass 34 | 35 | @external 36 | @view 37 | def gsf(): 38 | pass 39 | 40 | @external 41 | @view 42 | def tgeo(): 43 | pass 44 | 45 | @external 46 | @view 47 | def OwnerTransferV7b711143(a: uint256): 48 | pass 49 | """, 50 | """ 51 | # check collision with ID = 0x00000000 52 | wycpnbqcyf:public(uint256) 53 | 54 | @external 55 | def randallsRevenge_ilxaotc(): pass 56 | """, 57 | ] 58 | 59 | 60 | @pytest.mark.parametrize("failing_contract_code", FAILING_CONTRACTS) 61 | def test_method_id_conflicts(failing_contract_code): 62 | with pytest.raises(StructureException): 63 | compiler.compile_code(failing_contract_code) 64 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = 3 | py{310,311} 4 | lint 5 | mypy 6 | docs 7 | 8 | [testenv] 9 | usedevelop = True 10 | commands = 11 | pytest -m "not fuzzing" --showlocals {posargs:tests/} 12 | basepython = 13 | py310: python3.10 14 | py311: python3.11 15 | extras = 16 | test 17 | whitelist_externals = make 18 | 19 | [testenv:docs] 20 | basepython=python3 21 | deps = 22 | sphinx 23 | sphinx_rtd_theme 24 | recommonmark 25 | commands = 26 | sphinx-build {posargs:-E} -b html docs dist/docs -n -q --color 27 | 28 | [testenv:fuzzing] 29 | basepython = python3 30 | commands = 31 | pytest -m fuzzing {posargs:tests/} 32 | extras = 33 | test 34 | whitelist_externals = make 35 | 36 | [testenv:memory] 37 | basepython = python3 38 | commands = 39 | pytest --memorymock {posargs:tests/} 40 | extras = 41 | test 42 | whitelist_externals = make 43 | 44 | [testenv:lint] 45 | basepython = python3 46 | extras = lint 47 | commands = 48 | black -C -t py311 {toxinidir}/vyper {toxinidir}/tests {toxinidir}/setup.py 49 | flake8 {toxinidir}/vyper {toxinidir}/tests 50 | isort {toxinidir}/vyper {toxinidir}/tests {toxinidir}/setup.py 51 | 52 | [testenv:mypy] 53 | basepython = python3 54 | extras = lint 55 | commands = 56 | mypy --install-types --non-interactive --follow-imports=silent --ignore-missing-imports --disallow-incomplete-defs -p vyper 57 | -------------------------------------------------------------------------------- /vyper/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path as _Path 2 | 3 | from vyper.compiler import compile_code, compile_codes # noqa: F401 4 | 5 | try: 6 | from importlib.metadata import PackageNotFoundError # type: ignore 7 | from importlib.metadata import version as _version # type: ignore 8 | except ModuleNotFoundError: 9 | from importlib_metadata import PackageNotFoundError # type: ignore 10 | from importlib_metadata import version as _version # type: ignore 11 | 12 | _commit_hash_file = _Path(__file__).parent.joinpath("vyper_git_commithash.txt") 13 | 14 | if _commit_hash_file.exists(): 15 | with _commit_hash_file.open() as fp: 16 | __commit__ = fp.read() 17 | else: 18 | __commit__ = "unknown" 19 | 20 | try: 21 | __version__ = _version(__name__) 22 | except PackageNotFoundError: 23 | from vyper.version import version as __version__ 24 | -------------------------------------------------------------------------------- /vyper/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: UTF-8 -*- 3 | import sys 4 | 5 | from vyper.cli import vyper_compile, vyper_ir, vyper_serve 6 | 7 | if __name__ == "__main__": 8 | allowed_subcommands = ("--vyper-compile", "--vyper-ir", "--vyper-serve") 9 | 10 | if len(sys.argv) <= 1 or sys.argv[1] not in allowed_subcommands: 11 | # default (no args, no switch in first arg): run vyper_compile 12 | vyper_compile._parse_cli_args() 13 | else: 14 | # pop switch and forward args to subcommand 15 | subcommand = sys.argv.pop(1) 16 | if subcommand == "--vyper-serve": 17 | vyper_serve._parse_cli_args() 18 | elif subcommand == "--vyper-ir": 19 | vyper_ir._parse_cli_args() 20 | else: 21 | vyper_compile._parse_cli_args() 22 | -------------------------------------------------------------------------------- /vyper/ast/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | isort:skip_file 3 | """ 4 | import sys 5 | 6 | from . import nodes, validation 7 | from .natspec import parse_natspec 8 | from .nodes import compare_nodes 9 | from .utils import ast_to_dict, parse_to_ast, parse_to_ast_with_settings 10 | 11 | # adds vyper.ast.nodes classes into the local namespace 12 | for name, obj in ( 13 | (k, v) for k, v in nodes.__dict__.items() if type(v) is type and nodes.VyperNode in v.__mro__ 14 | ): 15 | setattr(sys.modules[__name__], name, obj) 16 | 17 | 18 | # required to avoid circular dependency 19 | from . import expansion, folding # noqa: E402 20 | -------------------------------------------------------------------------------- /vyper/ast/__init__.pyi: -------------------------------------------------------------------------------- 1 | import ast as python_ast 2 | from typing import Any, Optional, Union 3 | 4 | from . import expansion, folding, nodes, validation 5 | from .natspec import parse_natspec as parse_natspec 6 | from .nodes import * 7 | from .utils import ast_to_dict as ast_to_dict 8 | from .utils import parse_to_ast as parse_to_ast 9 | -------------------------------------------------------------------------------- /vyper/ast/grammar.py: -------------------------------------------------------------------------------- 1 | # EXPERIMENTAL VYPER PARSER 2 | import textwrap 3 | 4 | from lark import Lark 5 | from lark.indenter import Indenter 6 | 7 | 8 | class PythonIndenter(Indenter): 9 | NL_type = "_NEWLINE" 10 | OPEN_PAREN_types = ["LPAR", "LSQB", "LBRACE"] 11 | CLOSE_PAREN_types = ["RPAR", "RSQB", "RBRACE"] 12 | INDENT_type = "_INDENT" 13 | DEDENT_type = "_DEDENT" 14 | tab_len = 4 15 | 16 | 17 | _lark_grammar = None 18 | 19 | 20 | def vyper_grammar(): 21 | global _lark_grammar 22 | if _lark_grammar is None: 23 | _lark_grammar = Lark.open_from_package( 24 | "vyper", 25 | "grammar.lark", 26 | ("ast/",), 27 | parser="lalr", 28 | start="module", 29 | postlex=PythonIndenter(), 30 | ) 31 | return _lark_grammar 32 | 33 | 34 | def parse_vyper_source(code, dedent=False): 35 | if dedent: 36 | code = textwrap.dedent(code) 37 | return vyper_grammar().parse(code + "\n") 38 | -------------------------------------------------------------------------------- /vyper/builtins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/vyper/builtins/__init__.py -------------------------------------------------------------------------------- /vyper/builtins/_utils.py: -------------------------------------------------------------------------------- 1 | from vyper.ast import parse_to_ast 2 | from vyper.codegen.context import Context 3 | from vyper.codegen.global_context import GlobalContext 4 | from vyper.codegen.stmt import parse_body 5 | from vyper.semantics.analysis.local import FunctionNodeVisitor 6 | from vyper.semantics.namespace import Namespace, override_global_namespace 7 | from vyper.semantics.types.function import ContractFunctionT, FunctionVisibility, StateMutability 8 | 9 | 10 | def _strip_source_pos(ir_node): 11 | ir_node.source_pos = None 12 | for x in ir_node.args: 13 | _strip_source_pos(x) 14 | 15 | 16 | def generate_inline_function(code, variables, variables_2, memory_allocator): 17 | ast_code = parse_to_ast(code, add_fn_node="dummy_fn") 18 | # Annotate the AST with a temporary old (i.e. typecheck) namespace 19 | namespace = Namespace() 20 | namespace.update(variables_2) 21 | with override_global_namespace(namespace): 22 | # Initialise a placeholder `FunctionDef` AST node and corresponding 23 | # `ContractFunctionT` type to rely on the annotation visitors in semantics 24 | # module. 25 | ast_code.body[0]._metadata["type"] = ContractFunctionT( 26 | "sqrt_builtin", [], [], None, FunctionVisibility.INTERNAL, StateMutability.NONPAYABLE 27 | ) 28 | # The FunctionNodeVisitor's constructor performs semantic checks 29 | # annotate the AST as side effects. 30 | FunctionNodeVisitor(ast_code, ast_code.body[0], namespace) 31 | 32 | new_context = Context( 33 | vars_=variables, global_ctx=GlobalContext(), memory_allocator=memory_allocator 34 | ) 35 | generated_ir = parse_body(ast_code.body[0].body, new_context) 36 | # strip source position info from the generated_ir since 37 | # it doesn't make any sense (e.g. the line numbers will start from 0 38 | # instead of where we are in the code) 39 | # NOTE if we ever use this for inlining user-code, it would make 40 | # sense to fix the offsets of the source positions in the generated 41 | # code instead of stripping them. 42 | _strip_source_pos(generated_ir) 43 | return new_context, generated_ir 44 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/ERC165.py: -------------------------------------------------------------------------------- 1 | interface_code = """ 2 | @view 3 | @external 4 | def supportsInterface(interface_id: bytes4) -> bool: 5 | pass 6 | """ 7 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/ERC20.py: -------------------------------------------------------------------------------- 1 | interface_code = """ 2 | # Events 3 | event Transfer: 4 | _from: indexed(address) 5 | _to: indexed(address) 6 | _value: uint256 7 | 8 | event Approval: 9 | _owner: indexed(address) 10 | _spender: indexed(address) 11 | _value: uint256 12 | 13 | # Functions 14 | @view 15 | @external 16 | def totalSupply() -> uint256: 17 | pass 18 | 19 | @view 20 | @external 21 | def balanceOf(_owner: address) -> uint256: 22 | pass 23 | 24 | @view 25 | @external 26 | def allowance(_owner: address, _spender: address) -> uint256: 27 | pass 28 | 29 | @external 30 | def transfer(_to: address, _value: uint256) -> bool: 31 | pass 32 | 33 | @external 34 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool: 35 | pass 36 | 37 | @external 38 | def approve(_spender: address, _value: uint256) -> bool: 39 | pass 40 | """ 41 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/ERC20Detailed.py: -------------------------------------------------------------------------------- 1 | """ 2 | NOTE: interface uses `String[1]` where 1 is the lower bound of the string returned by the function. 3 | For end-users this means they can't use `implements: ERC20Detailed` unless their implementation 4 | uses a value n >= 1. Regardless this is fine as one can't do String[0] where n == 0. 5 | """ 6 | 7 | interface_code = """ 8 | @view 9 | @external 10 | def name() -> String[1]: 11 | pass 12 | 13 | @view 14 | @external 15 | def symbol() -> String[1]: 16 | pass 17 | 18 | @view 19 | @external 20 | def decimals() -> uint8: 21 | pass 22 | """ 23 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/ERC4626.py: -------------------------------------------------------------------------------- 1 | interface_code = """ 2 | # Events 3 | event Deposit: 4 | sender: indexed(address) 5 | owner: indexed(address) 6 | assets: uint256 7 | shares: uint256 8 | 9 | event Withdraw: 10 | sender: indexed(address) 11 | receiver: indexed(address) 12 | owner: indexed(address) 13 | assets: uint256 14 | shares: uint256 15 | 16 | # Functions 17 | @view 18 | @external 19 | def asset() -> address: 20 | pass 21 | 22 | @view 23 | @external 24 | def totalAssets() -> uint256: 25 | pass 26 | 27 | @view 28 | @external 29 | def convertToShares(assetAmount: uint256) -> uint256: 30 | pass 31 | 32 | @view 33 | @external 34 | def convertToAssets(shareAmount: uint256) -> uint256: 35 | pass 36 | 37 | @view 38 | @external 39 | def maxDeposit(owner: address) -> uint256: 40 | pass 41 | 42 | @view 43 | @external 44 | def previewDeposit(assets: uint256) -> uint256: 45 | pass 46 | 47 | @external 48 | def deposit(assets: uint256, receiver: address=msg.sender) -> uint256: 49 | pass 50 | 51 | @view 52 | @external 53 | def maxMint(owner: address) -> uint256: 54 | pass 55 | 56 | @view 57 | @external 58 | def previewMint(shares: uint256) -> uint256: 59 | pass 60 | 61 | @external 62 | def mint(shares: uint256, receiver: address=msg.sender) -> uint256: 63 | pass 64 | 65 | @view 66 | @external 67 | def maxWithdraw(owner: address) -> uint256: 68 | pass 69 | 70 | @view 71 | @external 72 | def previewWithdraw(assets: uint256) -> uint256: 73 | pass 74 | 75 | @external 76 | def withdraw(assets: uint256, receiver: address=msg.sender, owner: address=msg.sender) -> uint256: 77 | pass 78 | 79 | @view 80 | @external 81 | def maxRedeem(owner: address) -> uint256: 82 | pass 83 | 84 | @view 85 | @external 86 | def previewRedeem(shares: uint256) -> uint256: 87 | pass 88 | 89 | @external 90 | def redeem(shares: uint256, receiver: address=msg.sender, owner: address=msg.sender) -> uint256: 91 | pass 92 | """ 93 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/ERC721.py: -------------------------------------------------------------------------------- 1 | interface_code = """ 2 | # Events 3 | 4 | event Transfer: 5 | _from: indexed(address) 6 | _to: indexed(address) 7 | _tokenId: indexed(uint256) 8 | 9 | event Approval: 10 | _owner: indexed(address) 11 | _approved: indexed(address) 12 | _tokenId: indexed(uint256) 13 | 14 | event ApprovalForAll: 15 | _owner: indexed(address) 16 | _operator: indexed(address) 17 | _approved: bool 18 | 19 | # Functions 20 | 21 | @view 22 | @external 23 | def supportsInterface(interface_id: bytes4) -> bool: 24 | pass 25 | 26 | @view 27 | @external 28 | def balanceOf(_owner: address) -> uint256: 29 | pass 30 | 31 | @view 32 | @external 33 | def ownerOf(_tokenId: uint256) -> address: 34 | pass 35 | 36 | @view 37 | @external 38 | def getApproved(_tokenId: uint256) -> address: 39 | pass 40 | 41 | @view 42 | @external 43 | def isApprovedForAll(_owner: address, _operator: address) -> bool: 44 | pass 45 | 46 | @external 47 | @payable 48 | def transferFrom(_from: address, _to: address, _tokenId: uint256): 49 | pass 50 | 51 | @external 52 | @payable 53 | def safeTransferFrom(_from: address, _to: address, _tokenId: uint256): 54 | pass 55 | 56 | @external 57 | @payable 58 | def safeTransferFrom(_from: address, _to: address, _tokenId: uint256, _data: Bytes[1024]): 59 | pass 60 | 61 | @external 62 | @payable 63 | def approve(_approved: address, _tokenId: uint256): 64 | pass 65 | 66 | @external 67 | def setApprovalForAll(_operator: address, _approved: bool): 68 | pass 69 | 70 | """ 71 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/vyper/builtins/interfaces/__init__.py -------------------------------------------------------------------------------- /vyper/cli/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/vyper/cli/__init__.py -------------------------------------------------------------------------------- /vyper/cli/vyper_ir.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import argparse 3 | import sys 4 | 5 | import vyper 6 | from vyper.codegen.ir_node import IRnode 7 | from vyper.ir import compile_ir, optimizer 8 | from vyper.ir.s_expressions import parse_s_exp 9 | 10 | 11 | def _parse_cli_args(): 12 | return _parse_args(sys.argv[1:]) 13 | 14 | 15 | def _parse_args(argv): 16 | parser = argparse.ArgumentParser(description="Vyper IR IR compiler") 17 | parser.add_argument("input_file", help="Vyper sourcecode to compile") 18 | parser.add_argument( 19 | "--version", action="version", version=f"{vyper.__version__}+commit{vyper.__commit__}" 20 | ) 21 | parser.add_argument( 22 | "-f", 23 | help="Format to print csv list of ir,opt_ir,asm,bytecode", 24 | default="bytecode", 25 | dest="format", 26 | ) 27 | parser.add_argument( 28 | "--show-gas-estimates", help="Show gas estimates in ir output mode.", action="store_true" 29 | ) 30 | 31 | args = parser.parse_args(argv) 32 | output_formats = set(dict.fromkeys(args.format.split(","))) 33 | compiler_data = compile_to_ir(args.input_file, output_formats, args.show_gas_estimates) 34 | 35 | for key in ("ir", "opt_ir", "asm", "bytecode"): 36 | if key in compiler_data: 37 | print(compiler_data[key]) 38 | 39 | 40 | def compile_to_ir(input_file, output_formats, show_gas_estimates=False): 41 | with open(input_file) as fh: 42 | s_expressions = parse_s_exp(fh.read()) 43 | 44 | if show_gas_estimates: 45 | IRnode.repr_show_gas = True 46 | 47 | compiler_data = {} 48 | ir = IRnode.from_list(s_expressions[0]) 49 | ir = optimizer.optimize(ir) 50 | if "ir" in output_formats: 51 | compiler_data["ir"] = ir 52 | 53 | asm = compile_ir.compile_to_assembly(ir) 54 | if "asm" in output_formats: 55 | compiler_data["asm"] = asm 56 | 57 | if "bytecode" in output_formats: 58 | bytecode, _ = compile_ir.assembly_to_evm(asm) 59 | compiler_data["bytecode"] = "0x" + bytecode.hex() 60 | 61 | return compiler_data 62 | 63 | 64 | if __name__ == "__main__": 65 | _parse_cli_args() 66 | -------------------------------------------------------------------------------- /vyper/codegen/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/vyper/codegen/__init__.py -------------------------------------------------------------------------------- /vyper/codegen/function_definitions/__init__.py: -------------------------------------------------------------------------------- 1 | from .common import FuncIR, generate_ir_for_function # noqa 2 | -------------------------------------------------------------------------------- /vyper/codegen/function_definitions/utils.py: -------------------------------------------------------------------------------- 1 | from vyper.evm.opcodes import version_check 2 | from vyper.semantics.types.function import StateMutability 3 | 4 | 5 | def get_nonreentrant_lock(func_type): 6 | if not func_type.nonreentrant: 7 | return ["pass"], ["pass"] 8 | 9 | nkey = func_type.reentrancy_key_position.position 10 | 11 | LOAD, STORE = "sload", "sstore" 12 | if version_check(begin="cancun"): 13 | LOAD, STORE = "tload", "tstore" 14 | 15 | if version_check(begin="berlin"): 16 | # any nonzero values would work here (see pricing as of net gas 17 | # metering); these values are chosen so that downgrading to the 18 | # 0,1 scheme (if it is somehow necessary) is safe. 19 | final_value, temp_value = 3, 2 20 | else: 21 | final_value, temp_value = 0, 1 22 | 23 | check_notset = ["assert", ["ne", temp_value, [LOAD, nkey]]] 24 | 25 | if func_type.mutability == StateMutability.VIEW: 26 | return [check_notset], [["seq"]] 27 | 28 | else: 29 | pre = ["seq", check_notset, [STORE, nkey, temp_value]] 30 | post = [STORE, nkey, final_value] 31 | return [pre], [post] 32 | -------------------------------------------------------------------------------- /vyper/codegen/global_context.py: -------------------------------------------------------------------------------- 1 | from functools import cached_property 2 | from typing import Optional 3 | 4 | from vyper import ast as vy_ast 5 | 6 | 7 | # Datatype to store all global context information. 8 | # TODO: rename me to ModuleT 9 | class GlobalContext: 10 | def __init__(self, module: Optional[vy_ast.Module] = None): 11 | self._module = module 12 | 13 | @cached_property 14 | def functions(self): 15 | return self._module.get_children(vy_ast.FunctionDef) 16 | 17 | @cached_property 18 | def variables(self): 19 | # variables that this module defines, ex. 20 | # `x: uint256` is a private storage variable named x 21 | if self._module is None: # TODO: make self._module never be None 22 | return None 23 | variable_decls = self._module.get_children(vy_ast.VariableDecl) 24 | return {s.target.id: s.target._metadata["varinfo"] for s in variable_decls} 25 | 26 | @property 27 | def immutables(self): 28 | return [t for t in self.variables.values() if t.is_immutable] 29 | 30 | @cached_property 31 | def immutable_section_bytes(self): 32 | return sum([imm.typ.memory_bytes_required for imm in self.immutables]) 33 | -------------------------------------------------------------------------------- /vyper/codegen/keccak256_helper.py: -------------------------------------------------------------------------------- 1 | from math import ceil 2 | 3 | from vyper.codegen.core import bytes_data_ptr, ensure_in_memory, get_bytearray_length 4 | from vyper.codegen.ir_node import IRnode 5 | from vyper.exceptions import CompilerPanic 6 | from vyper.semantics.types.bytestrings import _BytestringT 7 | from vyper.semantics.types.shortcuts import BYTES32_T 8 | from vyper.utils import SHA3_BASE, SHA3_PER_WORD, MemoryPositions, bytes_to_int, keccak256 9 | 10 | 11 | def _check_byteslike(typ): 12 | if not isinstance(typ, _BytestringT) and typ != BYTES32_T: 13 | # NOTE this may be checked at a higher level, but just be safe 14 | raise CompilerPanic("keccak256 only accepts bytes-like objects") 15 | 16 | 17 | def _gas_bound(num_words): 18 | return SHA3_BASE + num_words * SHA3_PER_WORD 19 | 20 | 21 | def keccak256_helper(to_hash, context): 22 | _check_byteslike(to_hash.typ) 23 | 24 | # Can hash literals 25 | # TODO this is dead code. 26 | if isinstance(to_hash, bytes): 27 | return IRnode.from_list(bytes_to_int(keccak256(to_hash)), typ=BYTES32_T) 28 | 29 | # Can hash bytes32 objects 30 | # TODO: Want to generalize to all bytes_M 31 | if to_hash.typ == BYTES32_T: 32 | return IRnode.from_list( 33 | [ 34 | "seq", 35 | ["mstore", MemoryPositions.FREE_VAR_SPACE, to_hash], 36 | ["sha3", MemoryPositions.FREE_VAR_SPACE, 32], 37 | ], 38 | typ=BYTES32_T, 39 | add_gas_estimate=_gas_bound(1), 40 | ) 41 | 42 | to_hash = ensure_in_memory(to_hash, context) 43 | 44 | with to_hash.cache_when_complex("buf") as (b1, to_hash): 45 | data = bytes_data_ptr(to_hash) 46 | len_ = get_bytearray_length(to_hash) 47 | return b1.resolve( 48 | IRnode.from_list( 49 | ["sha3", data, len_], 50 | typ=BYTES32_T, 51 | annotation="keccak256", 52 | add_gas_estimate=_gas_bound(ceil(to_hash.typ.maxlen / 32)), 53 | ) 54 | ) 55 | -------------------------------------------------------------------------------- /vyper/compiler/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dataclasses import dataclass 3 | from enum import Enum 4 | from typing import Optional 5 | 6 | VYPER_COLOR_OUTPUT = os.environ.get("VYPER_COLOR_OUTPUT", "0") == "1" 7 | VYPER_ERROR_CONTEXT_LINES = int(os.environ.get("VYPER_ERROR_CONTEXT_LINES", "1")) 8 | VYPER_ERROR_LINE_NUMBERS = os.environ.get("VYPER_ERROR_LINE_NUMBERS", "1") == "1" 9 | 10 | VYPER_TRACEBACK_LIMIT: Optional[int] 11 | 12 | _tb_limit_str = os.environ.get("VYPER_TRACEBACK_LIMIT") 13 | if _tb_limit_str is not None: 14 | VYPER_TRACEBACK_LIMIT = int(_tb_limit_str) 15 | else: 16 | VYPER_TRACEBACK_LIMIT = None 17 | 18 | 19 | class OptimizationLevel(Enum): 20 | NONE = 1 21 | GAS = 2 22 | CODESIZE = 3 23 | 24 | @classmethod 25 | def from_string(cls, val): 26 | match val: 27 | case "none": 28 | return cls.NONE 29 | case "gas": 30 | return cls.GAS 31 | case "codesize": 32 | return cls.CODESIZE 33 | raise ValueError(f"unrecognized optimization level: {val}") 34 | 35 | @classmethod 36 | def default(cls): 37 | return cls.GAS 38 | 39 | 40 | @dataclass 41 | class Settings: 42 | compiler_version: Optional[str] = None 43 | optimize: Optional[OptimizationLevel] = None 44 | evm_version: Optional[str] = None 45 | 46 | 47 | _DEBUG = False 48 | 49 | 50 | def _is_debug_mode(): 51 | global _DEBUG 52 | return _DEBUG 53 | 54 | 55 | def _set_debug_mode(dbg: bool = False) -> None: 56 | global _DEBUG 57 | _DEBUG = dbg 58 | -------------------------------------------------------------------------------- /vyper/compiler/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from vyper.semantics.types.function import ContractFunctionT 4 | 5 | 6 | def build_gas_estimates(func_ts: Dict[str, ContractFunctionT]) -> dict: 7 | # note: `.gas_estimate` is added to ContractFunctionT._ir_info 8 | # in vyper/semantics/types/function.py 9 | ret = {} 10 | for k, v in func_ts.items(): 11 | ret[k] = v._ir_info.gas_estimate 12 | return ret 13 | 14 | 15 | def expand_source_map(compressed_map: str) -> list: 16 | """ 17 | Expand a compressed source map string. 18 | 19 | Arguments 20 | --------- 21 | compressed_map : str 22 | `sourceMap` as generated by the compiler, i.e. "1:1:0;;;;3::2;;;4;" 23 | 24 | Returns 25 | ------- 26 | List 27 | Expanded source map as `[[start, length, jump, source id], .. ]` 28 | """ 29 | source_map: list = [_expand_row(i) if i else None for i in compressed_map.split(";")[:-1]] 30 | 31 | for i, value in enumerate(source_map[1:], 1): 32 | if value is None: 33 | source_map[i] = source_map[i - 1][:3] + [None] 34 | continue 35 | for x in range(3): 36 | if source_map[i][x] is None: 37 | source_map[i][x] = source_map[i - 1][x] 38 | 39 | return source_map 40 | 41 | 42 | def _expand_row(row): 43 | result = [None] * 4 44 | for i, value in enumerate(row.split(":")): 45 | if value: 46 | result[i] = value if i == 3 else int(value) 47 | return result 48 | -------------------------------------------------------------------------------- /vyper/evm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/vyper/evm/__init__.py -------------------------------------------------------------------------------- /vyper/evm/address_space.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Optional 3 | 4 | # TODO consider renaming this module to avoid confusion with the EVM 5 | # concept of addresses (160-bit account IDs). 6 | 7 | 8 | @dataclass(frozen=True) 9 | class AddrSpace: 10 | """ 11 | Object representing info about the "address space", analogous to the 12 | LLVM concept. It includes some metadata so that codegen can be 13 | written in a more generic way. 14 | 15 | Attributes: 16 | name: human-readable nickname for the address space 17 | word_scale: a constant which helps calculate offsets in a given 18 | address space. 1 for word-addressable locations (storage), 19 | 32 for byte-addressable locations (memory, calldata, code) 20 | load_op: the opcode for loading a word from this address space 21 | store_op: the opcode for storing a word to this address space 22 | (an address space is read-only if store_op is None) 23 | """ 24 | 25 | name: str 26 | word_scale: int 27 | load_op: str 28 | # TODO maybe make positional instead of defaulting to None 29 | store_op: Optional[str] = None 30 | 31 | @property 32 | def word_addressable(self) -> bool: 33 | return self.word_scale == 1 34 | 35 | @property 36 | def byte_addressable(self) -> bool: 37 | return self.word_scale == 32 38 | 39 | 40 | # alternative: 41 | # class Memory(AddrSpace): 42 | # @property 43 | # def word_scale(self): 44 | # return 32 45 | # # implement more properties... 46 | # 47 | # MEMORY = Memory() 48 | 49 | MEMORY = AddrSpace("memory", 32, "mload", "mstore") 50 | STORAGE = AddrSpace("storage", 1, "sload", "sstore") 51 | TRANSIENT = AddrSpace("transient", 1, "tload", "tstore") 52 | CALLDATA = AddrSpace("calldata", 32, "calldataload") 53 | # immutables address space: "immutables" section of memory 54 | # which is read-write in deploy code but then gets turned into 55 | # the "data" section of the runtime code 56 | IMMUTABLES = AddrSpace("immutables", 32, "iload", "istore") 57 | # data addrspace: "data" section of runtime code, read-only. 58 | DATA = AddrSpace("data", 32, "dload") 59 | -------------------------------------------------------------------------------- /vyper/ir/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cyfrin/2023-09-vyper-compiler/46449fe7bde9a9a9c438f2a6f7edce54e8927413/vyper/ir/__init__.py -------------------------------------------------------------------------------- /vyper/ir/s_expressions.py: -------------------------------------------------------------------------------- 1 | # Adapted from https://en.wikipedia.org/wiki/S-expression#Parsing 2 | 3 | 4 | def parse_literal(word): 5 | try: 6 | return int(word) 7 | except ValueError: 8 | return word 9 | 10 | 11 | def parse_s_exp(string): 12 | sexp = [[]] 13 | word = "" 14 | in_str = False 15 | in_comment = False 16 | for char in string: 17 | if in_comment: 18 | if char == "\n": # comment ends at the end of a line 19 | in_comment = False 20 | continue 21 | if char == ";": # start of comment 22 | in_comment = True 23 | continue 24 | if char == "(" and not in_str: 25 | sexp.append([]) 26 | elif char == ")" and not in_str: 27 | if word: 28 | sexp[-1].append(parse_literal(word)) 29 | word = "" 30 | temp = sexp.pop() 31 | sexp[-1].append(temp) 32 | elif char in (" ", "\n", "\t") and not in_str: 33 | if word: 34 | sexp[-1].append(parse_literal(word)) 35 | word = "" 36 | elif char == '"': 37 | in_str = not in_str 38 | else: 39 | word += char 40 | return sexp[0] 41 | -------------------------------------------------------------------------------- /vyper/semantics/__init__.py: -------------------------------------------------------------------------------- 1 | from .analysis import validate_semantics 2 | from .analysis.data_positions import set_data_positions 3 | -------------------------------------------------------------------------------- /vyper/semantics/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | import vyper.ast as vy_ast 2 | 3 | from .. import types # break a dependency cycle. 4 | from ..namespace import get_namespace 5 | from .local import validate_functions 6 | from .module import add_module_namespace 7 | from .utils import _ExprAnalyser 8 | 9 | 10 | def validate_semantics(vyper_ast, interface_codes): 11 | # validate semantics and annotate AST with type/semantics information 12 | namespace = get_namespace() 13 | 14 | with namespace.enter_scope(): 15 | add_module_namespace(vyper_ast, interface_codes) 16 | vy_ast.expansion.expand_annotated_ast(vyper_ast) 17 | validate_functions(vyper_ast) 18 | -------------------------------------------------------------------------------- /vyper/semantics/analysis/common.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from vyper.exceptions import StructureException 4 | 5 | 6 | class VyperNodeVisitorBase: 7 | ignored_types: Tuple = () 8 | scope_name = "" 9 | 10 | def visit(self, node, *args): 11 | if isinstance(node, self.ignored_types): 12 | return 13 | node_type = type(node).__name__ 14 | visitor_fn = getattr(self, f"visit_{node_type}", None) 15 | if visitor_fn is None: 16 | raise StructureException( 17 | f"Unsupported syntax for {self.scope_name} namespace: {node_type}", node 18 | ) 19 | visitor_fn(node, *args) 20 | -------------------------------------------------------------------------------- /vyper/semantics/data_locations.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | 4 | class DataLocation(enum.Enum): 5 | UNSET = 0 6 | MEMORY = 1 7 | STORAGE = 2 8 | CALLDATA = 3 9 | CODE = 4 10 | # XXX: needed for separate transient storage allocator 11 | # TRANSIENT = 5 12 | -------------------------------------------------------------------------------- /vyper/semantics/environment.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | from vyper.semantics.analysis.base import VarInfo 4 | from vyper.semantics.types import AddressT, BytesT, VyperType 5 | from vyper.semantics.types.shortcuts import BYTES32_T, UINT256_T 6 | 7 | 8 | # common properties for environment variables 9 | class _EnvType(VyperType): 10 | def __eq__(self, other): 11 | return self is other 12 | 13 | def __hash__(self): 14 | return hash(id(self)) 15 | 16 | 17 | class _Block(_EnvType): 18 | _id = "block" 19 | _type_members = { 20 | "coinbase": AddressT(), 21 | "difficulty": UINT256_T, 22 | "prevrandao": UINT256_T, 23 | "number": UINT256_T, 24 | "gaslimit": UINT256_T, 25 | "basefee": UINT256_T, 26 | "prevhash": BYTES32_T, 27 | "timestamp": UINT256_T, 28 | } 29 | 30 | 31 | class _Chain(_EnvType): 32 | _id = "chain" 33 | _type_members = {"id": UINT256_T} 34 | 35 | 36 | class _Msg(_EnvType): 37 | _id = "msg" 38 | _type_members = {"data": BytesT(), "gas": UINT256_T, "sender": AddressT(), "value": UINT256_T} 39 | 40 | 41 | class _Tx(_EnvType): 42 | _id = "tx" 43 | _type_members = {"origin": AddressT(), "gasprice": UINT256_T} 44 | 45 | 46 | CONSTANT_ENVIRONMENT_VARS = {t._id: t for t in (_Block(), _Chain(), _Tx(), _Msg())} 47 | 48 | 49 | def get_constant_vars() -> Dict: 50 | """ 51 | Get a dictionary of constant environment variables. 52 | """ 53 | result = {} 54 | for k, v in CONSTANT_ENVIRONMENT_VARS.items(): 55 | result[k] = VarInfo(v, is_constant=True) 56 | 57 | return result 58 | 59 | 60 | MUTABLE_ENVIRONMENT_VARS: Dict[str, type] = {"self": AddressT} 61 | 62 | 63 | def get_mutable_vars() -> Dict: 64 | """ 65 | Get a dictionary of mutable environment variables (those that are 66 | modified during the course of contract execution, such as `self`). 67 | """ 68 | return {name: VarInfo(type_()) for name, type_ in MUTABLE_ENVIRONMENT_VARS.items()} 69 | -------------------------------------------------------------------------------- /vyper/semantics/types/__init__.py: -------------------------------------------------------------------------------- 1 | from . import primitives, subscriptable, user 2 | from .base import TYPE_T, KwargSettings, VyperType, is_type_t 3 | from .bytestrings import BytesT, StringT, _BytestringT 4 | from .function import MemberFunctionT 5 | from .primitives import AddressT, BoolT, BytesM_T, DecimalT, IntegerT 6 | from .subscriptable import DArrayT, HashMapT, SArrayT, TupleT 7 | from .user import EnumT, EventT, InterfaceT, StructT 8 | 9 | 10 | def _get_primitive_types(): 11 | res = [BoolT(), DecimalT()] 12 | 13 | res.extend(IntegerT.all()) 14 | res.extend(BytesM_T.all()) 15 | 16 | # order of the types matters! 17 | # parsing of literal hex: prefer address over bytes20 18 | res.append(AddressT()) 19 | 20 | # note: since bytestrings are parametrizable, the *class* objects 21 | # are in the namespace instead of concrete type objects. 22 | res.extend([BytesT, StringT]) 23 | 24 | ret = {t._id: t for t in res} 25 | ret.update(_get_sequence_types()) 26 | 27 | return ret 28 | 29 | 30 | def _get_sequence_types(): 31 | # since these guys are parametrizable, the *class* objects 32 | # are in the namespace instead of concrete type objects. 33 | 34 | res = [HashMapT, DArrayT] 35 | 36 | ret = {t._id: t for t in res} 37 | 38 | # (static) arrays and tuples are special types which don't show up 39 | # in the type annotation itself. 40 | # since we don't have special handling of annotations in the parser, 41 | # break a dependency cycle by injecting these into the namespace with 42 | # mangled names (that no user can create). 43 | ret["$SArrayT"] = SArrayT 44 | ret["$TupleT"] = TupleT 45 | 46 | return ret 47 | 48 | 49 | # note: it might be good to make this a frozen dict of some sort 50 | PRIMITIVE_TYPES = _get_primitive_types() 51 | -------------------------------------------------------------------------------- /vyper/semantics/types/shortcuts.py: -------------------------------------------------------------------------------- 1 | from vyper.semantics.types.primitives import SINT, UINT, BytesM_T, IntegerT 2 | 3 | # shortcut type names 4 | UINT256_T = IntegerT(False, 256) 5 | UINT8_T = IntegerT(False, 8) 6 | INT256_T = IntegerT(True, 256) 7 | INT128_T = IntegerT(True, 128) 8 | UINT160_T = IntegerT(False, 160) 9 | 10 | BYTES32_T = BytesM_T(32) 11 | BYTES20_T = BytesM_T(20) 12 | BYTES4_T = BytesM_T(4) 13 | 14 | _ = UINT, SINT # explicitly use: addresses linter F401 15 | -------------------------------------------------------------------------------- /vyper/typing.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional, Sequence, Tuple, Union 2 | 3 | # Parser 4 | ModificationOffsets = Dict[Tuple[int, int], str] 5 | ParserPosition = Tuple[int, int] 6 | 7 | # Compiler 8 | ContractPath = str 9 | SourceCode = str 10 | ContractCodes = Dict[ContractPath, SourceCode] 11 | OutputFormats = Sequence[str] 12 | OutputDict = Dict[ContractPath, OutputFormats] 13 | StorageLayout = Dict 14 | 15 | # Interfaces 16 | InterfaceAsName = str 17 | InterfaceImportPath = str 18 | InterfaceImports = Dict[InterfaceAsName, InterfaceImportPath] 19 | InterfaceDict = Dict[ContractPath, InterfaceImports] 20 | 21 | # Opcodes 22 | OpcodeGasCost = Union[int, Tuple] 23 | OpcodeValue = Tuple[Optional[int], int, int, OpcodeGasCost] 24 | OpcodeMap = Dict[str, OpcodeValue] 25 | OpcodeRulesetValue = Tuple[Optional[int], int, int, int] 26 | OpcodeRulesetMap = Dict[str, OpcodeRulesetValue] 27 | -------------------------------------------------------------------------------- /vyper/warnings.py: -------------------------------------------------------------------------------- 1 | # TODO: Create VyperWarning class similarly to what is being done with exceptinos? 2 | class ContractSizeLimitWarning(Warning): 3 | pass 4 | --------------------------------------------------------------------------------