├── tests ├── __init__.py ├── unit │ ├── __init__.py │ ├── compiler │ │ ├── ir │ │ │ ├── __init__.py │ │ │ ├── test_calldatacopy.py │ │ │ └── test_repeat.py │ │ ├── __init__.py │ │ ├── venom │ │ │ ├── test_stack_at_external_return.py │ │ │ ├── test_variables.py │ │ │ ├── test_liveness_simple_loop.py │ │ │ ├── test_stack_cleanup.py │ │ │ ├── test_simplify_cfg.py │ │ │ ├── test_duplicate_operands.py │ │ │ ├── test_stack_reorder.py │ │ │ ├── test_make_ssa.py │ │ │ └── test_convert_basicblock_simple.py │ │ ├── test_sha3_32.py │ │ ├── test_default_settings.py │ │ └── test_compile_code.py │ ├── cli │ │ ├── storage_layout │ │ │ ├── __init__.py │ │ │ └── utils.py │ │ ├── vyper_compile │ │ │ └── test_parse_args.py │ │ └── vyper_json │ │ │ └── test_get_settings.py │ ├── utils │ │ └── test_keccak256.py │ ├── ast │ │ ├── nodes │ │ │ ├── test_binary.py │ │ │ ├── test_get_children.py │ │ │ ├── test_from_node.py │ │ │ ├── test_fold_unaryop.py │ │ │ ├── test_compare_nodes.py │ │ │ ├── test_hex.py │ │ │ └── test_fold_subscript.py │ │ ├── test_parser.py │ │ ├── test_metadata_journal.py │ │ └── test_annotate_and_optimize_ast.py │ ├── abi_types │ │ └── test_invalid_abi_types.py │ └── semantics │ │ ├── conftest.py │ │ └── types │ │ ├── test_type_from_abi.py │ │ └── test_size_in_bytes.py ├── functional │ ├── __init__.py │ ├── codegen │ │ ├── __init__.py │ │ ├── modules │ │ │ ├── __init__.py │ │ │ ├── test_flag_imports.py │ │ │ ├── test_events.py │ │ │ └── test_nonreentrant.py │ │ ├── features │ │ │ ├── test_gas.py │ │ │ ├── test_mana.py │ │ │ ├── test_comments.py │ │ │ ├── decorators │ │ │ │ ├── test_public.py │ │ │ │ └── test_view.py │ │ │ ├── test_memory_alloc.py │ │ │ ├── test_address_balance.py │ │ │ ├── test_string_map_keys.py │ │ │ ├── test_memory_dealloc.py │ │ │ ├── test_conditionals.py │ │ │ ├── test_reverting.py │ │ │ ├── test_packing.py │ │ │ └── test_comparison.py │ │ ├── environment_variables │ │ │ ├── test_tx.py │ │ │ ├── test_block_number.py │ │ │ ├── test_blockhash.py │ │ │ └── test_blobbasefee.py │ │ ├── integration │ │ │ └── test_basics.py │ │ ├── storage_variables │ │ │ └── test_storage_variable.py │ │ ├── types │ │ │ ├── numbers │ │ │ │ └── test_division.py │ │ │ ├── test_bytes_zero_padding.py │ │ │ ├── test_string_literal.py │ │ │ ├── test_struct.py │ │ │ └── test_node_types.py │ │ └── calling_convention │ │ │ └── test_self_call_struct.py │ ├── syntax │ │ ├── __init__.py │ │ ├── modules │ │ │ ├── __init__.py │ │ │ ├── helpers.py │ │ │ ├── test_implements.py │ │ │ └── test_deploy_visibility.py │ │ ├── test_ceil.py │ │ ├── test_floor.py │ │ ├── exceptions │ │ │ ├── test_unknown_type.py │ │ │ ├── test_variable_declaration_exception.py │ │ │ ├── test_call_violation.py │ │ │ ├── test_invalid_payable.py │ │ │ ├── test_overflow_exception.py │ │ │ ├── test_invalid_reference.py │ │ │ ├── test_invalid_literal_exception.py │ │ │ ├── test_namespace_collision.py │ │ │ ├── test_function_declaration_exception.py │ │ │ ├── test_invalid_type_exception.py │ │ │ ├── test_type_mismatch_exception.py │ │ │ ├── test_undeclared_definition.py │ │ │ └── test_vyper_exception_pos.py │ │ ├── test_uint2str.py │ │ ├── test_unary.py │ │ ├── test_epsilon.py │ │ ├── test_conditionals.py │ │ ├── test_self_balance.py │ │ ├── test_selfdestruct.py │ │ ├── test_minmax_value.py │ │ ├── signatures │ │ │ └── test_invalid_function_decorators.py │ │ ├── test_powmod.py │ │ ├── test_abs.py │ │ ├── test_as_uint256.py │ │ ├── test_print.py │ │ ├── test_bool_ops.py │ │ ├── test_abi_decode.py │ │ ├── test_code_size.py │ │ ├── test_method_id.py │ │ ├── test_public.py │ │ ├── test_codehash.py │ │ ├── test_init.py │ │ ├── test_len.py │ │ ├── test_addmulmod.py │ │ ├── test_dynamic_array.py │ │ ├── test_keccak256.py │ │ ├── test_minmax.py │ │ ├── test_string.py │ │ ├── test_functions_call.py │ │ ├── test_nested_list.py │ │ ├── test_slice.py │ │ └── names │ │ │ └── test_variable_names.py │ ├── venom │ │ ├── __init__.py │ │ └── parser │ │ │ └── __init__.py │ ├── builtins │ │ ├── codegen │ │ │ ├── __init__.py │ │ │ ├── test_is_contract.py │ │ │ ├── test_length.py │ │ │ ├── test_uint2str.py │ │ │ ├── test_blobhash.py │ │ │ ├── test_minmax_value.py │ │ │ └── test_method_id.py │ │ └── folding │ │ │ ├── test_epsilon.py │ │ │ ├── test_powmod.py │ │ │ ├── test_addmod_mulmod.py │ │ │ ├── test_floor_ceil.py │ │ │ ├── test_len.py │ │ │ ├── test_abs.py │ │ │ └── test_fold_as_wei_value.py │ └── examples │ │ ├── name_registry │ │ └── test_name_registry.py │ │ ├── storage │ │ └── test_storage.py │ │ └── crowdfund │ │ └── test_crowdfund_example.py ├── evm_backends │ └── __init__.py ├── integration │ └── test_pickle_ast.py ├── ast_utils.py ├── utils.py └── venom_utils.py ├── vyper ├── cli │ └── __init__.py ├── evm │ └── __init__.py ├── ir │ ├── __init__.py │ └── s_expressions.py ├── builtins │ ├── __init__.py │ └── interfaces │ │ ├── IERC165.vyi │ │ ├── IERC20Detailed.vyi │ │ ├── IERC20.vyi │ │ ├── IERC721.vyi │ │ └── IERC4626.vyi ├── codegen │ ├── __init__.py │ ├── function_definitions │ │ └── __init__.py │ └── keccak256_helper.py ├── venom │ ├── passes │ │ ├── sccp │ │ │ └── __init__.py │ │ ├── base_pass.py │ │ ├── __init__.py │ │ ├── float_allocas.py │ │ ├── store_expansion.py │ │ ├── remove_unused_variables.py │ │ ├── store_elimination.py │ │ ├── lower_dload.py │ │ ├── branch_optimization.py │ │ └── load_elimination.py │ └── analysis │ │ ├── __init__.py │ │ └── equivalent_vars.py ├── semantics │ ├── __init__.py │ ├── analysis │ │ ├── __init__.py │ │ └── common.py │ ├── data_locations.py │ └── types │ │ ├── shortcuts.py │ │ └── __init__.py ├── warnings.py ├── ast │ ├── __init__.pyi │ ├── __init__.py │ ├── grammar.py │ └── utils.py ├── typing.py ├── __main__.py ├── __init__.py └── compiler │ └── utils.py ├── requirements-docs.txt ├── FUNDING.yml ├── codecov.yml ├── examples ├── storage │ ├── storage.vy │ └── advanced_storage.vy ├── name_registry │ └── name_registry.vy ├── factory │ ├── Exchange.vy │ └── Factory.vy └── crowdfund.vy ├── FUNDING.json ├── hooks └── build ├── .readthedocs.yaml ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug.md │ └── vip.md └── workflows │ ├── codeql.yml │ ├── release-pypi.yml │ └── pull-request.yaml ├── pyproject.toml ├── quicktest.sh ├── docs ├── testing-contracts-titanoboa.rst ├── Makefile ├── testing-contracts.rst ├── make.bat ├── logo.svg ├── toctree.rst ├── deploying-contracts.rst └── _templates │ └── versions.html ├── .gitignore ├── .pre-commit-config.yaml ├── Dockerfile ├── setup.cfg └── make.cmd /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vyper/cli/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vyper/evm/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vyper/ir/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vyper/builtins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vyper/codegen/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/evm_backends/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/codegen/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/syntax/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/venom/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/compiler/ir/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/syntax/modules/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/venom/parser/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/cli/storage_layout/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/builtins/codegen/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/functional/codegen/modules/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vyper/venom/passes/sccp/__init__.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.passes.sccp.sccp import SCCP 2 | -------------------------------------------------------------------------------- /requirements-docs.txt: -------------------------------------------------------------------------------- 1 | shibuya==2024.1.17 2 | sphinx==7.2.6 3 | sphinx-copybutton==0.5.2 4 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/IERC165.vyi: -------------------------------------------------------------------------------- 1 | @view 2 | @external 3 | def supportsInterface(interface_id: bytes4) -> bool: 4 | ... 5 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://etherscan.io/address/0x70CCBE10F980d80b7eBaab7D2E3A73e87D67B775", "https://giveth.io/project/vyper"] 2 | -------------------------------------------------------------------------------- /tests/unit/compiler/__init__.py: -------------------------------------------------------------------------------- 1 | # prevent module name collision between tests/compiler/test_pre_parser.py 2 | # and tests/ast/test_pre_parser.py 3 | -------------------------------------------------------------------------------- /vyper/semantics/__init__.py: -------------------------------------------------------------------------------- 1 | from .analysis import analyze_module, validate_compilation_target 2 | from .analysis.data_positions import set_data_positions 3 | -------------------------------------------------------------------------------- /vyper/warnings.py: -------------------------------------------------------------------------------- 1 | # TODO: Create VyperWarning class similarly to what is being done with exceptinos? 2 | class ContractSizeLimitWarning(Warning): 3 | pass 4 | -------------------------------------------------------------------------------- /tests/functional/syntax/modules/helpers.py: -------------------------------------------------------------------------------- 1 | NONREENTRANT_NOTE = ( 2 | "\n note that use of the `@nonreentrant` decorator is also considered state access" 3 | ) 4 | -------------------------------------------------------------------------------- /tests/unit/compiler/ir/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/unit/compiler/venom/test_stack_at_external_return.py: -------------------------------------------------------------------------------- 1 | def test_stack_at_external_return(): 2 | """ 3 | TODO: USE BOA DO GENERATE THIS TEST 4 | """ 5 | pass 6 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # https://docs.codecov.com/docs/codecovyml-reference 2 | coverage: 3 | status: 4 | project: 5 | default: 6 | # set threshold given noise in the coverage from fuzzing 7 | threshold: 0.5% 8 | -------------------------------------------------------------------------------- /examples/storage/storage.vy: -------------------------------------------------------------------------------- 1 | #pragma version >0.3.10 2 | 3 | storedData: public(int128) 4 | 5 | @deploy 6 | def __init__(_x: int128): 7 | self.storedData = _x 8 | 9 | @external 10 | def set(_x: int128): 11 | self.storedData = _x 12 | -------------------------------------------------------------------------------- /tests/unit/compiler/venom/test_variables.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.basicblock import IRVariable 2 | 3 | 4 | def test_variable_equality(): 5 | v1 = IRVariable("%x") 6 | v2 = IRVariable("%x") 7 | assert v1 == v2 8 | assert v1 != IRVariable("%y") 9 | -------------------------------------------------------------------------------- /vyper/semantics/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | from .. import types # break a dependency cycle. 2 | from .global_ import validate_compilation_target 3 | from .module import analyze_module 4 | 5 | __all__ = [validate_compilation_target, analyze_module] # type: ignore[misc] 6 | -------------------------------------------------------------------------------- /FUNDING.json: -------------------------------------------------------------------------------- 1 | { 2 | "drips": { 3 | "ethereum": { 4 | "ownedBy": "0x70CCBE10F980d80b7eBaab7D2E3A73e87D67B775" 5 | } 6 | }, 7 | "opRetro": { 8 | "projectId": "0x9ca1f7b0e0d10d3bd2619e51a54f2e4175e029c87a2944cf1ebc89164ba77ea0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/unit/utils/test_keccak256.py: -------------------------------------------------------------------------------- 1 | def test_keccak_sanity(keccak): 2 | # sanity check -- ensure keccak is keccak256, not sha3 3 | # https://ethereum.stackexchange.com/a/107985 4 | assert keccak(b"").hex() == "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" 5 | -------------------------------------------------------------------------------- /vyper/codegen/function_definitions/__init__.py: -------------------------------------------------------------------------------- 1 | from .external_function import generate_ir_for_external_function 2 | from .internal_function import generate_ir_for_internal_function 3 | 4 | __all__ = [generate_ir_for_internal_function, generate_ir_for_external_function] # type: ignore 5 | -------------------------------------------------------------------------------- /vyper/venom/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | from .analysis import IRAnalysesCache, IRAnalysis 2 | from .cfg import CFGAnalysis 3 | from .dfg import DFGAnalysis 4 | from .dominators import DominatorTreeAnalysis 5 | from .equivalent_vars import VarEquivalenceAnalysis 6 | from .liveness import LivenessAnalysis 7 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/test_gas.py: -------------------------------------------------------------------------------- 1 | def test_gas_call(get_contract): 2 | gas_call = """ 3 | @external 4 | def foo() -> uint256: 5 | return msg.gas 6 | """ 7 | 8 | c = get_contract(gas_call) 9 | 10 | assert c.foo(gas=50000) < 50000 11 | assert c.foo(gas=50000) > 25000 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/test_mana.py: -------------------------------------------------------------------------------- 1 | def test_mana_call(get_contract): 2 | mana_call = """ 3 | @external 4 | def foo() -> uint256: 5 | return msg.mana 6 | """ 7 | 8 | c = get_contract(mana_call) 9 | 10 | assert c.foo(gas=50000) < 50000 11 | assert c.foo(gas=50000) > 25000 12 | -------------------------------------------------------------------------------- /vyper/ast/__init__.pyi: -------------------------------------------------------------------------------- 1 | import ast as python_ast 2 | from typing import Any, Optional, Union 3 | 4 | from . import nodes, validation 5 | from .natspec import parse_natspec as parse_natspec 6 | from .nodes import * 7 | from .parse import parse_to_ast as parse_to_ast 8 | from .utils import ast_to_dict as ast_to_dict 9 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/test_comments.py: -------------------------------------------------------------------------------- 1 | def test_comment_test(get_contract): 2 | comment_test = """ 3 | @external 4 | def foo() -> int128: 5 | # Returns 3 6 | return 3 7 | """ 8 | 9 | c = get_contract(comment_test) 10 | assert c.foo() == 3 11 | print("Passed comment test") 12 | -------------------------------------------------------------------------------- /vyper/semantics/data_locations.py: -------------------------------------------------------------------------------- 1 | import enum 2 | 3 | from vyper.utils import StringEnum 4 | 5 | 6 | class DataLocation(StringEnum): 7 | UNSET = enum.auto() 8 | MEMORY = enum.auto() 9 | STORAGE = enum.auto() 10 | CALLDATA = enum.auto() 11 | CODE = enum.auto() 12 | TRANSIENT = enum.auto() 13 | -------------------------------------------------------------------------------- /tests/functional/codegen/environment_variables/test_tx.py: -------------------------------------------------------------------------------- 1 | def test_tx_gasprice(get_contract, env): 2 | code = """ 3 | @external 4 | def tx_gasprice() -> uint256: 5 | return tx.gasprice 6 | """ 7 | env.set_balance(env.deployer, 10**20) 8 | c = get_contract(code) 9 | for i in range(10): 10 | assert c.tx_gasprice(gas_price=10**i) == 10**i 11 | -------------------------------------------------------------------------------- /tests/functional/codegen/environment_variables/test_block_number.py: -------------------------------------------------------------------------------- 1 | def test_block_number(get_contract, env): 2 | block_number_code = """ 3 | @external 4 | def block_number() -> uint256: 5 | return block.number 6 | """ 7 | c = get_contract(block_number_code) 8 | 9 | assert c.block_number() == 1 10 | env.block_number += 1 11 | assert c.block_number() == 2 12 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/decorators/test_public.py: -------------------------------------------------------------------------------- 1 | from vyper.exceptions import FunctionDeclarationException 2 | 3 | 4 | def test_invalid_if_both_public_and_internal(assert_compile_failed, get_contract): 5 | code = """ 6 | @external 7 | @internal 8 | def foo(): 9 | x: uint256 = 1 10 | """ 11 | 12 | assert_compile_failed(lambda: get_contract(code), FunctionDeclarationException) 13 | -------------------------------------------------------------------------------- /tests/functional/examples/name_registry/test_name_registry.py: -------------------------------------------------------------------------------- 1 | def test_name_registry(env, get_contract, tx_failed): 2 | a0, a1 = env.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) 7 | assert c.lookup(b"jacques") == a0 8 | with tx_failed(): 9 | c.register(b"jacques", a1) 10 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_ceil.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | 5 | valid_list = [ 6 | """ 7 | BAR: constant(decimal) = 2.5 8 | FOO: constant(int256) = ceil(BAR) 9 | 10 | @external 11 | def foo(): 12 | a: int256 = FOO 13 | """ 14 | ] 15 | 16 | 17 | @pytest.mark.parametrize("code", valid_list) 18 | def test_ceil_good(code): 19 | assert compile_code(code) is not None 20 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_floor.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | 5 | valid_list = [ 6 | """ 7 | BAR: constant(decimal) = 2.5 8 | FOO: constant(int256) = floor(BAR) 9 | 10 | @external 11 | def foo(): 12 | a: int256 = FOO 13 | """ 14 | ] 15 | 16 | 17 | @pytest.mark.parametrize("code", valid_list) 18 | def test_floor_good(code): 19 | assert compile_code(code) is not None 20 | -------------------------------------------------------------------------------- /examples/name_registry/name_registry.vy: -------------------------------------------------------------------------------- 1 | #pragma version >0.3.10 2 | 3 | registry: HashMap[Bytes[100], address] 4 | 5 | @external 6 | def register(name: Bytes[100], owner: address): 7 | assert self.registry[name] == empty(address) # check name has not been set yet. 8 | self.registry[name] = owner 9 | 10 | 11 | @view 12 | @external 13 | def lookup(name: Bytes[100]) -> address: 14 | return self.registry[name] 15 | -------------------------------------------------------------------------------- /tests/functional/syntax/exceptions/test_unknown_type.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import UnknownType 5 | 6 | 7 | def test_unknown_type_exception(): 8 | code = """ 9 | @internal 10 | def foobar(token: IERC20): 11 | pass 12 | """ 13 | with pytest.raises(UnknownType) as e: 14 | compiler.compile_code(code) 15 | assert "(hint: )" not in str(e.value) 16 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_uint2str.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | 5 | valid_list = [ 6 | """ 7 | FOO: constant(uint256) = 3 8 | BAR: constant(String[78]) = uint2str(FOO) 9 | 10 | @external 11 | def foo(): 12 | a: String[78] = BAR 13 | """ 14 | ] 15 | 16 | 17 | @pytest.mark.parametrize("code", valid_list) 18 | def test_addmulmod_pass(code): 19 | assert compile_code(code) is not None 20 | -------------------------------------------------------------------------------- /tests/unit/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/functional/codegen/integration/test_basics.py: -------------------------------------------------------------------------------- 1 | def test_null_code(get_contract): 2 | null_code = """ 3 | @external 4 | def foo(): 5 | pass 6 | """ 7 | c = get_contract(null_code) 8 | c.foo() 9 | 10 | 11 | def test_basic_code(get_contract): 12 | basic_code = """ 13 | @external 14 | def foo(x: int128) -> int128: 15 | return x * 2 16 | 17 | """ 18 | c = get_contract(basic_code) 19 | assert c.foo(9) == 18 20 | -------------------------------------------------------------------------------- /tests/integration/test_pickle_ast.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import pickle 3 | 4 | from vyper.compiler.phases import CompilerData 5 | 6 | 7 | def test_pickle_ast(): 8 | code = """ 9 | @external 10 | def foo(): 11 | self.bar() 12 | y: uint256 = 5 13 | x: uint256 = 5 14 | def bar(): 15 | pass 16 | """ 17 | f = CompilerData(code) 18 | copy.deepcopy(f.annotated_vyper_module) 19 | pickle.loads(pickle.dumps(f.annotated_vyper_module)) 20 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | # TODO: update to `latest` once supported 5 | # https://github.com/readthedocs/readthedocs.org/issues/8861 6 | os: ubuntu-22.04 7 | tools: 8 | python: "3.11" 9 | 10 | sphinx: 11 | configuration: docs/conf.py 12 | 13 | # We can't use "all" because "htmlzip" format is broken for now 14 | formats: 15 | - epub 16 | - pdf 17 | 18 | python: 19 | install: 20 | - requirements: requirements-docs.txt 21 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_unary.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import TypeMismatch 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo() -> int128: 11 | return -2**127 12 | """, 13 | TypeMismatch, 14 | ) 15 | ] 16 | 17 | 18 | @pytest.mark.parametrize("code,exc", fail_list) 19 | def test_unary_fail(code, exc): 20 | with pytest.raises(exc): 21 | compile_code(code) 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/unit/compiler/venom/test_liveness_simple_loop.py: -------------------------------------------------------------------------------- 1 | import vyper 2 | from vyper.compiler.settings import Settings 3 | 4 | source = """ 5 | @external 6 | def foo(a: uint256): 7 | _numBids: uint256 = 20 8 | b: uint256 = 10 9 | 10 | for i: uint256 in range(128): 11 | b = 1 + _numBids 12 | """ 13 | 14 | 15 | def test_liveness_simple_loop(): 16 | vyper.compile_code( 17 | source, output_formats=["opcodes"], settings=Settings(experimental_codegen=True) 18 | ) 19 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/test_memory_alloc.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.compiler import compile_code 4 | from vyper.exceptions import MemoryAllocationException 5 | 6 | 7 | def test_memory_overflow(): 8 | code = """ 9 | @external 10 | def zzz(x: DynArray[uint256, 2**59]): # 2**64 / 32 bytes per word == 2**59 11 | y: uint256[7] = [0,0,0,0,0,0,0] 12 | 13 | y[6] = y[5] 14 | """ 15 | with pytest.raises(MemoryAllocationException): 16 | compile_code(code) 17 | -------------------------------------------------------------------------------- /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 as_tuple 9 | from .utils import ast_to_dict 10 | from .parse import parse_to_ast, parse_to_ast_with_settings 11 | 12 | # adds vyper.ast.nodes classes into the local namespace 13 | for name, obj in ( 14 | (k, v) for k, v in nodes.__dict__.items() if type(v) is type and nodes.VyperNode in v.__mro__ 15 | ): 16 | setattr(sys.modules[__name__], name, obj) 17 | -------------------------------------------------------------------------------- /vyper/typing.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Optional, Sequence, Tuple, Union 2 | 3 | # Parser 4 | ParserPosition = Tuple[int, int] 5 | 6 | # Compiler 7 | ContractPath = str 8 | SourceCode = str 9 | OutputFormats = Sequence[str] 10 | StorageLayout = Dict 11 | 12 | # Opcodes 13 | OpcodeGasCost = Union[int, Tuple] 14 | OpcodeValue = Tuple[Optional[int], int, int, OpcodeGasCost] 15 | OpcodeMap = Dict[str, OpcodeValue] 16 | OpcodeRulesetValue = Tuple[Optional[int], int, int, int] 17 | OpcodeRulesetMap = Dict[str, OpcodeRulesetValue] 18 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_epsilon.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import InvalidType 5 | 6 | # CMC 2023-12-31 this could probably go in builtins/folding/ 7 | fail_list = [ 8 | ( 9 | """ 10 | FOO: constant(address) = epsilon(address) 11 | """, 12 | InvalidType, 13 | ) 14 | ] 15 | 16 | 17 | @pytest.mark.parametrize("bad_code,exc", fail_list) 18 | def test_block_fail(bad_code, exc): 19 | with pytest.raises(exc): 20 | compile_code(bad_code) 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### What I did 2 | 3 | ### How I did it 4 | 5 | ### How to verify it 6 | 7 | ### Commit message 8 | 9 | Commit message for the final, squashed PR. (Optional, but reviewers will appreciate it! Please see [our commit message style guide](../../master/docs/style-guide.rst#best-practices-1) for what we would ideally like to see in a commit message.) 10 | 11 | ### Description for the changelog 12 | 13 | ### Cute Animal Picture 14 | 15 | ![Put a link to a cute animal picture inside the parenthesis-->]() 16 | -------------------------------------------------------------------------------- /examples/storage/advanced_storage.vy: -------------------------------------------------------------------------------- 1 | #pragma version >0.3.10 2 | 3 | event DataChange: 4 | setter: indexed(address) 5 | value: int128 6 | 7 | storedData: public(int128) 8 | 9 | @deploy 10 | def __init__(_x: int128): 11 | self.storedData = _x 12 | 13 | @external 14 | def set(_x: int128): 15 | assert _x >= 0, "No negative values" 16 | assert self.storedData < 100, "Storage is locked when 100 or more is stored" 17 | self.storedData = _x 18 | log DataChange(msg.sender, _x) 19 | 20 | @external 21 | def reset(): 22 | self.storedData = 0 23 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/IERC20Detailed.vyi: -------------------------------------------------------------------------------- 1 | #NOTE: interface uses `String[1]` where 1 is the lower bound of the string returned by the function. 2 | # For end-users this means they can't use `implements: ERC20Detailed` unless their implementation 3 | # uses a value n >= 1. Regardless this is fine as one can't do String[0] where n == 0. 4 | 5 | @view 6 | @external 7 | def name() -> String[1]: 8 | ... 9 | 10 | @view 11 | @external 12 | def symbol() -> String[1]: 13 | ... 14 | 15 | @view 16 | @external 17 | def decimals() -> uint8: 18 | ... 19 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/test_address_balance.py: -------------------------------------------------------------------------------- 1 | def test_constant_address_balance(env, get_contract): 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(code) 13 | 14 | assert c.foo() == 0 15 | 16 | env.set_balance(env.deployer, 1337) 17 | env.message_call(address, value=1337) 18 | 19 | assert c.foo() == 1337 20 | -------------------------------------------------------------------------------- /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 | | \.venv 17 | | _build 18 | | buck-out 19 | | build 20 | | dist 21 | | env 22 | | venv 23 | )/ 24 | ''' 25 | -------------------------------------------------------------------------------- /tests/unit/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/functional/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 | -------------------------------------------------------------------------------- /quicktest.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # examples: 4 | # ./quicktest.sh 5 | # ./quicktest.sh -m "not fuzzing" 6 | # ./quicktest.sh -m "not fuzzing" -n (this is the most useful) 7 | # ./quicktest.sh -m "not fuzzing" -n0 8 | # ./quicktest.sh tests/.../mytest.py 9 | 10 | # run pytest but bail out on first error 11 | # useful for dev workflow. 12 | 13 | pytest -q -s --instafail -x --disable-warnings "$@" 14 | 15 | # useful options include: 16 | # -n0 (uses only one core but faster startup) 17 | # -nauto (uses only one core but faster startup) 18 | # -m "not fuzzing" - skip slow/fuzzing tests 19 | -------------------------------------------------------------------------------- /vyper/venom/passes/base_pass.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.analysis import IRAnalysesCache 2 | from vyper.venom.function import IRFunction 3 | 4 | 5 | class IRPass: 6 | """ 7 | Base class for all Venom IR passes. 8 | """ 9 | 10 | function: IRFunction 11 | analyses_cache: IRAnalysesCache 12 | 13 | def __init__(self, analyses_cache: IRAnalysesCache, function: IRFunction): 14 | self.function = function 15 | self.analyses_cache = analyses_cache 16 | 17 | def run_pass(self, *args, **kwargs): 18 | raise NotImplementedError(f"Not implemented! {self.__class__}.run_pass()") 19 | -------------------------------------------------------------------------------- /docs/testing-contracts-titanoboa.rst: -------------------------------------------------------------------------------- 1 | .. _testing-contracts-titanoboa: 2 | 3 | Testing with Titanoboa 4 | ###################### 5 | 6 | Titanoboa is a Vyper interpreter which is fast and provides a "swiss-army knife" toolkit for developing vyper applications. The best place to start is at `the official docs `_, and skip down to the `testing reference `_ for an overview of testing strategies. Finally, a more detailed API reference is available in the `API reference subsection `_. 7 | -------------------------------------------------------------------------------- /tests/functional/builtins/folding/test_epsilon.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.utils import decimal_to_int, parse_and_fold 4 | 5 | 6 | @pytest.mark.parametrize("typ_name", ["decimal"]) 7 | def test_epsilon(get_contract, typ_name): 8 | source = f""" 9 | @external 10 | def foo() -> {typ_name}: 11 | return epsilon({typ_name}) 12 | """ 13 | contract = get_contract(source) 14 | 15 | vyper_ast = parse_and_fold(f"epsilon({typ_name})") 16 | old_node = vyper_ast.body[0].value 17 | new_node = old_node.get_folded_value() 18 | 19 | assert contract.foo() == decimal_to_int(new_node.value) 20 | -------------------------------------------------------------------------------- /tests/unit/ast/nodes/test_binary.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.ast_utils import deepequals 4 | from vyper import ast as vy_ast 5 | from vyper.exceptions import SyntaxException 6 | 7 | 8 | def test_binary_becomes_bytes(): 9 | expected = vy_ast.parse_to_ast( 10 | """ 11 | def x(): 12 | foo: Bytes[1] = b'\x01' 13 | """ 14 | ) 15 | mutated = vy_ast.parse_to_ast( 16 | """ 17 | def x(): 18 | foo: Bytes[1] = 0b00000001 19 | """ 20 | ) 21 | 22 | assert deepequals(expected, mutated) 23 | 24 | 25 | def test_binary_length(): 26 | with pytest.raises(SyntaxException): 27 | vy_ast.parse_to_ast("foo: Bytes[1] = 0b01") 28 | -------------------------------------------------------------------------------- /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) -------------------------------------------------------------------------------- /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 6 | 7 | if __name__ == "__main__": 8 | allowed_subcommands = ("--vyper-compile", "--vyper-ir") 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-ir": 17 | vyper_ir._parse_cli_args() 18 | else: 19 | vyper_compile._parse_cli_args() 20 | -------------------------------------------------------------------------------- /tests/functional/codegen/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/functional/codegen/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(a=empty(address), b=3) 13 | amount: uint256 = 1 14 | flargen: uint256 = 42 15 | extcall 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/unit/cli/storage_layout/utils.py: -------------------------------------------------------------------------------- 1 | from vyper.evm.opcodes import version_check 2 | 3 | 4 | def adjust_storage_layout_for_cancun(layout): 5 | def _go(layout): 6 | for _varname, item in layout.items(): 7 | if "slot" in item and isinstance(item["slot"], int): 8 | item["slot"] -= 1 9 | else: 10 | # recurse to submodule 11 | _go(item) 12 | 13 | if version_check(begin="cancun"): 14 | nonreentrant = layout["storage_layout"].pop("$.nonreentrant_key", None) 15 | if nonreentrant is not None: 16 | layout["transient_storage_layout"] = {"$.nonreentrant_key": nonreentrant} 17 | _go(layout["storage_layout"]) 18 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_self_balance.py: -------------------------------------------------------------------------------- 1 | from vyper import compiler 2 | 3 | 4 | def test_self_balance(env, get_contract): 5 | code = """ 6 | @external 7 | @view 8 | def get_balance() -> uint256: 9 | a: uint256 = self.balance 10 | return a 11 | 12 | @external 13 | @payable 14 | def __default__(): 15 | pass 16 | """ 17 | opcodes = compiler.compile_code(code, output_formats=["opcodes"])["opcodes"] 18 | assert "SELFBALANCE" in opcodes 19 | 20 | c = get_contract(code) 21 | env.set_balance(env.deployer, 1337) 22 | env.message_call(c.address, value=1337) 23 | 24 | assert c.get_balance() == 1337 25 | assert env.get_balance(c.address) == 1337 26 | assert env.get_balance(env.deployer) == 0 27 | -------------------------------------------------------------------------------- /tests/unit/compiler/venom/test_stack_cleanup.py: -------------------------------------------------------------------------------- 1 | from vyper.compiler.settings import OptimizationLevel 2 | from vyper.venom import generate_assembly_experimental 3 | from vyper.venom.context import IRContext 4 | 5 | 6 | def test_cleanup_stack(): 7 | ctx = IRContext() 8 | fn = ctx.create_function("test") 9 | bb = fn.get_basic_block() 10 | ret_val = bb.append_instruction("param") 11 | op = bb.append_instruction("store", 10) 12 | op2 = bb.append_instruction("store", op) 13 | bb.append_instruction("add", op, op2) 14 | bb.append_instruction("ret", ret_val) 15 | 16 | asm = generate_assembly_experimental(ctx, optimize=OptimizationLevel.GAS) 17 | assert asm == ["PUSH1", 10, "DUP1", "ADD", "POP", "JUMP"] 18 | -------------------------------------------------------------------------------- /tests/functional/syntax/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() 17 | """, 18 | """ 19 | foo.a: int128 20 | """, 21 | """ 22 | @external 23 | def foo(): 24 | bar.x: int128 = 0 25 | """, 26 | ] 27 | 28 | 29 | @pytest.mark.parametrize("bad_code", fail_list) 30 | def test_variable_declaration_exception(bad_code): 31 | with pytest.raises(VariableDeclarationException): 32 | compiler.compile_code(bad_code) 33 | -------------------------------------------------------------------------------- /.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 53 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_selfdestruct.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import TypeMismatch 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(TypeMismatch): 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 | -------------------------------------------------------------------------------- /vyper/venom/passes/__init__.py: -------------------------------------------------------------------------------- 1 | from .algebraic_optimization import AlgebraicOptimizationPass 2 | from .branch_optimization import BranchOptimizationPass 3 | from .dft import DFTPass 4 | from .float_allocas import FloatAllocas 5 | from .literals_codesize import ReduceLiteralsCodesize 6 | from .load_elimination import LoadElimination 7 | from .lower_dload import LowerDloadPass 8 | from .make_ssa import MakeSSA 9 | from .mem2var import Mem2Var 10 | from .memmerging import MemMergePass 11 | from .normalization import NormalizationPass 12 | from .remove_unused_variables import RemoveUnusedVariablesPass 13 | from .sccp import SCCP 14 | from .simplify_cfg import SimplifyCFGPass 15 | from .store_elimination import StoreElimination 16 | from .store_expansion import StoreExpansionPass 17 | -------------------------------------------------------------------------------- /tests/functional/builtins/codegen/test_is_contract.py: -------------------------------------------------------------------------------- 1 | def test_is_contract(env, get_contract): 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 = env.accounts[:2] 15 | c1 = get_contract(contract_1) 16 | c2 = get_contract(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/unit/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 | 25 | _parse_args([str(bar_path)]) # absolute path 26 | os.chdir(chdir_path.parent) 27 | 28 | _parse_args([str(bar_path)]) # absolute path, subfolder of cwd 29 | _parse_args([str(bar_path.relative_to(chdir_path.parent))]) # relative path 30 | -------------------------------------------------------------------------------- /tests/functional/builtins/folding/test_powmod.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import given, settings 3 | from hypothesis import strategies as st 4 | 5 | from tests.utils import parse_and_fold 6 | 7 | st_uint256 = st.integers(min_value=0, max_value=(2**256 - 1)) 8 | 9 | 10 | @pytest.mark.fuzzing 11 | @settings(max_examples=100) 12 | @given(a=st_uint256, b=st_uint256) 13 | def test_powmod_uint256(get_contract, a, b): 14 | source = """ 15 | @external 16 | def foo(a: uint256, b: uint256) -> uint256: 17 | return pow_mod256(a, b) 18 | """ 19 | contract = get_contract(source) 20 | 21 | vyper_ast = parse_and_fold(f"pow_mod256({a}, {b})") 22 | old_node = vyper_ast.body[0].value 23 | new_node = old_node.get_folded_value() 24 | 25 | assert contract.foo(a, b) == new_node.value 26 | -------------------------------------------------------------------------------- /tests/unit/ast/test_parser.py: -------------------------------------------------------------------------------- 1 | from tests.ast_utils import deepequals 2 | from vyper.ast.parse import parse_to_ast 3 | 4 | 5 | def test_ast_equal(): 6 | code = """ 7 | @external 8 | def test() -> int128: 9 | a: uint256 = 100 10 | return 123 11 | """ 12 | 13 | ast1 = parse_to_ast(code) 14 | ast2 = parse_to_ast("\n \n" + code + "\n\n") 15 | 16 | assert deepequals(ast1, ast2) 17 | 18 | 19 | def test_ast_unequal(): 20 | code1 = """ 21 | @external 22 | def test() -> int128: 23 | a: uint256 = 100 24 | return 123 25 | """ 26 | code2 = """ 27 | @external 28 | def test() -> int128: 29 | a: uint256 = 100 30 | return 121 31 | """ 32 | 33 | ast1 = parse_to_ast(code1) 34 | ast2 = parse_to_ast(code2) 35 | 36 | assert not deepequals(ast1, ast2) 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 | * `Titanoboa `_: A Vyper interpreter, pretty tracebacks, forking, debugging and deployment features. Maintained by the Vyper team. 9 | * `Brownie `_: A development and testing framework for smart contracts targeting the Ethereum Virtual Machine 10 | 11 | Example usage for each package is provided in the sections listed below. 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | testing-contracts-titanoboa.rst 17 | testing-contracts-brownie.rst 18 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_minmax_value.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import InvalidType 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo(): 11 | a: address = min_value(address) 12 | """, 13 | InvalidType, 14 | ), 15 | ( 16 | """ 17 | @external 18 | def foo(): 19 | a: address = max_value(address) 20 | """, 21 | InvalidType, 22 | ), 23 | ( 24 | """ 25 | FOO: constant(address) = min_value(address) 26 | 27 | @external 28 | def foo(): 29 | a: address = FOO 30 | """, 31 | InvalidType, 32 | ), 33 | ] 34 | 35 | 36 | @pytest.mark.parametrize("bad_code,exc", fail_list) 37 | def test_block_fail(bad_code, exc): 38 | with pytest.raises(exc): 39 | compile_code(bad_code) 40 | -------------------------------------------------------------------------------- /tests/unit/abi_types/test_invalid_abi_types.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.abi_types import ABI_Bytes, ABI_BytesM, ABI_DynamicArray, ABI_GIntM, ABI_String 4 | from vyper.exceptions import InvalidABIType 5 | 6 | cases_invalid_types = [ 7 | (ABI_GIntM, ((0, False), (7, False), (300, True), (300, False))), 8 | (ABI_BytesM, ((0,), (33,), (-10,))), 9 | (ABI_Bytes, ((-1,), (-69,))), 10 | (ABI_DynamicArray, ((ABI_GIntM(256, False), -1), (ABI_String(256), -10))), 11 | ] 12 | 13 | 14 | @pytest.mark.parametrize("typ,params_variants", cases_invalid_types) 15 | def test_invalid_abi_types(assert_compile_failed, typ, params_variants): 16 | # double parametrization cannot work because the 2nd dimension is variable 17 | for params in params_variants: 18 | assert_compile_failed(lambda p=params: typ(*p), InvalidABIType) 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Any general feedback or bug reports about the Vyper Compiler. No new features proposals. 4 | labels: ["needs triage"] 5 | --- 6 | 7 | ### Version Information 8 | 9 | * vyper Version (output of `vyper --version`): x.x.x 10 | * OS: osx/linux/win 11 | * Python Version (output of `python --version`): 12 | 13 | ### What's your issue about? 14 | 15 | Please include information like: 16 | 17 | * full output of the error you received 18 | * what command you ran 19 | * the code that caused the failure (see [this link](https://help.github.com/articles/basic-writing-and-formatting-syntax/) for help with formatting code) 20 | * please try running your example with the `--verbose` flag turned on 21 | 22 | 23 | ### How can it be fixed? 24 | 25 | Fill this in if you know how to fix it. 26 | -------------------------------------------------------------------------------- /tests/functional/builtins/codegen/test_length.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | def test_test_length(get_contract): 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(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, 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(code) 30 | assert c.boo() == 0 31 | -------------------------------------------------------------------------------- /tests/functional/syntax/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 11 | def nonreentrant_foo() -> uint256: 12 | return 1 13 | """, 14 | """ 15 | @external 16 | @nonreentrant 17 | @nonreentrant 18 | def nonreentrant_foo() -> uint256: 19 | return 1 20 | """, 21 | """ 22 | @external 23 | @nonreentrant("foo") 24 | def nonreentrant_foo() -> uint256: 25 | return 1 26 | """, 27 | ] 28 | 29 | 30 | @pytest.mark.parametrize("failing_contract_code", FAILING_CONTRACTS) 31 | def test_invalid_function_decorators(failing_contract_code): 32 | with pytest.raises(StructureException): 33 | compiler.compile_code(failing_contract_code) 34 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | analyze: 11 | name: Analyze 12 | runs-on: ubuntu-latest 13 | permissions: 14 | actions: read 15 | contents: read 16 | security-events: write 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v4 21 | 22 | - name: Initialize CodeQL 23 | uses: github/codeql-action/init@v3 24 | with: 25 | languages: python 26 | queries: +security-and-quality 27 | 28 | - name: Autobuild 29 | uses: github/codeql-action/autobuild@v3 30 | 31 | - name: Perform CodeQL Analysis 32 | uses: github/codeql-action/analyze@v3 33 | with: 34 | category: "/language:python" 35 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/IERC20.vyi: -------------------------------------------------------------------------------- 1 | # Events 2 | event Transfer: 3 | sender: indexed(address) 4 | receiver: indexed(address) 5 | value: uint256 6 | 7 | event Approval: 8 | owner: indexed(address) 9 | spender: indexed(address) 10 | value: uint256 11 | 12 | # Functions 13 | @view 14 | @external 15 | def totalSupply() -> uint256: 16 | ... 17 | 18 | @view 19 | @external 20 | def balanceOf(_owner: address) -> uint256: 21 | ... 22 | 23 | @view 24 | @external 25 | def allowance(_owner: address, _spender: address) -> uint256: 26 | ... 27 | 28 | @external 29 | def transfer(_to: address, _value: uint256) -> bool: 30 | ... 31 | 32 | @external 33 | def transferFrom(_from: address, _to: address, _value: uint256) -> bool: 34 | ... 35 | 36 | @external 37 | def approve(_spender: address, _value: uint256) -> bool: 38 | ... 39 | -------------------------------------------------------------------------------- /tests/functional/examples/storage/test_storage.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | INITIAL_VALUE = 4 4 | 5 | 6 | @pytest.fixture(scope="module") 7 | def storage_contract(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(storage_contract): 21 | storage_contract.set(10) 22 | assert storage_contract.storedData() == 10 # Directly access storedData 23 | 24 | storage_contract.set(-5) 25 | assert storage_contract.storedData() == -5 26 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_powmod.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import TypeMismatch 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo(): 11 | a: uint256 = pow_mod256(-1, -1) 12 | """, 13 | TypeMismatch, 14 | ) 15 | ] 16 | 17 | 18 | @pytest.mark.parametrize("bad_code,exc", fail_list) 19 | def test_powmod_fail(bad_code, exc): 20 | with pytest.raises(exc): 21 | compile_code(bad_code) 22 | 23 | 24 | valid_list = [ 25 | """ 26 | FOO: constant(uint256) = 3 27 | BAR: constant(uint256) = 5 28 | BAZ: constant(uint256) = pow_mod256(FOO, BAR) 29 | 30 | @external 31 | def foo(): 32 | a: uint256 = BAZ 33 | """ 34 | ] 35 | 36 | 37 | @pytest.mark.parametrize("code", valid_list) 38 | def test_powmod_pass(code): 39 | assert compile_code(code) is not None 40 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pycqa/isort 3 | rev: 5.13.2 4 | hooks: 5 | - id: isort 6 | name: isort 7 | 8 | - repo: https://github.com/psf/black 9 | rev: 23.12.0 10 | hooks: 11 | - id: black 12 | name: black 13 | 14 | - repo: https://github.com/PyCQA/flake8 15 | rev: 6.1.0 16 | hooks: 17 | - id: flake8 18 | 19 | - repo: https://github.com/pre-commit/mirrors-mypy 20 | rev: v1.7.1 21 | hooks: 22 | - id: mypy 23 | additional_dependencies: 24 | - "types-setuptools" 25 | args: # settings from Makefile 26 | - --install-types 27 | - --non-interactive 28 | - --follow-imports=silent 29 | - --ignore-missing-imports 30 | - --implicit-optional 31 | 32 | default_language_version: 33 | python: python3.10 34 | -------------------------------------------------------------------------------- /tests/functional/syntax/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 | @deploy 32 | def __init__(): 33 | pass 34 | 35 | @internal 36 | def foo(): 37 | self.__init__() 38 | """, 39 | ] 40 | 41 | 42 | @pytest.mark.parametrize("bad_code", call_violation_list) 43 | def test_call_violation_exception(bad_code): 44 | with raises(CallViolation): 45 | compiler.compile_code(bad_code) 46 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_abs.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import TypeMismatch 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo(): 11 | y: int256 = abs( 12 | -57896044618658097711785492504343953926634992332820282019728792003956564819968 13 | ) 14 | """, 15 | TypeMismatch, 16 | ) 17 | ] 18 | 19 | 20 | @pytest.mark.parametrize("bad_code,exc", fail_list) 21 | def test_abs_fail(bad_code, exc): 22 | with pytest.raises(exc): 23 | compile_code(bad_code) 24 | 25 | 26 | valid_list = [ 27 | """ 28 | FOO: constant(int256) = -3 29 | BAR: constant(int256) = abs(FOO) 30 | 31 | @external 32 | def foo(): 33 | a: int256 = BAR 34 | """ 35 | ] 36 | 37 | 38 | @pytest.mark.parametrize("code", valid_list) 39 | def test_abs_pass(code): 40 | assert compile_code(code) is not None 41 | -------------------------------------------------------------------------------- /tests/unit/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/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/functional/codegen/storage_variables/test_storage_variable.py: -------------------------------------------------------------------------------- 1 | from pytest import raises 2 | 3 | from vyper.exceptions import UndeclaredDefinition 4 | 5 | 6 | def test_permanent_variables_test(get_contract): 7 | permanent_variables_test = """ 8 | struct Var: 9 | a: int128 10 | b: int128 11 | var: Var 12 | 13 | @deploy 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(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/ast_utils.py: -------------------------------------------------------------------------------- 1 | from vyper.ast.nodes import VyperNode 2 | 3 | 4 | def deepequals(node: VyperNode, other: VyperNode): 5 | # checks two nodes are recursively equal, ignoring metadata 6 | # like line info. 7 | if not isinstance(other, type(node)): 8 | return False 9 | 10 | if isinstance(node, list): 11 | if len(node) != len(other): 12 | return False 13 | return all(deepequals(a, b) for a, b in zip(node, other)) 14 | 15 | if not isinstance(node, VyperNode): 16 | return node == other 17 | 18 | if getattr(node, "node_id", None) != getattr(other, "node_id", None): 19 | return False 20 | for field_name in (i for i in node.get_fields() if i not in VyperNode.__slots__): 21 | lhs = getattr(node, field_name, None) 22 | rhs = getattr(other, field_name, None) 23 | if not deepequals(lhs, rhs): 24 | return False 25 | return True 26 | -------------------------------------------------------------------------------- /tests/functional/codegen/types/numbers/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/functional/syntax/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_declaration_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/functional/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 tests.utils import parse_and_fold 6 | 7 | st_uint256 = st.integers(min_value=0, max_value=2**256 - 1) 8 | 9 | 10 | @pytest.mark.fuzzing 11 | @settings(max_examples=50) 12 | @given(a=st_uint256, b=st_uint256, c=st_uint256) 13 | @pytest.mark.parametrize("fn_name", ["uint256_addmod", "uint256_mulmod"]) 14 | def test_modmath(get_contract, a, b, c, fn_name): 15 | assume(c > 0) 16 | 17 | source = f""" 18 | @external 19 | def foo(a: uint256, b: uint256, c: uint256) -> uint256: 20 | return {fn_name}(a, b, c) 21 | """ 22 | contract = get_contract(source) 23 | 24 | vyper_ast = parse_and_fold(f"{fn_name}({a}, {b}, {c})") 25 | old_node = vyper_ast.body[0].value 26 | new_node = old_node.get_folded_value() 27 | 28 | assert contract.foo(a, b, c) == new_node.value 29 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /vyper/__init__.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path as _Path 2 | 3 | from vyper.compiler import compile_code, compile_from_file_input 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 | 25 | # pep440 version with commit hash 26 | __long_version__ = f"{__version__}+commit.{__commit__}" 27 | -------------------------------------------------------------------------------- /tests/unit/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/decorators/test_view.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import FunctionDeclarationException 4 | 5 | 6 | def test_constant_test(get_contract): 7 | constant_test = """ 8 | @external 9 | @view 10 | def foo() -> int128: 11 | return 5 12 | """ 13 | 14 | c = get_contract(constant_test) 15 | assert c.foo() == 5 16 | 17 | print("Passed constant function test") 18 | 19 | 20 | @pytest.mark.requires_evm_version("cancun") 21 | def test_transient_test(get_contract): 22 | code = """ 23 | x: transient(uint256) 24 | 25 | @external 26 | @view 27 | def foo() -> uint256: 28 | return self.x 29 | """ 30 | c = get_contract(code) 31 | assert c.foo() == 0 32 | 33 | 34 | def test_invalid_constant_and_payable(get_contract, assert_compile_failed): 35 | code = """ 36 | @external 37 | @payable 38 | @view 39 | def foo() -> num: 40 | return 5 41 | """ 42 | assert_compile_failed(lambda: get_contract(code), FunctionDeclarationException) 43 | -------------------------------------------------------------------------------- /tests/functional/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/functional/codegen/modules/test_flag_imports.py: -------------------------------------------------------------------------------- 1 | def test_import_flag_types(make_input_bundle, get_contract): 2 | lib1 = """ 3 | import lib2 4 | 5 | flag Roles: 6 | ADMIN 7 | USER 8 | 9 | enum Roles2: 10 | ADMIN 11 | USER 12 | 13 | role: Roles 14 | role2: Roles2 15 | role3: lib2.Roles3 16 | """ 17 | lib2 = """ 18 | flag Roles3: 19 | ADMIN 20 | USER 21 | NOBODY 22 | """ 23 | contract = """ 24 | import lib1 25 | 26 | initializes: lib1 27 | 28 | @external 29 | def bar(r: lib1.Roles, r2: lib1.Roles2, r3: lib1.lib2.Roles3) -> bool: 30 | lib1.role = r 31 | lib1.role2 = r2 32 | lib1.role3 = r3 33 | assert lib1.role == lib1.Roles.ADMIN 34 | assert lib1.role2 == lib1.Roles2.USER 35 | assert lib1.role3 == lib1.lib2.Roles3.NOBODY 36 | return True 37 | """ 38 | 39 | input_bundle = make_input_bundle({"lib1.vy": lib1, "lib2.vy": lib2}) 40 | c = get_contract(contract, input_bundle=input_bundle) 41 | assert c.bar(1, 2, 4) is True 42 | -------------------------------------------------------------------------------- /tests/unit/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_new_node_has_no_parent(): 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 new_node._parent is None 32 | assert new_node._depth == 0 33 | -------------------------------------------------------------------------------- /tests/functional/codegen/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): 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 _: uint256 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 | return get_contract(code) 24 | 25 | 26 | @pytest.mark.fuzzing 27 | @hypothesis.given(value=hypothesis.strategies.integers(min_value=0, max_value=(2**64 - 1))) 28 | def test_zero_pad_range(little_endian_contract, value): 29 | actual_bytes = value.to_bytes(8, byteorder="little") 30 | contract_bytes = little_endian_contract.get_count(value) 31 | assert contract_bytes == actual_bytes 32 | -------------------------------------------------------------------------------- /tests/unit/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(optimize): 19 | assert core._opt_gas() == (optimize == OptimizationLevel.GAS) 20 | assert core._opt_none() == (optimize == OptimizationLevel.NONE) 21 | assert core._opt_codesize() == (optimize == OptimizationLevel.CODESIZE) 22 | 23 | 24 | def test_debug_mode(pytestconfig): 25 | debug_mode = pytestconfig.getoption("enable_compiler_debug_mode") 26 | assert _is_debug_mode() == debug_mode 27 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/test_conditionals.py: -------------------------------------------------------------------------------- 1 | def test_conditional_return_code(get_contract): 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 | """ 11 | 12 | c = get_contract(conditional_return_code) 13 | assert c.foo(True) == 5 14 | assert c.foo(False) == 7 15 | 16 | print("Passed conditional return tests") 17 | 18 | 19 | def test_single_branch_underflow_public(get_contract): 20 | code = """ 21 | @external 22 | def doit(): 23 | if False: 24 | raw_call(msg.sender, b"", max_outsize=0, value=0, gas=msg.gas) 25 | """ 26 | c = get_contract(code) 27 | c.doit() 28 | 29 | 30 | def test_single_branch_underflow_private(get_contract): 31 | code = """ 32 | @internal 33 | def priv() -> uint256: 34 | return 1 35 | 36 | @external 37 | def dont_doit(): 38 | if False: 39 | self.priv() 40 | """ 41 | c = get_contract(code) 42 | c.dont_doit() 43 | -------------------------------------------------------------------------------- /tests/unit/compiler/venom/test_simplify_cfg.py: -------------------------------------------------------------------------------- 1 | from tests.venom_utils import assert_ctx_eq, parse_venom 2 | from vyper.venom.analysis import IRAnalysesCache 3 | from vyper.venom.passes import SCCP, SimplifyCFGPass 4 | 5 | 6 | def test_phi_reduction_after_block_pruning(): 7 | pre = """ 8 | function _global { 9 | _global: 10 | jnz 1, @then, @else 11 | then: 12 | %1 = 1 13 | jmp @join 14 | else: 15 | %2 = 2 16 | jmp @join 17 | join: 18 | %3 = phi @then, %1, @else, %2 19 | stop 20 | } 21 | """ 22 | post = """ 23 | function _global { 24 | _global: 25 | %1 = 1 26 | %3 = %1 27 | stop 28 | } 29 | """ 30 | ctx1 = parse_venom(pre) 31 | for fn in ctx1.functions.values(): 32 | ac = IRAnalysesCache(fn) 33 | SCCP(ac, fn).run_pass() 34 | SimplifyCFGPass(ac, fn).run_pass() 35 | 36 | ctx2 = parse_venom(post) 37 | assert_ctx_eq(ctx1, ctx2) 38 | -------------------------------------------------------------------------------- /vyper/semantics/analysis/common.py: -------------------------------------------------------------------------------- 1 | from typing import Tuple 2 | 3 | from vyper.exceptions import StructureException, tag_exceptions 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 | 14 | # iterate over the MRO until we find a matching visitor function 15 | # this lets us use a single function to broadly target several 16 | # node types with a shared parent 17 | for class_ in node.__class__.mro(): 18 | ast_type = class_.__name__ 19 | 20 | with tag_exceptions(node): 21 | visitor_fn = getattr(self, f"visit_{ast_type}", None) 22 | if visitor_fn: 23 | return visitor_fn(node, *args) 24 | 25 | node_type = type(node).__name__ 26 | raise StructureException( 27 | f"Unsupported syntax for {self.scope_name} namespace: {node_type}", node 28 | ) 29 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_bool_ops.py: -------------------------------------------------------------------------------- 1 | def test_convert_from_bool(get_contract): 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(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 | -------------------------------------------------------------------------------- /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 | using-modules.rst 29 | interfaces.rst 30 | event-logging.rst 31 | natspec.rst 32 | 33 | .. toctree:: 34 | :caption: Using the Compiler 35 | :maxdepth: 2 36 | 37 | compiling-a-contract.rst 38 | compiler-exceptions.rst 39 | deploying-contracts.rst 40 | testing-contracts.rst 41 | 42 | .. toctree:: 43 | :caption: Additional Resources 44 | :maxdepth: 2 45 | 46 | resources 47 | release-notes.rst 48 | contributing.rst 49 | style-guide.rst 50 | versioning.rst 51 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_abi_decode.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import TypeMismatch 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo(j: uint256) -> bool: 11 | s: bool = _abi_decode(j, bool, unwrap_tuple= False) 12 | return s 13 | """, 14 | TypeMismatch, 15 | ), 16 | ( 17 | """ 18 | @external 19 | def bar(j: String[32]) -> bool: 20 | s: bool = _abi_decode(j, bool, unwrap_tuple= False) 21 | return s 22 | """, 23 | TypeMismatch, 24 | ), 25 | ] 26 | 27 | 28 | @pytest.mark.parametrize("bad_code,exc", fail_list) 29 | def test_abi_decode_fail(bad_code, exc): 30 | with pytest.raises(exc): 31 | compiler.compile_code(bad_code) 32 | 33 | 34 | valid_list = [ 35 | """ 36 | @external 37 | def foo(x: Bytes[32]) -> uint256: 38 | return _abi_decode(x, uint256) 39 | """ 40 | ] 41 | 42 | 43 | @pytest.mark.parametrize("good_code", valid_list) 44 | def test_abi_decode_success(good_code): 45 | assert compiler.compile_code(good_code) is not None 46 | -------------------------------------------------------------------------------- /tests/functional/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/functional/syntax/test_method_id.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import InvalidLiteral, InvalidType 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo(): 11 | a: Bytes[4] = method_id("bar ()") 12 | """, 13 | InvalidLiteral, 14 | ), 15 | ( 16 | """ 17 | FOO: constant(Bytes[4]) = method_id(1) 18 | """, 19 | InvalidType, 20 | ), 21 | ( 22 | """ 23 | FOO: constant(Bytes[4]) = method_id("bar ()") 24 | """, 25 | InvalidLiteral, 26 | ), 27 | ] 28 | 29 | 30 | @pytest.mark.parametrize("bad_code,exc", fail_list) 31 | def test_method_id_fail(bad_code, exc): 32 | with pytest.raises(exc): 33 | compile_code(bad_code) 34 | 35 | 36 | valid_list = [ 37 | """ 38 | FOO: constant(String[5]) = "foo()" 39 | BAR: constant(Bytes[4]) = method_id(FOO) 40 | 41 | @external 42 | def foo(a: Bytes[4] = BAR): 43 | pass 44 | """ 45 | ] 46 | 47 | 48 | @pytest.mark.parametrize("code", valid_list) 49 | def test_method_id_pass(code): 50 | assert compile_code(code) is not None 51 | -------------------------------------------------------------------------------- /tests/unit/compiler/test_compile_code.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import pytest 4 | 5 | import vyper 6 | 7 | 8 | @pytest.fixture 9 | def huge_bytestring(): 10 | r = random.Random(b"vyper") 11 | 12 | return bytes([r.getrandbits(8) for _ in range(0x6001)]) 13 | 14 | 15 | def test_contract_size_exceeded(huge_bytestring): 16 | code = f""" 17 | @external 18 | def a() -> bool: 19 | q: Bytes[24577] = {huge_bytestring} 20 | return True 21 | """ 22 | with pytest.warns(vyper.warnings.ContractSizeLimitWarning): 23 | vyper.compile_code(code, output_formats=["bytecode_runtime"]) 24 | 25 | 26 | # test that each compilation run gets a fresh analysis and storage allocator 27 | def test_shared_modules_allocation(make_input_bundle): 28 | lib1 = """ 29 | x: uint256 30 | """ 31 | main1 = """ 32 | import lib1 33 | initializes: lib1 34 | """ 35 | main2 = """ 36 | import lib1 37 | initializes: lib1 38 | """ 39 | input_bundle = make_input_bundle({"lib1.vy": lib1}) 40 | 41 | vyper.compile_code(main1, input_bundle=input_bundle) 42 | vyper.compile_code(main2, input_bundle=input_bundle) 43 | -------------------------------------------------------------------------------- /tests/functional/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 | @deploy 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 flag 34 | """ 35 | flag 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/functional/codegen/types/test_string_literal.py: -------------------------------------------------------------------------------- 1 | def test_string_literal_return(get_contract): 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(code) 14 | 15 | assert c.test() == "hello world!" 16 | assert c.testb() == b"hello world!" 17 | 18 | 19 | def test_string_convert(get_contract): 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(code) 31 | 32 | assert c.testb() == "hello world!" 33 | assert c.testbb() == "hello world!" 34 | 35 | 36 | def test_str_assign(get_contract): 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(code) 45 | 46 | assert c.test() == "baba black sheep" 47 | -------------------------------------------------------------------------------- /vyper/ast/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Union 2 | 3 | from vyper.ast import nodes as vy_ast 4 | from vyper.exceptions import CompilerPanic 5 | 6 | 7 | def ast_to_dict(ast_struct: Union[vy_ast.VyperNode, List]) -> Union[Dict, List]: 8 | """ 9 | Converts a Vyper AST node, or list of nodes, into a dictionary suitable for 10 | output to the user. 11 | """ 12 | if isinstance(ast_struct, vy_ast.VyperNode): 13 | return ast_struct.to_dict() 14 | elif isinstance(ast_struct, list): 15 | return [i.to_dict() for i in ast_struct] 16 | else: 17 | raise CompilerPanic(f'Unknown Vyper AST node provided: "{type(ast_struct)}".') 18 | 19 | 20 | def dict_to_ast(ast_struct: Union[Dict, List]) -> Union[vy_ast.VyperNode, List]: 21 | """ 22 | Converts an AST dict, or list of dicts, into Vyper AST node objects. 23 | """ 24 | if isinstance(ast_struct, dict): 25 | return vy_ast.get_node(ast_struct) 26 | if isinstance(ast_struct, list): 27 | return [vy_ast.get_node(i) for i in ast_struct] 28 | raise CompilerPanic(f'Unknown ast_struct provided: "{type(ast_struct)}".') 29 | -------------------------------------------------------------------------------- /tests/unit/ast/nodes/test_fold_unaryop.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.utils import parse_and_fold 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 = parse_and_fold(f"not {bool_cond}") 16 | old_node = vyper_ast.body[0].value 17 | new_node = old_node.get_folded_value() 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 = parse_and_fold(literal_op) 34 | new_node = vyper_ast.body[0].value.get_folded_value() 35 | expected = new_node.value 36 | 37 | assert contract.foo(bool_cond) == expected 38 | -------------------------------------------------------------------------------- /tests/functional/syntax/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/unit/ast/nodes/test_compare_nodes.py: -------------------------------------------------------------------------------- 1 | from tests.ast_utils import deepequals 2 | from vyper import ast as vy_ast 3 | 4 | 5 | def test_compare_different_node_clases(): 6 | vyper_ast = vy_ast.parse_to_ast("foo = 42") 7 | left = vyper_ast.body[0].target 8 | right = vyper_ast.body[0].value 9 | 10 | assert not deepequals(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 not deepequals(left, right) 18 | 19 | 20 | def test_compare_different_nodes_same_value(): 21 | vyper_ast = vy_ast.parse_to_ast("[1, 1]") 22 | left, right = vyper_ast.body[0].value.elements 23 | 24 | assert not deepequals(left, right) 25 | 26 | 27 | def test_compare_similar_node(): 28 | # test equality without node_ids 29 | left = vy_ast.Int(value=1) 30 | right = vy_ast.Int(value=1) 31 | 32 | assert deepequals(left, right) 33 | 34 | 35 | def test_compare_same_node(): 36 | vyper_ast = vy_ast.parse_to_ast("42") 37 | node = vyper_ast.body[0].value 38 | 39 | assert deepequals(node, node) 40 | -------------------------------------------------------------------------------- /examples/factory/Exchange.vy: -------------------------------------------------------------------------------- 1 | #pragma version >0.3.10 2 | 3 | from ethereum.ercs import IERC20 4 | 5 | 6 | interface Factory: 7 | def register(): nonpayable 8 | 9 | 10 | token: public(IERC20) 11 | factory: Factory 12 | 13 | 14 | @deploy 15 | def __init__(_token: IERC20, _factory: Factory): 16 | self.token = _token 17 | self.factory = _factory 18 | 19 | 20 | @external 21 | def initialize(): 22 | # Anyone can safely call this function because of EXTCODEHASH 23 | extcall self.factory.register() 24 | 25 | 26 | # NOTE: This contract restricts trading to only be done by the factory. 27 | # A practical implementation would probably want counter-pairs 28 | # and liquidity management features for each exchange pool. 29 | 30 | 31 | @external 32 | def receive(_from: address, _amt: uint256): 33 | assert msg.sender == self.factory.address 34 | success: bool = extcall self.token.transferFrom(_from, self, _amt) 35 | assert success 36 | 37 | 38 | @external 39 | def transfer(_to: address, _amt: uint256): 40 | assert msg.sender == self.factory.address 41 | success: bool = extcall self.token.transfer(_to, _amt) 42 | assert success 43 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_codehash.py: -------------------------------------------------------------------------------- 1 | from vyper.compiler import compile_code 2 | from vyper.utils import keccak256 3 | 4 | 5 | def test_get_extcodehash(get_contract): 6 | code = """ 7 | a: address 8 | 9 | @deploy 10 | def __init__(): 11 | self.a = self 12 | 13 | @external 14 | def foo(x: address) -> bytes32: 15 | return x.codehash 16 | 17 | @external 18 | def foo2(x: address) -> bytes32: 19 | b: address = x 20 | return b.codehash 21 | 22 | @external 23 | def foo3() -> bytes32: 24 | return self.codehash 25 | 26 | @external 27 | def foo4() -> bytes32: 28 | return self.a.codehash 29 | """ 30 | compiled = compile_code(code, output_formats=["bytecode_runtime"]) 31 | bytecode = bytes.fromhex(compiled["bytecode_runtime"][2:]) 32 | hash_ = keccak256(bytecode) 33 | 34 | c = get_contract(code) 35 | 36 | assert c.foo(c.address) == hash_ 37 | assert not int(c.foo("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF").hex(), 16) 38 | 39 | assert c.foo2(c.address) == hash_ 40 | assert not int(c.foo2("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF").hex(), 16) 41 | 42 | assert c.foo3() == hash_ 43 | assert c.foo4() == hash_ 44 | -------------------------------------------------------------------------------- /tests/unit/compiler/venom/test_duplicate_operands.py: -------------------------------------------------------------------------------- 1 | from vyper.compiler.settings import OptimizationLevel 2 | from vyper.venom import generate_assembly_experimental 3 | from vyper.venom.analysis import IRAnalysesCache 4 | from vyper.venom.context import IRContext 5 | from vyper.venom.passes import StoreExpansionPass 6 | 7 | 8 | def test_duplicate_operands(): 9 | """ 10 | Test the duplicate operands code generation. 11 | The venom code: 12 | 13 | %1 = 10 14 | %2 = add %1, %1 15 | %3 = mul %1, %2 16 | stop 17 | 18 | Should compile to: [PUSH1, 10, DUP1, DUP2, ADD, MUL, POP, STOP] 19 | """ 20 | ctx = IRContext() 21 | fn = ctx.create_function("test") 22 | bb = fn.get_basic_block() 23 | op = bb.append_instruction("store", 10) 24 | sum_ = bb.append_instruction("add", op, op) 25 | bb.append_instruction("mul", sum_, op) 26 | bb.append_instruction("stop") 27 | 28 | ac = IRAnalysesCache(fn) 29 | StoreExpansionPass(ac, fn).run_pass() 30 | 31 | optimize = OptimizationLevel.GAS 32 | asm = generate_assembly_experimental(ctx, optimize=optimize) 33 | assert asm == ["PUSH1", 10, "DUP1", "DUP2", "ADD", "MUL", "POP", "STOP"] 34 | -------------------------------------------------------------------------------- /tests/functional/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 tests.utils import decimal_to_int, parse_and_fold 8 | 9 | st_decimals = st.decimals( 10 | min_value=-(2**32), max_value=2**32, allow_nan=False, allow_infinity=False, places=10 11 | ) 12 | 13 | 14 | @pytest.mark.fuzzing 15 | @settings(max_examples=50) 16 | @given(value=st_decimals) 17 | @example(value=Decimal("0.9999999999")) 18 | @example(value=Decimal("0.0000000001")) 19 | @example(value=Decimal("-0.9999999999")) 20 | @example(value=Decimal("-0.0000000001")) 21 | @pytest.mark.parametrize("fn_name", ["floor", "ceil"]) 22 | def test_floor_ceil(get_contract, value, fn_name): 23 | source = f""" 24 | @external 25 | def foo(a: decimal) -> int256: 26 | return {fn_name}(a) 27 | """ 28 | contract = get_contract(source) 29 | 30 | vyper_ast = parse_and_fold(f"{fn_name}({value})") 31 | old_node = vyper_ast.body[0].value 32 | new_node = old_node.get_folded_value() 33 | 34 | assert isinstance(new_node.value, int) 35 | assert contract.foo(decimal_to_int(value)) == new_node.value 36 | -------------------------------------------------------------------------------- /tests/unit/compiler/venom/test_stack_reorder.py: -------------------------------------------------------------------------------- 1 | from vyper.venom import generate_assembly_experimental 2 | from vyper.venom.analysis import IRAnalysesCache 3 | from vyper.venom.context import IRContext 4 | from vyper.venom.passes import StoreExpansionPass 5 | 6 | 7 | def test_stack_reorder(): 8 | """ 9 | Test to was created from the example in the 10 | issue https://github.com/vyperlang/vyper/issues/4215 11 | this example should fail with original stack reorder 12 | algorithm but succeed with new one 13 | """ 14 | ctx = IRContext() 15 | fn = ctx.create_function("_global") 16 | 17 | bb = fn.get_basic_block() 18 | var0 = bb.append_instruction("store", 1) 19 | var1 = bb.append_instruction("store", 2) 20 | var2 = bb.append_instruction("store", 3) 21 | var3 = bb.append_instruction("store", 4) 22 | var4 = bb.append_instruction("store", 5) 23 | 24 | bb.append_instruction("staticcall", var0, var1, var2, var3, var4, var3) 25 | 26 | ret_val = bb.append_instruction("add", var4, var4) 27 | 28 | bb.append_instruction("ret", ret_val) 29 | 30 | ac = IRAnalysesCache(fn) 31 | StoreExpansionPass(ac, fn).run_pass() 32 | 33 | generate_assembly_experimental(ctx) 34 | -------------------------------------------------------------------------------- /tests/functional/syntax/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 | @deploy 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/functional/syntax/modules/test_implements.py: -------------------------------------------------------------------------------- 1 | from vyper.compiler import compile_code 2 | 3 | 4 | def test_implements_from_vyi(make_input_bundle): 5 | vyi = """ 6 | @external 7 | def foo(): 8 | ... 9 | """ 10 | lib1 = """ 11 | import some_interface 12 | """ 13 | main = """ 14 | import lib1 15 | 16 | implements: lib1.some_interface 17 | 18 | @external 19 | def foo(): # implementation 20 | pass 21 | """ 22 | input_bundle = make_input_bundle({"some_interface.vyi": vyi, "lib1.vy": lib1}) 23 | 24 | assert compile_code(main, input_bundle=input_bundle) is not None 25 | 26 | 27 | def test_implements_from_vyi2(make_input_bundle): 28 | # test implements via nested imported vyi file 29 | vyi = """ 30 | @external 31 | def foo(): 32 | ... 33 | """ 34 | lib1 = """ 35 | import some_interface 36 | """ 37 | lib2 = """ 38 | import lib1 39 | """ 40 | main = """ 41 | import lib2 42 | 43 | implements: lib2.lib1.some_interface 44 | 45 | @external 46 | def foo(): # implementation 47 | pass 48 | """ 49 | input_bundle = make_input_bundle({"some_interface.vyi": vyi, "lib1.vy": lib1, "lib2.vy": lib2}) 50 | 51 | assert compile_code(main, input_bundle=input_bundle) is not None 52 | -------------------------------------------------------------------------------- /tests/functional/codegen/environment_variables/test_blockhash.py: -------------------------------------------------------------------------------- 1 | def test_blockhash(get_contract): 2 | block_number_code = """ 3 | @external 4 | def prev() -> bytes32: 5 | return block.prevhash 6 | 7 | @external 8 | def previous_blockhash() -> bytes32: 9 | return blockhash(block.number - 1) 10 | """ 11 | c = get_contract(block_number_code) 12 | assert c.prev() == c.previous_blockhash() 13 | 14 | 15 | def test_negative_blockhash(assert_compile_failed, get_contract): 16 | code = """ 17 | @external 18 | def foo() -> bytes32: 19 | return blockhash(-1) 20 | """ 21 | assert_compile_failed(lambda: get_contract(code)) 22 | 23 | 24 | def test_too_old_blockhash(tx_failed, get_contract, env): 25 | env.block_number += 257 26 | code = """ 27 | @external 28 | def get_50_blockhash() -> bytes32: 29 | return blockhash(block.number - 257) 30 | """ 31 | c = get_contract(code) 32 | with tx_failed(): 33 | c.get_50_blockhash() 34 | 35 | 36 | def test_non_existing_blockhash(tx_failed, get_contract): 37 | code = """ 38 | @external 39 | def get_future_blockhash() -> bytes32: 40 | return blockhash(block.number + 1) 41 | """ 42 | c = get_contract(code) 43 | with tx_failed(): 44 | c.get_future_blockhash() 45 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/functional/codegen/environment_variables/test_blobbasefee.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from eth.vm.forks.cancun.constants import BLOB_BASE_FEE_UPDATE_FRACTION, MIN_BLOB_BASE_FEE 3 | from eth.vm.forks.cancun.state import fake_exponential 4 | 5 | 6 | @pytest.mark.requires_evm_version("cancun") 7 | def test_blobbasefee(env, get_contract): 8 | code = """ 9 | @external 10 | @view 11 | def get_blobbasefee() -> uint256: 12 | return block.blobbasefee 13 | """ 14 | c = get_contract(code) 15 | 16 | assert c.get_blobbasefee() == MIN_BLOB_BASE_FEE 17 | 18 | env.set_balance(env.deployer, 10**20) 19 | env.set_excess_blob_gas(10**6) 20 | 21 | # kzg_hash(b"Vyper is the language of the sneks") 22 | env.blob_hashes = [ 23 | (bytes.fromhex("015a5c97e3cc516f22a95faf7eefff00eb2fee7a65037fde07ac5446fc93f2a0")) 24 | ] * 6 25 | 26 | env.message_call( 27 | "0xb45BEc6eeCA2a09f4689Dd308F550Ad7855051B5", # random address 28 | gas=21000, 29 | gas_price=10**10, 30 | ) 31 | 32 | excess_blob_gas = env.get_excess_blob_gas() 33 | expected_blobbasefee = fake_exponential( 34 | MIN_BLOB_BASE_FEE, excess_blob_gas, BLOB_BASE_FEE_UPDATE_FRACTION 35 | ) 36 | assert c.get_blobbasefee() == expected_blobbasefee 37 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_init.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.compiler import compile_code 4 | from vyper.exceptions import FunctionDeclarationException 5 | 6 | good_list = [ 7 | """ 8 | @deploy 9 | def __init__(): 10 | pass 11 | """, 12 | """ 13 | @deploy 14 | @payable 15 | def __init__(): 16 | pass 17 | """, 18 | """ 19 | counter: uint256 20 | SOME_IMMUTABLE: immutable(uint256) 21 | 22 | @deploy 23 | def __init__(): 24 | SOME_IMMUTABLE = 5 25 | self.counter = 1 26 | """, 27 | ] 28 | 29 | 30 | @pytest.mark.parametrize("code", good_list) 31 | def test_good_init_funcs(code): 32 | assert compile_code(code) is not None 33 | 34 | 35 | fail_list = [ 36 | """ 37 | @internal 38 | def __init__(): 39 | pass 40 | """, 41 | """ 42 | @deploy 43 | @view 44 | def __init__(): 45 | pass 46 | """, 47 | """ 48 | @deploy 49 | @pure 50 | def __init__(): 51 | pass 52 | """, 53 | """ 54 | @deploy 55 | def some_function(): # for now, only __init__() functions can be marked @deploy 56 | pass 57 | """, 58 | ] 59 | 60 | 61 | @pytest.mark.parametrize("code", fail_list) 62 | def test_bad_init_funcs(code): 63 | with pytest.raises(FunctionDeclarationException): 64 | compile_code(code) 65 | -------------------------------------------------------------------------------- /tests/functional/syntax/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(): 22 | x: String[100] = "these bytes are nо gооd because the o's are from the Russian alphabet" 23 | """, 24 | """ 25 | @external 26 | def foo(): 27 | x: String[100] = "这个傻老外不懂中文" 28 | """, 29 | """ 30 | @external 31 | def foo(): 32 | a: Bytes[100] = "ѓtest" 33 | """, 34 | """ 35 | @external 36 | def foo(): 37 | a: bytes32 = keccak256("ѓtest") 38 | """, 39 | # test constant folding inside of `convert()` 40 | """ 41 | BAR: constant(uint16) = 256 42 | 43 | @external 44 | def foo(): 45 | a: uint8 = convert(BAR, uint8) 46 | """, 47 | ] 48 | 49 | 50 | @pytest.mark.parametrize("bad_code", fail_list) 51 | def test_invalid_literal_exception(bad_code): 52 | with raises(InvalidLiteral): 53 | compiler.compile_code(bad_code) 54 | -------------------------------------------------------------------------------- /tests/unit/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: address in [0x6b175474e89094c44da98b954eedeac495271d0F]: 28 | pass 29 | """, 30 | """ 31 | foo: constant(bytes20) = 0x6b175474e89094c44da98b954eedeac495271d0F 32 | """, 33 | """ 34 | foo: constant(bytes4) = 0x12_34_56 35 | """, 36 | """ 37 | foo: constant(bytes4) = 0X12345678 38 | """, 39 | ] 40 | 41 | 42 | @pytest.mark.parametrize("code", code_invalid_checksum) 43 | def test_invalid_checksum(code): 44 | with pytest.raises(InvalidLiteral): 45 | vyper_module = vy_ast.parse_to_ast(code) 46 | semantics.analyze_module(vyper_module) 47 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_len.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import TypeMismatch 5 | 6 | fail_list = [ 7 | """ 8 | @external 9 | def foo(inp: Bytes[4]) -> int128: 10 | return len(inp) 11 | """, 12 | """ 13 | @external 14 | def foo(inp: int128) -> uint256: 15 | return len(inp) 16 | """, 17 | ] 18 | 19 | 20 | @pytest.mark.parametrize("bad_code", fail_list) 21 | def test_block_fail(bad_code): 22 | if isinstance(bad_code, tuple): 23 | with pytest.raises(bad_code[1]): 24 | compile_code(bad_code[0]) 25 | else: 26 | with pytest.raises(TypeMismatch): 27 | compile_code(bad_code) 28 | 29 | 30 | valid_list = [ 31 | """ 32 | @external 33 | def foo(inp: Bytes[10]) -> uint256: 34 | return len(inp) 35 | """, 36 | """ 37 | @external 38 | def foo(inp: String[10]) -> uint256: 39 | return len(inp) 40 | """, 41 | """ 42 | BAR: constant(String[5]) = "vyper" 43 | FOO: constant(uint256) = len(BAR) 44 | 45 | @external 46 | def foo() -> uint256: 47 | a: uint256 = FOO 48 | return a 49 | """, 50 | ] 51 | 52 | 53 | @pytest.mark.parametrize("good_code", valid_list) 54 | def test_list_success(good_code): 55 | assert compile_code(good_code) is not None 56 | -------------------------------------------------------------------------------- /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://docs.vyperlang.org/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 | -------------------------------------------------------------------------------- /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 | docs 13 | build 14 | per-file-ignores = 15 | */__init__.py: F401 16 | 17 | [tool:isort] 18 | force_grid_wrap = 0 19 | include_trailing_comma = True 20 | known_first_party = vyper 21 | multi_line_output = 3 22 | use_parentheses = True 23 | ensure_newline_before_comments = True 24 | line_length = 100 25 | 26 | [tool:pytest] 27 | addopts = -n auto 28 | --dist worksteal 29 | python_files = test_*.py 30 | testpaths = tests 31 | xfail_strict = true 32 | markers = 33 | fuzzing: Run Hypothesis fuzz test suite (deselect with '-m "not fuzzing"') 34 | requires_evm_version(version): Mark tests that require at least a specific EVM version and would throw `EvmVersionException` otherwise 35 | venom_xfail: mark a test case as a regression (expected to fail) under the venom pipeline 36 | 37 | 38 | [coverage:run] 39 | branch = True 40 | source = vyper 41 | 42 | # this is not available on the CI step that performs `coverage combine` 43 | omit = vyper/version.py 44 | 45 | # allow `coverage combine` to combine reports from heterogeneous OSes. 46 | # (mainly important for consolidating coverage reports in the CI). 47 | relative_files = True 48 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_addmulmod.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import TypeMismatch 5 | 6 | fail_list = [ 7 | ( # bad AST nodes given as arguments 8 | """ 9 | @external 10 | def foo() -> uint256: 11 | return uint256_addmod(1.1, 1.2, 3.0) 12 | """, 13 | TypeMismatch, 14 | ), 15 | ( # bad AST nodes given as arguments 16 | """ 17 | @external 18 | def foo() -> uint256: 19 | return uint256_mulmod(1.1, 1.2, 3.0) 20 | """, 21 | TypeMismatch, 22 | ), 23 | ] 24 | 25 | 26 | @pytest.mark.parametrize("code,exc", fail_list) 27 | def test_add_mod_fail(assert_compile_failed, get_contract, code, exc): 28 | assert_compile_failed(lambda: get_contract(code), exc) 29 | 30 | 31 | valid_list = [ 32 | """ 33 | FOO: constant(uint256) = 3 34 | BAR: constant(uint256) = 5 35 | BAZ: constant(uint256) = 19 36 | BAX: constant(uint256) = uint256_addmod(FOO, BAR, BAZ) 37 | """, 38 | """ 39 | FOO: constant(uint256) = 3 40 | BAR: constant(uint256) = 5 41 | BAZ: constant(uint256) = 19 42 | BAX: constant(uint256) = uint256_mulmod(FOO, BAR, BAZ) 43 | """, 44 | ] 45 | 46 | 47 | @pytest.mark.parametrize("code", valid_list) 48 | def test_addmulmod_pass(code): 49 | assert compile_code(code) is not None 50 | -------------------------------------------------------------------------------- /tests/unit/compiler/venom/test_make_ssa.py: -------------------------------------------------------------------------------- 1 | from tests.venom_utils import assert_ctx_eq, parse_venom 2 | from vyper.venom.analysis import IRAnalysesCache 3 | from vyper.venom.passes import MakeSSA 4 | 5 | 6 | def _check_pre_post(pre, post): 7 | ctx = parse_venom(pre) 8 | for fn in ctx.functions.values(): 9 | ac = IRAnalysesCache(fn) 10 | MakeSSA(ac, fn).run_pass() 11 | assert_ctx_eq(ctx, parse_venom(post)) 12 | 13 | 14 | def test_phi_case(): 15 | pre = """ 16 | function loop { 17 | main: 18 | %v = mload 64 19 | jmp @test 20 | test: 21 | jnz %v, @then, @else 22 | then: 23 | %t = mload 96 24 | assert %t 25 | jmp @if_exit 26 | else: 27 | jmp @if_exit 28 | if_exit: 29 | %v = add %v, 1 30 | jmp @test 31 | } 32 | """ 33 | post = """ 34 | function loop { 35 | main: 36 | %v = mload 64 37 | jmp @test 38 | test: 39 | %v:1 = phi @main, %v, @if_exit, %v:2 40 | jnz %v:1, @then, @else 41 | then: 42 | %t = mload 96 43 | assert %t 44 | jmp @if_exit 45 | else: 46 | jmp @if_exit 47 | if_exit: 48 | %v:2 = add %v:1, 1 49 | jmp @test 50 | } 51 | """ 52 | _check_pre_post(pre, post) 53 | -------------------------------------------------------------------------------- /tests/unit/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/functional/syntax/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/functional/syntax/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 | @external 23 | def test_func() -> int128: 24 | return (1, 2) 25 | """, 26 | """ 27 | @deploy 28 | def __init__(a: int128 = 12): 29 | pass 30 | """, 31 | """ 32 | @deploy 33 | def __init__() -> uint256: 34 | return 1 35 | """, 36 | """ 37 | @deploy 38 | def __init__() -> bool: 39 | pass 40 | """, 41 | """ 42 | a: immutable(uint256) 43 | 44 | @internal 45 | def __init__(): 46 | a = 1 47 | """, 48 | """ 49 | a: immutable(uint256) 50 | 51 | @deploy 52 | @pure 53 | def __init__(): 54 | a = 1 55 | """, 56 | """ 57 | a: immutable(uint256) 58 | 59 | @deploy 60 | @view 61 | def __init__(): 62 | a = 1 63 | """, 64 | ] 65 | 66 | 67 | @pytest.mark.parametrize("bad_code", fail_list) 68 | def test_function_declaration_exception(bad_code): 69 | with pytest.raises(FunctionDeclarationException): 70 | compiler.compile_code(bad_code) 71 | -------------------------------------------------------------------------------- /tests/unit/compiler/venom/test_convert_basicblock_simple.py: -------------------------------------------------------------------------------- 1 | from vyper.codegen.ir_node import IRnode 2 | from vyper.venom.ir_node_to_venom import ir_node_to_venom 3 | 4 | 5 | def test_simple(): 6 | ir = IRnode.from_list(["calldatacopy", 32, 0, ["calldatasize"]]) 7 | ir_node = IRnode.from_list(ir) 8 | venom = ir_node_to_venom(ir_node) 9 | assert venom is not None 10 | 11 | fn = list(venom.functions.values())[0] 12 | 13 | bb = fn.entry 14 | assert bb.instructions[0].opcode == "calldatasize" 15 | assert bb.instructions[1].opcode == "calldatacopy" 16 | 17 | 18 | def test_simple_2(): 19 | ir = [ 20 | "seq", 21 | [ 22 | "seq", 23 | [ 24 | "mstore", 25 | ["add", 64, 0], 26 | [ 27 | "with", 28 | "x", 29 | ["calldataload", ["add", 4, 0]], 30 | [ 31 | "with", 32 | "ans", 33 | ["add", "x", 1], 34 | ["seq", ["assert", ["ge", "ans", "x"]], "ans"], 35 | ], 36 | ], 37 | ], 38 | ], 39 | 32, 40 | ] 41 | ir_node = IRnode.from_list(ir) 42 | venom = ir_node_to_venom(ir_node) 43 | assert venom is not None 44 | -------------------------------------------------------------------------------- /tests/functional/syntax/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 | # Must be a literal string. 26 | """ 27 | @external 28 | def mint(_to: address, _value: uint256): 29 | assert msg.sender == self,msg.sender 30 | """, 31 | # Raise reason must be string 32 | """ 33 | @external 34 | def mint(_to: address, _value: uint256): 35 | raise 1 36 | """, 37 | """ 38 | x: int128[3.5] 39 | """, 40 | # Key of mapping must be a base type 41 | """ 42 | b: HashMap[(int128, decimal), int128] 43 | """, 44 | """ 45 | x: String <= 33 46 | """, 47 | """ 48 | x: Bytes <= wei 49 | """, 50 | """ 51 | x: 5 52 | """, 53 | ] 54 | 55 | 56 | @pytest.mark.parametrize("bad_code", invalid_list) 57 | def test_invalid_type_exception(bad_code, get_contract, assert_compile_failed): 58 | assert_compile_failed(lambda: get_contract(bad_code), InvalidType) 59 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/IERC721.vyi: -------------------------------------------------------------------------------- 1 | # Events 2 | 3 | event Transfer: 4 | sender: indexed(address) 5 | receiver: indexed(address) 6 | token_id: indexed(uint256) 7 | 8 | event Approval: 9 | owner: indexed(address) 10 | approved: indexed(address) 11 | token_id: indexed(uint256) 12 | 13 | event ApprovalForAll: 14 | owner: indexed(address) 15 | operator: indexed(address) 16 | approved: bool 17 | 18 | # Functions 19 | 20 | @view 21 | @external 22 | def supportsInterface(interface_id: bytes4) -> bool: 23 | ... 24 | 25 | @view 26 | @external 27 | def balanceOf(_owner: address) -> uint256: 28 | ... 29 | 30 | @view 31 | @external 32 | def ownerOf(_tokenId: uint256) -> address: 33 | ... 34 | 35 | @view 36 | @external 37 | def getApproved(_tokenId: uint256) -> address: 38 | ... 39 | 40 | @view 41 | @external 42 | def isApprovedForAll(_owner: address, _operator: address) -> bool: 43 | ... 44 | 45 | @external 46 | @payable 47 | def transferFrom(_from: address, _to: address, _tokenId: uint256): 48 | ... 49 | 50 | @external 51 | @payable 52 | def safeTransferFrom(_from: address, _to: address, _tokenId: uint256, _data: Bytes[1024] = b""): 53 | ... 54 | 55 | @external 56 | @payable 57 | def approve(_approved: address, _tokenId: uint256): 58 | ... 59 | 60 | @external 61 | def setApprovalForAll(_operator: address, _approved: bool): 62 | ... 63 | -------------------------------------------------------------------------------- /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 `Titanoboa `_: 25 | 26 | .. code-block:: python 27 | 28 | import boa 29 | boa.set_network_env() 30 | from eth_account import Account 31 | # in a real codebase, always load private keys safely from an encrypted store! 32 | boa.env.add_account(Account()) 33 | deployer = boa.load_partial("yourFileName.vy") 34 | deployer.deploy() 35 | 36 | * Use the development environment provided at https://try.vyperlang.org to compile and deploy your contract on your net of choice. try.vyperlang.org comes "batteries-included", with Titanoboa pre-installed, and browser signer integration as well. 37 | -------------------------------------------------------------------------------- /tests/unit/ast/nodes/test_fold_subscript.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from hypothesis import given, settings 3 | from hypothesis import strategies as st 4 | 5 | from tests.utils import parse_and_fold 6 | from vyper.compiler import compile_code 7 | from vyper.exceptions import ArrayIndexException 8 | 9 | 10 | @pytest.mark.fuzzing 11 | @settings(max_examples=50) 12 | @given( 13 | idx=st.integers(min_value=0, max_value=9), 14 | array=st.lists(st.integers(), min_size=10, max_size=10), 15 | ) 16 | def test_subscript(get_contract, array, idx): 17 | source = """ 18 | @external 19 | def foo(array: int128[10], idx: uint256) -> int128: 20 | return array[idx] 21 | """ 22 | contract = get_contract(source) 23 | 24 | vyper_ast = parse_and_fold(f"{array}[{idx}]") 25 | old_node = vyper_ast.body[0].value 26 | new_node = old_node.get_folded_value() 27 | 28 | assert contract.foo(array, idx) == new_node.value 29 | 30 | 31 | def test_negative_index(): 32 | source = """ 33 | @external 34 | def foo(array: int128[10]) -> int128: 35 | return array[0 - 1] 36 | """ 37 | with pytest.raises(ArrayIndexException): 38 | compile_code(source) 39 | 40 | 41 | def test_oob_index(): 42 | source = """ 43 | @external 44 | def foo(array: int128[10]) -> int128: 45 | return array[9 + 1] 46 | """ 47 | with pytest.raises(ArrayIndexException): 48 | compile_code(source) 49 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_dynamic_array.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 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 | @external 30 | def foo(): 31 | a: DynArray[uint256, FOO] = [1, 2, 3] 32 | """, 33 | StructureException, 34 | ), 35 | ] 36 | 37 | 38 | @pytest.mark.parametrize("bad_code,exc", fail_list) 39 | def test_block_fail(bad_code, exc): 40 | with pytest.raises(exc): 41 | compile_code(bad_code) 42 | 43 | 44 | valid_list = [ 45 | """ 46 | flag Foo: 47 | FE 48 | FI 49 | 50 | bar: DynArray[Foo, 10] 51 | """, # dynamic arrays of flags are allowed, but not static arrays 52 | """ 53 | bar: DynArray[Bytes[30], 10] 54 | """, # dynamic arrays of bytestrings are allowed, but not static arrays 55 | ] 56 | 57 | 58 | @pytest.mark.parametrize("good_code", valid_list) 59 | def test_dynarray_pass(good_code): 60 | assert compile_code(good_code) is not None 61 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/test_reverting.py: -------------------------------------------------------------------------------- 1 | from eth.codecs import abi 2 | 3 | from vyper.utils import method_id 4 | 5 | 6 | def test_revert_reason(env, tx_failed, get_contract): 7 | reverty_code = """ 8 | @external 9 | def foo(): 10 | data: Bytes[4] = method_id("NoFives()") 11 | raw_revert(data) 12 | """ 13 | 14 | revert_bytes = method_id("NoFives()") 15 | 16 | with tx_failed(exc_text=revert_bytes.hex()): 17 | get_contract(reverty_code).foo() 18 | 19 | 20 | def test_revert_reason_typed(env, tx_failed, get_contract): 21 | reverty_code = """ 22 | @external 23 | def foo(): 24 | val: uint256 = 5 25 | data: Bytes[100] = _abi_encode(val, method_id=method_id("NoFives(uint256)")) 26 | raw_revert(data) 27 | """ 28 | 29 | revert_bytes = method_id("NoFives(uint256)") + abi.encode("(uint256)", (5,)) 30 | 31 | with tx_failed(exc_text=revert_bytes.hex()): 32 | get_contract(reverty_code).foo() 33 | 34 | 35 | def test_revert_reason_typed_no_variable(env, tx_failed, get_contract): 36 | reverty_code = """ 37 | @external 38 | def foo(): 39 | val: uint256 = 5 40 | raw_revert(_abi_encode(val, method_id=method_id("NoFives(uint256)"))) 41 | """ 42 | 43 | revert_bytes = method_id("NoFives(uint256)") + abi.encode("(uint256)", (5,)) 44 | 45 | with tx_failed(exc_text=revert_bytes.hex()): 46 | get_contract(reverty_code).foo() 47 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_keccak256.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import TypeMismatch, 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(TypeMismatch): 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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/vip.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Vyper Improvement Proposal (VIP) 3 | about: This is the suggested template for new VIPs. 4 | labels: ["needs triage"] 5 | --- 6 | ## Simple Summary 7 | "If you can't explain it simply, you don't understand it well enough." Provide a simplified and layman-accessible explanation of the VIP. 8 | 9 | ## Motivation 10 | The motivation is critical for VIPs that add or change Vyper's functionality. It should clearly explain why the existing Vyper functionality is inadequate to address the problem that the VIP solves as well as how the VIP is in line with Vyper's goals and design philosophy. 11 | 12 | ## Specification 13 | The technical specification should describe the syntax and semantics of any new feature. The specification should be detailed enough to allow any developer to implement the functionality 14 | 15 | ## Backwards Compatibility 16 | All VIPs that introduce backwards incompatibilities must include a section describing these incompatibilities and their severity. The VIP must explain how the author proposes to deal with these incompatibilities. 17 | 18 | ## Dependencies 19 | If this VIP depends on any other VIPs being implemented, please mention them. 20 | 21 | ## References 22 | Add any references that this VIP might reference (other VIPs/issues, links to blog posts, etc.) 23 | 24 | ## Copyright 25 | Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/) 26 | -------------------------------------------------------------------------------- /tests/unit/cli/vyper_json/test_get_settings.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.cli.vyper_json import get_evm_version, get_settings 4 | from vyper.exceptions import JSONError 5 | 6 | 7 | def test_unknown_evm(): 8 | with pytest.raises(JSONError): 9 | get_evm_version({"settings": {"evmVersion": "foo"}}) 10 | 11 | 12 | @pytest.mark.parametrize( 13 | "evm_version_str", 14 | [ 15 | "homestead", 16 | "tangerineWhistle", 17 | "spuriousDragon", 18 | "byzantium", 19 | "constantinople", 20 | "petersburg", 21 | "istanbul", 22 | "berlin", 23 | ], 24 | ) 25 | def test_early_evm(evm_version_str): 26 | with pytest.raises(JSONError): 27 | get_evm_version({"settings": {"evmVersion": evm_version_str}}) 28 | 29 | 30 | @pytest.mark.parametrize("evm_version_str", ["london", "paris", "shanghai", "cancun"]) 31 | def test_valid_evm(evm_version_str): 32 | assert evm_version_str == get_evm_version({"settings": {"evmVersion": evm_version_str}}) 33 | 34 | 35 | def test_experimental_codegen_settings(): 36 | input_json = {"settings": {}} 37 | assert get_settings(input_json).experimental_codegen is None 38 | 39 | input_json = {"settings": {"experimentalCodegen": True}} 40 | assert get_settings(input_json).experimental_codegen is True 41 | 42 | input_json = {"settings": {"experimentalCodegen": False}} 43 | assert get_settings(input_json).experimental_codegen is False 44 | -------------------------------------------------------------------------------- /vyper/venom/passes/float_allocas.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.passes.base_pass import IRPass 2 | 3 | 4 | class FloatAllocas(IRPass): 5 | """ 6 | This pass moves allocas to the entry basic block of a function 7 | We could probably move them to the immediate dominator of the basic 8 | block defining the alloca instead of the entry (which dominates all 9 | basic blocks), but this is done for expedience. 10 | Without this step, sccp fails, possibly because dominators are not 11 | guaranteed to be traversed first. 12 | """ 13 | 14 | def run_pass(self): 15 | entry_bb = self.function.entry 16 | assert entry_bb.is_terminated 17 | tmp = entry_bb.instructions.pop() 18 | 19 | for bb in self.function.get_basic_blocks(): 20 | if bb is entry_bb: 21 | continue 22 | 23 | # Extract alloca instructions 24 | non_alloca_instructions = [] 25 | for inst in bb.instructions: 26 | if inst.opcode in ("alloca", "palloca"): 27 | # note: order of allocas impacts bytecode. 28 | # TODO: investigate. 29 | entry_bb.insert_instruction(inst) 30 | else: 31 | non_alloca_instructions.append(inst) 32 | 33 | # Replace original instructions with filtered list 34 | bb.instructions = non_alloca_instructions 35 | 36 | entry_bb.instructions.append(tmp) 37 | -------------------------------------------------------------------------------- /tests/functional/codegen/features/test_packing.py: -------------------------------------------------------------------------------- 1 | def test_packing_test(get_contract): 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(packing_test) 50 | assert c.foo() == 1023, c.foo() 51 | assert c.fop() == 1023, c.fop() 52 | print("Passed packing test") 53 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_minmax.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import OverflowException, TypeMismatch 5 | 6 | fail_list = [ 7 | ( 8 | """ 9 | @external 10 | def foo(): 11 | y: int128 = min(7, 0x1234567890123456789012345678901234567890) 12 | """, 13 | TypeMismatch, 14 | ), 15 | ( 16 | """ 17 | @external 18 | def foo(): 19 | a: int16 = min(min_value(int16), max_value(int8)) 20 | """, 21 | TypeMismatch, 22 | ), 23 | ( 24 | """ 25 | @external 26 | def foo(): 27 | a: decimal = min(1.0, 18707220957835557353007165858768422651595.9365500928) 28 | """, 29 | OverflowException, 30 | ), 31 | ] 32 | 33 | 34 | @pytest.mark.parametrize("bad_code,exc", fail_list) 35 | def test_block_fail(bad_code, exc): 36 | with pytest.raises(exc): 37 | compile_code(bad_code) 38 | 39 | 40 | valid_list = [ 41 | """ 42 | FOO: constant(uint256) = 123 43 | BAR: constant(uint256) = 456 44 | BAZ: constant(uint256) = min(FOO, BAR) 45 | 46 | @external 47 | def foo(): 48 | a: uint256 = BAZ 49 | """, 50 | """ 51 | FOO: constant(uint256) = 123 52 | BAR: constant(uint256) = 456 53 | BAZ: constant(uint256) = max(FOO, BAR) 54 | 55 | @external 56 | def foo(): 57 | a: uint256 = BAZ 58 | """, 59 | ] 60 | 61 | 62 | @pytest.mark.parametrize("good_code", valid_list) 63 | def test_block_success(good_code): 64 | assert compile_code(good_code) is not None 65 | -------------------------------------------------------------------------------- /tests/functional/codegen/types/test_struct.py: -------------------------------------------------------------------------------- 1 | def test_nested_struct(get_contract): 2 | code = """ 3 | struct Animal: 4 | location: address 5 | fur: String[32] 6 | 7 | struct Human: 8 | location: address 9 | animal: Animal 10 | 11 | @external 12 | def modify_nested_struct(_human: Human) -> Human: 13 | human: Human = _human 14 | 15 | # do stuff, edit the structs 16 | # (13 is the length of the result) 17 | human.animal.fur = slice(concat(human.animal.fur, " is great"), 0, 13) 18 | 19 | return human 20 | """ 21 | c = get_contract(code) 22 | addr1 = "0x1234567890123456789012345678901234567890" 23 | addr2 = "0x1234567890123456789012345678900000000000" 24 | # assert c.modify_nested_tuple([addr1, 123], [addr2, 456]) == ([addr1, 124], [addr2, 457]) 25 | assert c.modify_nested_struct((addr1, (addr2, "wool"))) == (addr1, (addr2, "wool is great")) 26 | 27 | 28 | def test_nested_single_struct(get_contract): 29 | code = """ 30 | struct Animal: 31 | fur: String[32] 32 | 33 | struct Human: 34 | animal: Animal 35 | 36 | @external 37 | def modify_nested_single_struct(_human: Human) -> Human: 38 | human: Human = _human 39 | 40 | # do stuff, edit the structs 41 | # (13 is the length of the result) 42 | human.animal.fur = slice(concat(human.animal.fur, " is great"), 0, 13) 43 | 44 | return human 45 | """ 46 | c = get_contract(code) 47 | 48 | assert c.modify_nested_single_struct((("wool",),)) == (("wool is great",),) 49 | -------------------------------------------------------------------------------- /tests/functional/syntax/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 | @external 30 | def foo(): 31 | raw_log(b"cow", b"dog") 32 | """, 33 | """ 34 | @external 35 | def foo(): 36 | xs: uint256[1] = [] 37 | """, 38 | # literal longer than event member 39 | """ 40 | event Foo: 41 | message: String[1] 42 | @external 43 | def foo(): 44 | log Foo(message="abcd") 45 | """, 46 | # Address literal must be checksummed 47 | """ 48 | a: constant(address) = 0x3cd751e6b0078be393132286c442345e5dc49699 49 | """, 50 | # test constant folding inside `convert()` 51 | """ 52 | BAR: constant(Bytes[5]) = b"vyper" 53 | 54 | @external 55 | def foo(): 56 | a: Bytes[4] = convert(BAR, Bytes[4]) 57 | """, 58 | ] 59 | 60 | 61 | @pytest.mark.parametrize("bad_code", fail_list) 62 | def test_type_mismatch_exception(bad_code): 63 | with raises(TypeMismatch): 64 | compiler.compile_code(bad_code) 65 | -------------------------------------------------------------------------------- /vyper/venom/passes/store_expansion.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis 2 | from vyper.venom.basicblock import IRInstruction, IRLiteral, IRVariable 3 | from vyper.venom.passes.base_pass import IRPass 4 | 5 | 6 | class StoreExpansionPass(IRPass): 7 | """ 8 | This pass extracts literals and variables so that they can be 9 | reordered by the DFT pass 10 | """ 11 | 12 | def run_pass(self): 13 | for bb in self.function.get_basic_blocks(): 14 | self._process_bb(bb) 15 | 16 | self.analyses_cache.invalidate_analysis(DFGAnalysis) 17 | self.analyses_cache.invalidate_analysis(LivenessAnalysis) 18 | 19 | def _process_bb(self, bb): 20 | i = 0 21 | while i < len(bb.instructions): 22 | inst = bb.instructions[i] 23 | if inst.opcode in ("store", "offset", "phi", "param"): 24 | i += 1 25 | continue 26 | 27 | for j, op in enumerate(inst.operands): 28 | # first operand to log is magic 29 | if inst.opcode == "log" and j == 0: 30 | continue 31 | 32 | if isinstance(op, (IRVariable, IRLiteral)): 33 | var = self.function.get_next_variable() 34 | to_insert = IRInstruction("store", [op], var) 35 | bb.insert_instruction(to_insert, index=i) 36 | inst.operands[j] = var 37 | i += 1 38 | 39 | i += 1 40 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/functional/syntax/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) as e: 70 | compiler.compile_code(bad_code) 71 | assert "(hint: )" not in str(e.value) 72 | -------------------------------------------------------------------------------- /.github/workflows/release-pypi.yml: -------------------------------------------------------------------------------- 1 | # upload to pypi using the pypa publish action 2 | # https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Publish to PyPI 5 | 6 | on: 7 | release: 8 | types: [published] # releases and pre-releases (release candidates) 9 | 10 | jobs: 11 | publish-pypi: 12 | runs-on: ubuntu-latest 13 | 14 | # https://docs.pypi.org/trusted-publishers/using-a-publisher/ 15 | permissions: 16 | # IMPORTANT: this permission is mandatory for trusted publishing 17 | id-token: write 18 | # Specifying a GitHub environment is optional, but strongly encouraged 19 | environment: release 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | with: 24 | # fetch unshallow so commit hash matches github release. 25 | # see https://github.com/vyperlang/vyper/blob/8f9a8cac49aafb3fbc9dde78f0f6125c390c32f0/.github/workflows/build.yml#L27-L32 26 | fetch-depth: 0 27 | 28 | # debug 29 | - name: Git shorthash 30 | run: git rev-parse --short HEAD 31 | 32 | - name: Python 33 | uses: actions/setup-python@v5 34 | with: 35 | python-version: "3.11" 36 | 37 | - name: Install dependencies 38 | run: | 39 | python -m pip install --upgrade pip 40 | pip install setuptools wheel twine 41 | 42 | - name: Build 43 | run: python setup.py sdist bdist_wheel 44 | 45 | - name: Publish 46 | uses: pypa/gh-action-pypi-publish@release/v1 47 | -------------------------------------------------------------------------------- /tests/functional/syntax/modules/test_deploy_visibility.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.compiler import compile_code 4 | from vyper.exceptions import CallViolation, UnknownAttribute 5 | 6 | 7 | def test_call_deploy_from_external(make_input_bundle): 8 | lib1 = """ 9 | @deploy 10 | def __init__(): 11 | pass 12 | """ 13 | 14 | main = """ 15 | import lib1 16 | 17 | @external 18 | def foo(): 19 | lib1.__init__() 20 | """ 21 | 22 | input_bundle = make_input_bundle({"lib1.vy": lib1}) 23 | 24 | with pytest.raises(CallViolation) as e: 25 | compile_code(main, input_bundle=input_bundle) 26 | 27 | assert e.value.message == "Cannot call an @deploy function from an @external function!" 28 | 29 | 30 | @pytest.mark.parametrize("interface_syntax", ["__interface__", "__at__"]) 31 | def test_module_interface_init(make_input_bundle, tmp_path, interface_syntax): 32 | lib1 = """ 33 | #lib1.vy 34 | k: uint256 35 | 36 | @external 37 | def bar(): 38 | pass 39 | 40 | @deploy 41 | def __init__(): 42 | self.k = 10 43 | """ 44 | input_bundle = make_input_bundle({"lib1.vy": lib1}) 45 | 46 | code = f""" 47 | import lib1 48 | 49 | @deploy 50 | def __init__(): 51 | lib1.{interface_syntax}(self).__init__() 52 | """ 53 | 54 | with pytest.raises(UnknownAttribute) as e: 55 | compile_code(code, input_bundle=input_bundle) 56 | 57 | # as_posix() for windows tests 58 | lib1_path = (tmp_path / "lib1.vy").as_posix() 59 | assert e.value.message == f"interface {lib1_path} has no member '__init__'." 60 | -------------------------------------------------------------------------------- /vyper/venom/analysis/equivalent_vars.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.analysis import DFGAnalysis, IRAnalysis 2 | from vyper.venom.basicblock import IRVariable 3 | 4 | 5 | class VarEquivalenceAnalysis(IRAnalysis): 6 | """ 7 | Generate equivalence sets of variables. This is used to avoid swapping 8 | variables which are the same during venom_to_assembly. Theoretically, 9 | the DFTPass should order variable declarations optimally, but, it is 10 | not aware of the "pickaxe" heuristic in venom_to_assembly, so they can 11 | interfere. 12 | """ 13 | 14 | def analyze(self): 15 | dfg = self.analyses_cache.request_analysis(DFGAnalysis) 16 | 17 | equivalence_set: dict[IRVariable, int] = {} 18 | 19 | for bag, (var, inst) in enumerate(dfg._dfg_outputs.items()): 20 | if inst.opcode != "store": 21 | continue 22 | 23 | source = inst.operands[0] 24 | 25 | assert var not in equivalence_set # invariant 26 | if source in equivalence_set: 27 | equivalence_set[var] = equivalence_set[source] 28 | continue 29 | else: 30 | equivalence_set[var] = bag 31 | equivalence_set[source] = bag 32 | 33 | self._equivalence_set = equivalence_set 34 | 35 | def equivalent(self, var1, var2): 36 | if var1 not in self._equivalence_set: 37 | return False 38 | if var2 not in self._equivalence_set: 39 | return False 40 | return self._equivalence_set[var1] == self._equivalence_set[var2] 41 | -------------------------------------------------------------------------------- /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 |
-------------------------------------------------------------------------------- /tests/functional/syntax/test_string.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidLiteral, StructureException 5 | 6 | valid_list = [ 7 | """ 8 | @external 9 | def foo() -> String[10]: 10 | return "badminton" 11 | """, 12 | """ 13 | @external 14 | def foo() -> bool: 15 | x: String[15] = "tres bien!" 16 | y: String[15] = "test" 17 | return x != y 18 | """, 19 | """ 20 | @external 21 | def test() -> String[100]: 22 | return "hello world!" 23 | """, 24 | ] 25 | 26 | 27 | @pytest.mark.parametrize("good_code", valid_list) 28 | def test_string_success(good_code): 29 | assert compiler.compile_code(good_code) is not None 30 | 31 | 32 | invalid_list = [ 33 | ( 34 | """ 35 | @external 36 | def foo(): 37 | # invalid type annotation - should be String[N] 38 | a: String = "abc" 39 | """, 40 | StructureException, 41 | ), 42 | ( 43 | """ 44 | @external 45 | @view 46 | def compile_hash() -> bytes32: 47 | # GH issue #3088 - ord("è") == 232 48 | return keccak256("è") 49 | """, 50 | InvalidLiteral, 51 | ), 52 | ( 53 | """ 54 | @external 55 | def foo() -> bool: 56 | # ord("¡") == 161 57 | x: String[15] = "¡très bien!" 58 | y: String[12] = "test" 59 | return x != y 60 | """, 61 | InvalidLiteral, 62 | ), 63 | ] 64 | 65 | 66 | @pytest.mark.parametrize("bad_code,exc", invalid_list) 67 | def test_string_fail(get_contract, bad_code, exc): 68 | with pytest.raises(exc): 69 | compiler.compile_code(bad_code) 70 | -------------------------------------------------------------------------------- /vyper/venom/passes/remove_unused_variables.py: -------------------------------------------------------------------------------- 1 | from vyper.utils import OrderedSet, uniq 2 | from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis 3 | from vyper.venom.basicblock import IRInstruction 4 | from vyper.venom.passes.base_pass import IRPass 5 | 6 | 7 | class RemoveUnusedVariablesPass(IRPass): 8 | """ 9 | This pass removes instructions that produce output that is never used. 10 | """ 11 | 12 | dfg: DFGAnalysis 13 | work_list: OrderedSet[IRInstruction] 14 | 15 | def run_pass(self): 16 | self.dfg = self.analyses_cache.request_analysis(DFGAnalysis) 17 | 18 | work_list = OrderedSet() 19 | self.work_list = work_list 20 | 21 | uses = self.dfg.outputs.values() 22 | work_list.addmany(uses) 23 | 24 | while len(work_list) > 0: 25 | inst = work_list.pop() 26 | self._process_instruction(inst) 27 | 28 | self.analyses_cache.invalidate_analysis(LivenessAnalysis) 29 | self.analyses_cache.invalidate_analysis(DFGAnalysis) 30 | 31 | def _process_instruction(self, inst): 32 | if inst.output is None: 33 | return 34 | if inst.is_volatile or inst.is_bb_terminator: 35 | return 36 | uses = self.dfg.get_uses(inst.output) 37 | if len(uses) > 0: 38 | return 39 | 40 | for operand in uniq(inst.get_input_variables()): 41 | self.dfg.remove_use(operand, inst) 42 | new_uses = self.dfg.get_uses(operand) 43 | self.work_list.addmany(new_uses) 44 | 45 | inst.parent.remove_instruction(inst) 46 | -------------------------------------------------------------------------------- /examples/crowdfund.vy: -------------------------------------------------------------------------------- 1 | #pragma version >0.3.10 2 | 3 | ########################################################################### 4 | ## THIS IS EXAMPLE CODE, NOT MEANT TO BE USED IN PRODUCTION! CAVEAT EMPTOR! 5 | ########################################################################### 6 | 7 | # example of a crowd funding contract 8 | 9 | funders: HashMap[address, uint256] 10 | beneficiary: address 11 | deadline: public(uint256) 12 | goal: public(uint256) 13 | timelimit: public(uint256) 14 | 15 | # Setup global variables 16 | @deploy 17 | def __init__(_beneficiary: address, _goal: uint256, _timelimit: uint256): 18 | self.beneficiary = _beneficiary 19 | self.deadline = block.timestamp + _timelimit 20 | self.timelimit = _timelimit 21 | self.goal = _goal 22 | 23 | # Participate in this crowdfunding campaign 24 | @external 25 | @payable 26 | def participate(): 27 | assert block.timestamp < self.deadline, "deadline has expired" 28 | 29 | self.funders[msg.sender] += msg.value 30 | 31 | # Enough money was raised! Send funds to the beneficiary 32 | @external 33 | def finalize(): 34 | assert block.timestamp >= self.deadline, "deadline has not expired yet" 35 | assert self.balance >= self.goal, "goal has not been reached" 36 | 37 | selfdestruct(self.beneficiary) 38 | 39 | # Let participants withdraw their fund 40 | @external 41 | def refund(): 42 | assert block.timestamp >= self.deadline and self.balance < self.goal 43 | assert self.funders[msg.sender] > 0 44 | 45 | value: uint256 = self.funders[msg.sender] 46 | self.funders[msg.sender] = 0 47 | 48 | send(msg.sender, value) 49 | -------------------------------------------------------------------------------- /tests/functional/builtins/folding/test_len.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.utils import parse_and_fold 4 | 5 | 6 | @pytest.mark.parametrize("length", [0, 1, 32, 33, 64, 65, 1024]) 7 | def test_len_string(get_contract, length): 8 | source = """ 9 | @external 10 | def foo(a: String[1024]) -> uint256: 11 | return len(a) 12 | """ 13 | contract = get_contract(source) 14 | 15 | value = "a" * length 16 | 17 | vyper_ast = parse_and_fold(f"len('{value}')") 18 | old_node = vyper_ast.body[0].value 19 | new_node = old_node.get_folded_value() 20 | 21 | assert contract.foo(value) == new_node.value 22 | 23 | 24 | @pytest.mark.parametrize("length", [0, 1, 32, 33, 64, 65, 1024]) 25 | def test_len_bytes(get_contract, length): 26 | source = """ 27 | @external 28 | def foo(a: Bytes[1024]) -> uint256: 29 | return len(a) 30 | """ 31 | contract = get_contract(source) 32 | 33 | value = "a" * length 34 | 35 | vyper_ast = parse_and_fold(f"len(b'{value}')") 36 | old_node = vyper_ast.body[0].value 37 | new_node = old_node.get_folded_value() 38 | 39 | assert contract.foo(value.encode()) == new_node.value 40 | 41 | 42 | @pytest.mark.parametrize("length", [1, 32, 33, 64, 65, 1024]) 43 | def test_len_hex(get_contract, length): 44 | source = """ 45 | @external 46 | def foo(a: Bytes[1024]) -> uint256: 47 | return len(a) 48 | """ 49 | contract = get_contract(source) 50 | 51 | value = f"0x{'00' * length}" 52 | 53 | vyper_ast = parse_and_fold(f"len({value})") 54 | old_node = vyper_ast.body[0].value 55 | new_node = old_node.get_folded_value() 56 | 57 | assert contract.foo(value) == new_node.value 58 | -------------------------------------------------------------------------------- /tests/functional/builtins/codegen/test_uint2str.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | import pytest 4 | 5 | from vyper.compiler import compile_code 6 | from vyper.exceptions import InvalidType, OverflowException 7 | 8 | VALID_BITS = list(range(8, 257, 8)) 9 | 10 | 11 | @pytest.mark.parametrize("bits", VALID_BITS) 12 | def test_mkstr(get_contract, bits): 13 | n_digits = math.ceil(bits * math.log(2) / math.log(10)) 14 | code = f""" 15 | @external 16 | def foo(inp: uint{bits}) -> String[{n_digits}]: 17 | return uint2str(inp) 18 | """ 19 | 20 | c = get_contract(code) 21 | for i in [1, 2, 2**bits - 1, 0]: 22 | assert c.foo(i) == str(i), (i, c.foo(i)) 23 | 24 | 25 | # test for buffer overflow 26 | @pytest.mark.parametrize("bits", VALID_BITS) 27 | def test_mkstr_buffer(get_contract, bits): 28 | n_digits = math.ceil(bits * math.log(2) / math.log(10)) 29 | code = f""" 30 | some_string: String[{n_digits}] 31 | @internal 32 | def _foo(x: uint{bits}): 33 | self.some_string = uint2str(x) 34 | 35 | @external 36 | def foo(x: uint{bits}) -> uint256: 37 | y: uint256 = 0 38 | self._foo(x) 39 | return y 40 | """ 41 | c = get_contract(code) 42 | assert c.foo(2**bits - 1) == 0, bits 43 | 44 | 45 | def test_bignum_throws(): 46 | code = """ 47 | @external 48 | def test(): 49 | a: String[78] = uint2str(2**256) 50 | pass 51 | """ 52 | with pytest.raises(OverflowException): 53 | compile_code(code) 54 | 55 | 56 | def test_int_fails(): 57 | code = """ 58 | @external 59 | def test(): 60 | a: String[78] = uint2str(-1) 61 | pass 62 | """ 63 | with pytest.raises(InvalidType): 64 | compile_code(code) 65 | -------------------------------------------------------------------------------- /tests/functional/codegen/modules/test_events.py: -------------------------------------------------------------------------------- 1 | def test_module_event(get_contract, make_input_bundle, get_logs): 2 | # log from a module 3 | lib1 = """ 4 | event MyEvent: 5 | pass 6 | 7 | @internal 8 | def foo(): 9 | log MyEvent() 10 | """ 11 | main = """ 12 | import lib1 13 | 14 | @external 15 | def bar(): 16 | lib1.foo() 17 | """ 18 | input_bundle = make_input_bundle({"lib1.vy": lib1}) 19 | c = get_contract(main, input_bundle=input_bundle) 20 | c.bar() 21 | logs = get_logs(c, "MyEvent") 22 | assert len(logs) == 1 23 | 24 | 25 | def test_module_event2(get_contract, make_input_bundle, get_logs): 26 | # log a module event from main contract 27 | lib1 = """ 28 | event MyEvent: 29 | x: uint256 30 | """ 31 | main = """ 32 | import lib1 33 | 34 | @external 35 | def bar(): 36 | log lib1.MyEvent(5) 37 | """ 38 | input_bundle = make_input_bundle({"lib1.vy": lib1}) 39 | c = get_contract(main, input_bundle=input_bundle) 40 | c.bar() 41 | (log,) = get_logs(c, "MyEvent") 42 | assert log.args.x == 5 43 | 44 | 45 | def test_module_event_indexed(get_contract, make_input_bundle, get_logs): 46 | lib1 = """ 47 | event MyEvent: 48 | x: uint256 49 | y: indexed(uint256) 50 | 51 | @internal 52 | def foo(): 53 | log MyEvent(x=5, y=6) 54 | """ 55 | main = """ 56 | import lib1 57 | 58 | @external 59 | def bar(): 60 | lib1.foo() 61 | """ 62 | input_bundle = make_input_bundle({"lib1.vy": lib1}) 63 | c = get_contract(main, input_bundle=input_bundle) 64 | c.bar() 65 | (log,) = get_logs(c, "MyEvent") 66 | assert log.args.x == 5 67 | assert log.args.y == 6 68 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import decimal 3 | import os 4 | 5 | from vyper import ast as vy_ast 6 | from vyper.compiler.phases import CompilerData 7 | from vyper.semantics.analysis.constant_folding import constant_fold 8 | from vyper.utils import DECIMAL_EPSILON, round_towards_zero 9 | 10 | ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" 11 | 12 | 13 | @contextlib.contextmanager 14 | def working_directory(directory): 15 | tmp = os.getcwd() 16 | try: 17 | os.chdir(directory) 18 | yield 19 | finally: 20 | os.chdir(tmp) 21 | 22 | 23 | def parse_and_fold(source_code): 24 | ast = vy_ast.parse_to_ast(source_code) 25 | constant_fold(ast) 26 | return ast 27 | 28 | 29 | def decimal_to_int(*args): 30 | s = decimal.Decimal(*args) 31 | return round_towards_zero(s / DECIMAL_EPSILON) 32 | 33 | 34 | def check_precompile_asserts(source_code): 35 | # common sanity check for some tests, that calls to precompiles 36 | # are correctly wrapped in an assert. 37 | 38 | compiler_data = CompilerData(source_code) 39 | deploy_ir = compiler_data.ir_nodes 40 | runtime_ir = compiler_data.ir_runtime 41 | 42 | def _check(ir_node, parent=None): 43 | if ir_node.value == "staticcall": 44 | precompile_addr = ir_node.args[1] 45 | if isinstance(precompile_addr.value, int) and precompile_addr.value < 10: 46 | assert parent is not None and parent.value == "assert" 47 | for arg in ir_node.args: 48 | _check(arg, ir_node) 49 | 50 | _check(deploy_ir) 51 | # technically runtime_ir is contained in deploy_ir, but check it anyways. 52 | _check(runtime_ir) 53 | -------------------------------------------------------------------------------- /tests/functional/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 tests.utils import parse_and_fold 6 | from vyper.exceptions import TypeMismatch 7 | 8 | 9 | @pytest.mark.fuzzing 10 | @settings(max_examples=50) 11 | @given(a=st.integers(min_value=-(2**255) + 1, max_value=2**255 - 1)) 12 | @example(a=0) 13 | def test_abs(get_contract, a): 14 | source = """ 15 | @external 16 | def foo(a: int256) -> int256: 17 | return abs(a) 18 | """ 19 | contract = get_contract(source) 20 | 21 | vyper_ast = parse_and_fold(f"abs({a})") 22 | old_node = vyper_ast.body[0].value 23 | new_node = old_node.get_folded_value() 24 | 25 | assert contract.foo(a) == new_node.value == abs(a) 26 | 27 | 28 | @pytest.mark.fuzzing 29 | @settings(max_examples=50) 30 | @given(a=st.integers(min_value=2**255, max_value=2**256 - 1)) 31 | def test_abs_upper_bound_folding(get_contract, a): 32 | source = f""" 33 | @external 34 | def foo(a: int256) -> int256: 35 | return abs({a}) 36 | """ 37 | with pytest.raises(TypeMismatch): 38 | get_contract(source) 39 | 40 | 41 | def test_abs_lower_bound(get_contract, tx_failed): 42 | source = """ 43 | @external 44 | def foo(a: int256) -> int256: 45 | return abs(a) 46 | """ 47 | contract = get_contract(source) 48 | 49 | with tx_failed(): 50 | contract.foo(-(2**255)) 51 | 52 | 53 | def test_abs_lower_bound_folded(get_contract, tx_failed): 54 | source = """ 55 | @external 56 | def foo() -> int256: 57 | return abs(min_value(int256)) 58 | """ 59 | with pytest.raises(TypeMismatch): 60 | get_contract(source) 61 | -------------------------------------------------------------------------------- /tests/functional/builtins/codegen/test_blobhash.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import pytest 4 | 5 | from vyper.compiler import compile_code 6 | 7 | valid_list = [ 8 | """ 9 | @external 10 | @view 11 | def foo() -> bytes32: 12 | return blobhash(0) 13 | """, 14 | """ 15 | @external 16 | @view 17 | def foo() -> bytes32: 18 | a: bytes32 = 0x0000000000000000000000000000000000000000000000000000000000000005 19 | a = blobhash(2) 20 | return a 21 | """, 22 | """ 23 | @external 24 | @view 25 | def foo() -> bytes32: 26 | a: bytes32 = blobhash(0) 27 | assert a != empty(bytes32) 28 | return a 29 | """, 30 | """ 31 | @external 32 | @view 33 | def foo() -> bytes32: 34 | a: bytes32 = blobhash(1337) 35 | assert a == empty(bytes32) 36 | return a 37 | """, 38 | ] 39 | 40 | 41 | @pytest.mark.requires_evm_version("cancun") 42 | @pytest.mark.parametrize("good_code", valid_list) 43 | def test_blobhash_success(good_code): 44 | assert compile_code(good_code) is not None 45 | out = compile_code(good_code, output_formats=["opcodes_runtime"]) 46 | assembly = out["opcodes_runtime"].split(" ") 47 | assert "BLOBHASH" in assembly 48 | 49 | 50 | @pytest.mark.requires_evm_version("cancun") 51 | def test_get_blobhashes(env, get_contract, tx_failed): 52 | code = """ 53 | @external 54 | def get_blobhash(i: uint256) -> bytes32: 55 | return blobhash(i) 56 | """ 57 | c = get_contract(code) 58 | 59 | # mock the evm blobhash attribute 60 | env.blob_hashes = [random.randbytes(32) for _ in range(6)] 61 | 62 | for i in range(6): 63 | assert c.get_blobhash(i) == env.blob_hashes[i] 64 | 65 | assert c.get_blobhash(len(env.blob_hashes)) == b"\0" * 32 66 | -------------------------------------------------------------------------------- /tests/functional/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 ethereum.ercs import IERC20 56 | 57 | interface Factory: 58 | def getExchange(token_addr: address) -> address: view 59 | 60 | token: IERC20 61 | factory: Factory 62 | 63 | @external 64 | def setup(token_addr: address): 65 | self.token = IERC20(token_addr) 66 | assert staticcall 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/functional/syntax/test_nested_list.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import InvalidLiteral, 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 | TypeMismatch, # 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 | TypeMismatch, 32 | ), 33 | ( 34 | """ 35 | @external 36 | def foo() -> int128[2][2]: 37 | return [1,2] 38 | """, 39 | TypeMismatch, 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_success(good_code): 79 | assert compiler.compile_code(good_code) is not None 80 | -------------------------------------------------------------------------------- /tests/functional/codegen/calling_convention/test_self_call_struct.py: -------------------------------------------------------------------------------- 1 | from tests.utils import decimal_to_int 2 | 3 | 4 | def test_call_to_self_struct(env, 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_to_int("0.1")) == ( 28 | decimal_to_int("0.1"), 29 | env.timestamp, 30 | ) 31 | assert c.wrap_get_my_struct_BROKEN(decimal_to_int("0.1")) == ( 32 | decimal_to_int("0.1"), 33 | env.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_to_int("0.1")) == (decimal_to_int("0.1"),) 60 | assert c.wrap_get_my_struct_BROKEN(decimal_to_int("0.1")) == (decimal_to_int("0.1"),) 61 | -------------------------------------------------------------------------------- /tests/functional/codegen/modules/test_nonreentrant.py: -------------------------------------------------------------------------------- 1 | def test_export_nonreentrant(make_input_bundle, get_contract, tx_failed): 2 | lib1 = """ 3 | interface Foo: 4 | def foo() -> uint256: nonpayable 5 | 6 | implements: Foo 7 | 8 | @external 9 | @nonreentrant 10 | def foo() -> uint256: 11 | return 5 12 | """ 13 | main = """ 14 | import lib1 15 | 16 | initializes: lib1 17 | 18 | exports: lib1.foo 19 | 20 | @external 21 | @nonreentrant 22 | def re_enter(): 23 | extcall lib1.Foo(self).foo() # should always throw 24 | 25 | @external 26 | def __default__(): 27 | # sanity: make sure we don't revert due to bad selector 28 | pass 29 | """ 30 | 31 | input_bundle = make_input_bundle({"lib1.vy": lib1}) 32 | 33 | c = get_contract(main, input_bundle=input_bundle) 34 | assert c.foo() == 5 35 | with tx_failed(): 36 | c.re_enter() 37 | 38 | 39 | def test_internal_nonreentrant(make_input_bundle, get_contract, tx_failed): 40 | lib1 = """ 41 | interface Foo: 42 | def foo() -> uint256: nonpayable 43 | 44 | implements: Foo 45 | 46 | @external 47 | def foo() -> uint256: 48 | return self._safe_fn() 49 | 50 | @internal 51 | @nonreentrant 52 | def _safe_fn() -> uint256: 53 | return 10 54 | """ 55 | main = """ 56 | import lib1 57 | 58 | initializes: lib1 59 | 60 | exports: lib1.foo 61 | 62 | @external 63 | @nonreentrant 64 | def re_enter(): 65 | extcall lib1.Foo(self).foo() # should always throw 66 | 67 | @external 68 | def __default__(): 69 | # sanity: make sure we don't revert due to bad selector 70 | pass 71 | """ 72 | 73 | input_bundle = make_input_bundle({"lib1.vy": lib1}) 74 | 75 | c = get_contract(main, input_bundle=input_bundle) 76 | assert c.foo() == 10 77 | with tx_failed(): 78 | c.re_enter() 79 | -------------------------------------------------------------------------------- /tests/functional/syntax/test_slice.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper import compiler 4 | from vyper.exceptions import 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 | TypeMismatch, 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 | # test constant folding for `slice()` `length` argument 57 | """ 58 | @external 59 | def foo(): 60 | x: Bytes[32] = slice(msg.data, 0, 31 + 1) 61 | """, 62 | """ 63 | @external 64 | def foo(a: address): 65 | x: Bytes[32] = slice(a.code, 0, 31 + 1) 66 | """, 67 | """ 68 | @external 69 | def foo(inp: Bytes[5], start: uint256) -> Bytes[3]: 70 | return slice(inp, 0, 1 + 1) 71 | """, 72 | ] 73 | 74 | 75 | @pytest.mark.parametrize("good_code", valid_list) 76 | def test_slice_success(good_code): 77 | assert compiler.compile_code(good_code) is not None 78 | -------------------------------------------------------------------------------- /vyper/venom/passes/store_elimination.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis 2 | from vyper.venom.basicblock import IRVariable 3 | from vyper.venom.passes.base_pass import IRPass 4 | 5 | 6 | class StoreElimination(IRPass): 7 | """ 8 | This pass forwards variables to their uses though `store` instructions, 9 | and removes the `store` instruction. 10 | """ 11 | 12 | # TODO: consider renaming `store` instruction, since it is confusing 13 | # with LoadElimination 14 | 15 | def run_pass(self): 16 | self.dfg = self.analyses_cache.request_analysis(DFGAnalysis) 17 | 18 | for var, inst in self.dfg.outputs.items(): 19 | if inst.opcode != "store": 20 | continue 21 | self._process_store(inst, var, inst.operands[0]) 22 | 23 | self.analyses_cache.invalidate_analysis(LivenessAnalysis) 24 | self.analyses_cache.invalidate_analysis(DFGAnalysis) 25 | 26 | def _process_store(self, inst, var: IRVariable, new_var: IRVariable): 27 | """ 28 | Process store instruction. If the variable is only used by a load instruction, 29 | forward the variable to the load instruction. 30 | """ 31 | if any([inst.opcode == "phi" for inst in self.dfg.get_uses(new_var)]): 32 | return 33 | 34 | uses = self.dfg.get_uses(var) 35 | if any([inst.opcode == "phi" for inst in uses]): 36 | return 37 | for use_inst in uses.copy(): 38 | for i, operand in enumerate(use_inst.operands): 39 | if operand == var: 40 | use_inst.operands[i] = new_var 41 | 42 | self.dfg.add_use(new_var, use_inst) 43 | self.dfg.remove_use(var, use_inst) 44 | 45 | inst.parent.remove_instruction(inst) 46 | -------------------------------------------------------------------------------- /tests/functional/codegen/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 | -------------------------------------------------------------------------------- /vyper/venom/passes/lower_dload.py: -------------------------------------------------------------------------------- 1 | from vyper.utils import MemoryPositions 2 | from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis 3 | from vyper.venom.basicblock import IRBasicBlock, IRInstruction, IRLabel, IRLiteral 4 | from vyper.venom.passes.base_pass import IRPass 5 | 6 | 7 | class LowerDloadPass(IRPass): 8 | """ 9 | Lower dload and dloadbytes instructions 10 | """ 11 | 12 | def run_pass(self): 13 | for bb in self.function.get_basic_blocks(): 14 | self._handle_bb(bb) 15 | self.analyses_cache.invalidate_analysis(LivenessAnalysis) 16 | self.analyses_cache.invalidate_analysis(DFGAnalysis) 17 | 18 | def _handle_bb(self, bb: IRBasicBlock): 19 | fn = bb.parent 20 | for idx, inst in enumerate(bb.instructions): 21 | if inst.opcode == "dload": 22 | (ptr,) = inst.operands 23 | var = fn.get_next_variable() 24 | bb.insert_instruction( 25 | IRInstruction("add", [ptr, IRLabel("code_end")], output=var), index=idx 26 | ) 27 | idx += 1 28 | dst = IRLiteral(MemoryPositions.FREE_VAR_SPACE) 29 | bb.insert_instruction( 30 | IRInstruction("codecopy", [IRLiteral(32), var, dst]), index=idx 31 | ) 32 | 33 | inst.opcode = "mload" 34 | inst.operands = [dst] 35 | elif inst.opcode == "dloadbytes": 36 | _, src, _ = inst.operands 37 | code_ptr = fn.get_next_variable() 38 | bb.insert_instruction( 39 | IRInstruction("add", [src, IRLabel("code_end")], output=code_ptr), index=idx 40 | ) 41 | inst.opcode = "codecopy" 42 | inst.operands[1] = code_ptr 43 | -------------------------------------------------------------------------------- /tests/functional/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 tests.utils import decimal_to_int, parse_and_fold 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) 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 = parse_and_fold(f"as_wei_value({value:.10f}, '{denom}')") 34 | old_node = vyper_ast.body[0].value 35 | new_node = old_node.get_folded_value() 36 | 37 | assert isinstance(new_node.value, int) 38 | assert contract.foo(decimal_to_int(value)) == new_node.value 39 | 40 | 41 | @pytest.mark.fuzzing 42 | @settings(max_examples=10) 43 | @given(value=st.integers(min_value=0, max_value=2**128)) 44 | @pytest.mark.parametrize("denom", denoms) 45 | def test_integer(get_contract, value, denom): 46 | source = f""" 47 | @external 48 | def foo(a: uint256) -> uint256: 49 | return as_wei_value(a, '{denom}') 50 | """ 51 | contract = get_contract(source) 52 | 53 | vyper_ast = parse_and_fold(f"as_wei_value({value}, '{denom}')") 54 | old_node = vyper_ast.body[0].value 55 | new_node = old_node.get_folded_value() 56 | 57 | assert contract.foo(value) == new_node.value 58 | -------------------------------------------------------------------------------- /vyper/builtins/interfaces/IERC4626.vyi: -------------------------------------------------------------------------------- 1 | # Events 2 | event Deposit: 3 | sender: indexed(address) 4 | owner: indexed(address) 5 | assets: uint256 6 | shares: uint256 7 | 8 | event Withdraw: 9 | sender: indexed(address) 10 | receiver: indexed(address) 11 | owner: indexed(address) 12 | assets: uint256 13 | shares: uint256 14 | 15 | # Functions 16 | @view 17 | @external 18 | def asset() -> address: 19 | ... 20 | 21 | @view 22 | @external 23 | def totalAssets() -> uint256: 24 | ... 25 | 26 | @view 27 | @external 28 | def convertToShares(assetAmount: uint256) -> uint256: 29 | ... 30 | 31 | @view 32 | @external 33 | def convertToAssets(shareAmount: uint256) -> uint256: 34 | ... 35 | 36 | @view 37 | @external 38 | def maxDeposit(owner: address) -> uint256: 39 | ... 40 | 41 | @view 42 | @external 43 | def previewDeposit(assets: uint256) -> uint256: 44 | ... 45 | 46 | @external 47 | def deposit(assets: uint256, receiver: address) -> uint256: 48 | ... 49 | 50 | @view 51 | @external 52 | def maxMint(owner: address) -> uint256: 53 | ... 54 | 55 | @view 56 | @external 57 | def previewMint(shares: uint256) -> uint256: 58 | ... 59 | 60 | @external 61 | def mint(shares: uint256, receiver: address) -> uint256: 62 | ... 63 | 64 | @view 65 | @external 66 | def maxWithdraw(owner: address) -> uint256: 67 | ... 68 | 69 | @view 70 | @external 71 | def previewWithdraw(assets: uint256) -> uint256: 72 | ... 73 | 74 | @external 75 | def withdraw(assets: uint256, receiver: address, owner: address) -> uint256: 76 | ... 77 | 78 | @view 79 | @external 80 | def maxRedeem(owner: address) -> uint256: 81 | ... 82 | 83 | @view 84 | @external 85 | def previewRedeem(shares: uint256) -> uint256: 86 | ... 87 | 88 | @external 89 | def redeem(shares: uint256, receiver: address, owner: address) -> uint256: 90 | ... 91 | -------------------------------------------------------------------------------- /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 9 | 10 | 11 | def _check_byteslike(typ): 12 | if not isinstance(typ, _BytestringT) and typ != BYTES32_T: # pragma: nocover 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 bytes32 objects 25 | # TODO: Want to generalize to all bytes_M 26 | if to_hash.typ == BYTES32_T: 27 | return IRnode.from_list( 28 | [ 29 | "seq", 30 | ["mstore", MemoryPositions.FREE_VAR_SPACE, to_hash], 31 | ["sha3", MemoryPositions.FREE_VAR_SPACE, 32], 32 | ], 33 | typ=BYTES32_T, 34 | add_gas_estimate=_gas_bound(1), 35 | ) 36 | 37 | to_hash = ensure_in_memory(to_hash, context) 38 | 39 | with to_hash.cache_when_complex("buf") as (b1, to_hash): 40 | data = bytes_data_ptr(to_hash) 41 | len_ = get_bytearray_length(to_hash) 42 | return b1.resolve( 43 | IRnode.from_list( 44 | ["sha3", data, len_], 45 | typ=BYTES32_T, 46 | annotation="keccak256", 47 | add_gas_estimate=_gas_bound(ceil(to_hash.typ.maxlen / 32)), 48 | ) 49 | ) 50 | -------------------------------------------------------------------------------- /vyper/semantics/types/__init__.py: -------------------------------------------------------------------------------- 1 | from . import primitives, subscriptable, user 2 | from .base import TYPE_T, VOID_TYPE, KwargSettings, VyperType, is_type_t, map_void 3 | from .bytestrings import BytesT, StringT, _BytestringT 4 | from .function import ContractFunctionT, MemberFunctionT 5 | from .module import InterfaceT, ModuleT 6 | from .primitives import AddressT, BoolT, BytesM_T, DecimalT, IntegerT, SelfT 7 | from .subscriptable import DArrayT, HashMapT, SArrayT, TupleT 8 | from .user import EventT, FlagT, StructT 9 | 10 | 11 | def _get_primitive_types(): 12 | res = [BoolT(), DecimalT()] 13 | 14 | res.extend(IntegerT.all()) 15 | res.extend(BytesM_T.all()) 16 | 17 | # order of the types matters! 18 | # parsing of literal hex: prefer address over bytes20 19 | res.append(AddressT()) 20 | 21 | # note: since bytestrings are parametrizable, the *class* objects 22 | # are in the namespace instead of concrete type objects. 23 | res.extend([BytesT, StringT]) 24 | 25 | ret = {t._id: t for t in res} 26 | ret.update(_get_sequence_types()) 27 | 28 | return ret 29 | 30 | 31 | def _get_sequence_types(): 32 | # since these guys are parametrizable, the *class* objects 33 | # are in the namespace instead of concrete type objects. 34 | 35 | res = [HashMapT, DArrayT] 36 | 37 | ret = {t._id: t for t in res} 38 | 39 | # (static) arrays and tuples are special types which don't show up 40 | # in the type annotation itself. 41 | # since we don't have special handling of annotations in the parser, 42 | # break a dependency cycle by injecting these into the namespace with 43 | # mangled names (that no user can create). 44 | ret["$SArrayT"] = SArrayT 45 | ret["$TupleT"] = TupleT 46 | 47 | return ret 48 | 49 | 50 | # note: it might be good to make this a frozen dict of some sort 51 | PRIMITIVE_TYPES = _get_primitive_types() 52 | -------------------------------------------------------------------------------- /examples/factory/Factory.vy: -------------------------------------------------------------------------------- 1 | #pragma version >0.3.10 2 | 3 | from ethereum.ercs import IERC20 4 | 5 | interface Exchange: 6 | def token() -> IERC20: view 7 | def receive(_from: address, _amt: uint256): nonpayable 8 | def transfer(_to: address, _amt: uint256): nonpayable 9 | 10 | 11 | exchange_codehash: public(bytes32) 12 | # Maps token addresses to exchange addresses 13 | exchanges: public(HashMap[IERC20, Exchange]) 14 | 15 | 16 | @deploy 17 | def __init__(_exchange_codehash: bytes32): 18 | # Register the exchange code hash during deployment of the factory 19 | self.exchange_codehash = _exchange_codehash 20 | 21 | 22 | # NOTE: Could implement fancier upgrade logic around self.exchange_codehash 23 | # For example, allowing the deployer of this contract to change this 24 | # value allows them to use a new contract if the old one has an issue. 25 | # This would trigger a cascade effect across all exchanges that would 26 | # need to be handled appropriately. 27 | 28 | 29 | @external 30 | def register(): 31 | # Verify code hash is the exchange's code hash 32 | assert msg.sender.codehash == self.exchange_codehash 33 | # Save a lookup for the exchange 34 | # NOTE: Use exchange's token address because it should be globally unique 35 | # NOTE: Should do checks that it hasn't already been set, 36 | # which has to be rectified with any upgrade strategy. 37 | exchange: Exchange = Exchange(msg.sender) 38 | self.exchanges[staticcall exchange.token()] = exchange 39 | 40 | 41 | @external 42 | def trade(_token1: IERC20, _token2: IERC20, _amt: uint256): 43 | # Perform a straight exchange of token1 to token 2 (1:1 price) 44 | # NOTE: Any practical implementation would need to solve the price oracle problem 45 | extcall self.exchanges[_token1].receive(msg.sender, _amt) 46 | extcall self.exchanges[_token2].transfer(msg.sender, _amt) 47 | -------------------------------------------------------------------------------- /tests/functional/syntax/names/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/functional/syntax/exceptions/test_vyper_exception_pos.py: -------------------------------------------------------------------------------- 1 | from pytest import raises 2 | 3 | from vyper import compile_code 4 | from vyper.exceptions import SyntaxException, VyperException 5 | 6 | 7 | def test_type_exception_pos(): 8 | pos = (1, 2) 9 | 10 | with raises(VyperException) as e: 11 | raise VyperException("Fail!", pos) 12 | 13 | assert e.value.lineno == 1 14 | assert e.value.col_offset == 2 15 | assert str(e.value) == "line 1:2 Fail!" 16 | 17 | 18 | # multiple exceptions in file 19 | def test_multiple_exceptions(get_contract, assert_compile_failed): 20 | code = """ 21 | struct A: 22 | b: B # unknown type 23 | 24 | foo: immutable(uint256) 25 | bar: immutable(uint256) 26 | @deploy 27 | def __init__(): 28 | self.foo = 1 # SyntaxException 29 | self.bar = 2 # SyntaxException 30 | 31 | """ 32 | assert_compile_failed(lambda: get_contract(code), VyperException) 33 | 34 | 35 | def test_exception_contains_file(make_input_bundle): 36 | code = """ 37 | def bar()>: 38 | """ 39 | input_bundle = make_input_bundle({"code.vy": code}) 40 | with raises(SyntaxException, match="contract"): 41 | compile_code(code, input_bundle=input_bundle) 42 | 43 | 44 | def test_exception_reports_correct_file(make_input_bundle, chdir_tmp_path): 45 | code_a = "def bar()>:" 46 | code_b = "import A" 47 | input_bundle = make_input_bundle({"A.vy": code_a, "B.vy": code_b}) 48 | 49 | with raises(SyntaxException, match=r'contract "A\.vy:\d+"'): 50 | compile_code(code_b, input_bundle=input_bundle) 51 | 52 | 53 | def test_syntax_exception_reports_correct_offset(make_input_bundle): 54 | code = """ 55 | def foo(): 56 | uint256 a = pass 57 | """ 58 | input_bundle = make_input_bundle({"code.vy": code}) 59 | 60 | with raises(SyntaxException, match=r"line \d+:12"): 61 | compile_code(code, input_bundle=input_bundle) 62 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yaml: -------------------------------------------------------------------------------- 1 | # jobs to validate pull request well-formedness 2 | 3 | name: Validate PR metadata 4 | 5 | on: 6 | pull_request: 7 | types: 8 | - opened 9 | - edited 10 | - synchronize 11 | 12 | permissions: 13 | pull-requests: read 14 | 15 | jobs: 16 | validate-pr: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: amannn/action-semantic-pull-request@v5 20 | name: Run conventional commit checker 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | # https://github.com/amannn/action-semantic-pull-request?tab=readme-ov-file#configuration 24 | with: 25 | types: | 26 | feat 27 | perf 28 | fix 29 | chore 30 | refactor 31 | # ci: continuous integration 32 | # docs: documentation 33 | # test: test suite 34 | # lang: language changes 35 | # stdlib: changes to the stdlib 36 | # ux: language changes (UX) 37 | # parser: parser changes 38 | # tool: integration 39 | # ir: (old) IR/codegen changes 40 | # codegen: lowering from vyper AST to codegen 41 | # venom: venom changes 42 | scopes: | 43 | ci 44 | build 45 | docs 46 | test 47 | lang 48 | stdlib 49 | ux 50 | parser 51 | tool 52 | ir 53 | codegen 54 | venom 55 | requireScope: true 56 | subjectPattern: '^(?![A-Z]).+$' 57 | subjectPatternError: | 58 | Starts with uppercase letter: '{subject}' 59 | (Full PR title: '{title}') 60 | # type[scope]: subject 61 | # use [] instead of () for aesthetics 62 | headerPattern: '^(\w*)(?:\[([\w$.\-*/ ]*)\])?!?: (.*)$' 63 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/functional/examples/crowdfund/test_crowdfund_example.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | 4 | @pytest.fixture(scope="module") 5 | def c(env, get_contract): 6 | with open("examples/crowdfund.vy") as f: 7 | contract_code = f.read() 8 | return get_contract(contract_code, *[env.accounts[1], 50, 60]) 9 | 10 | 11 | def test_crowdfund_example(c, env): 12 | a0, a1, a2, a3, a4, a5, a6 = env.accounts[:7] 13 | env.set_balance(a0, 100) 14 | c.participate(value=5) 15 | env.timestamp += 1 # make sure auction has started 16 | 17 | assert c.timelimit() == 60 18 | assert c.deadline() - env.timestamp == 59 19 | assert env.timestamp < c.deadline() # expired 20 | assert env.get_balance(c.address) < c.goal() # not reached 21 | c.participate(value=49) 22 | # assert c.reached() 23 | pre_bal = env.get_balance(a1) 24 | env.timestamp += 100 25 | assert env.timestamp > c.deadline() # expired 26 | c.finalize() 27 | post_bal = env.get_balance(a1) 28 | assert post_bal - pre_bal == 54 29 | 30 | 31 | def test_crowdfund_example2(c, env, tx_failed): 32 | a0, a1, a2, a3, a4, a5, a6 = env.accounts[:7] 33 | for i, a in enumerate(env.accounts[3:7]): 34 | env.set_balance(a, i + 1) 35 | 36 | c.participate(value=1, sender=a3) 37 | c.participate(value=2, sender=a4) 38 | c.participate(value=3, sender=a5) 39 | c.participate(value=4, sender=a6) 40 | 41 | assert c.timelimit() == 60 42 | env.timestamp += 100 43 | # assert c.expired() 44 | # assert not c.reached() 45 | pre_bals = [env.get_balance(x) for x in [a3, a4, a5, a6]] 46 | with tx_failed(): 47 | c.refund(sender=a0) 48 | c.refund(sender=a3) 49 | with tx_failed(): 50 | c.refund(sender=a3) 51 | c.refund(sender=a4) 52 | c.refund(sender=a5) 53 | c.refund(sender=a6) 54 | post_bals = [env.get_balance(x) for x in [a3, a4, a5, a6]] 55 | assert [y - x for x, y in zip(pre_bals, post_bals)] == [1, 2, 3, 4] 56 | -------------------------------------------------------------------------------- /vyper/venom/passes/branch_optimization.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.analysis import CFGAnalysis, DFGAnalysis, LivenessAnalysis 2 | from vyper.venom.basicblock import IRInstruction 3 | from vyper.venom.passes.base_pass import IRPass 4 | 5 | 6 | class BranchOptimizationPass(IRPass): 7 | """ 8 | This pass optimizes branches inverting jnz instructions where appropriate 9 | """ 10 | 11 | def _optimize_branches(self) -> None: 12 | fn = self.function 13 | for bb in fn.get_basic_blocks(): 14 | term_inst = bb.instructions[-1] 15 | if term_inst.opcode != "jnz": 16 | continue 17 | 18 | fst, snd = bb.cfg_out 19 | 20 | fst_liveness = fst.instructions[0].liveness 21 | snd_liveness = snd.instructions[0].liveness 22 | 23 | cost_a, cost_b = len(fst_liveness), len(snd_liveness) 24 | 25 | cond = term_inst.operands[0] 26 | prev_inst = self.dfg.get_producing_instruction(cond) 27 | if cost_a >= cost_b and prev_inst.opcode == "iszero": 28 | new_cond = prev_inst.operands[0] 29 | term_inst.operands = [new_cond, term_inst.operands[2], term_inst.operands[1]] 30 | elif cost_a > cost_b: 31 | new_cond = fn.get_next_variable() 32 | inst = IRInstruction("iszero", [term_inst.operands[0]], output=new_cond) 33 | bb.insert_instruction(inst, index=-1) 34 | term_inst.operands = [new_cond, term_inst.operands[2], term_inst.operands[1]] 35 | 36 | def run_pass(self): 37 | self.liveness = self.analyses_cache.request_analysis(LivenessAnalysis) 38 | self.cfg = self.analyses_cache.request_analysis(CFGAnalysis) 39 | self.dfg = self.analyses_cache.request_analysis(DFGAnalysis) 40 | 41 | self._optimize_branches() 42 | 43 | self.analyses_cache.invalidate_analysis(LivenessAnalysis) 44 | self.analyses_cache.invalidate_analysis(CFGAnalysis) 45 | -------------------------------------------------------------------------------- /tests/venom_utils.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.basicblock import IRBasicBlock, IRInstruction 2 | from vyper.venom.context import IRContext 3 | from vyper.venom.function import IRFunction 4 | from vyper.venom.parser import parse_venom 5 | 6 | 7 | def parse_from_basic_block(source: str, funcname="_global"): 8 | """ 9 | Parse an IRContext from a basic block 10 | """ 11 | source = f"function {funcname} {{\n{source}\n}}" 12 | return parse_venom(source) 13 | 14 | 15 | def instructions_eq(i1: IRInstruction, i2: IRInstruction) -> bool: 16 | return i1.output == i2.output and i1.opcode == i2.opcode and i1.operands == i2.operands 17 | 18 | 19 | def assert_bb_eq(bb1: IRBasicBlock, bb2: IRBasicBlock): 20 | assert bb1.label.value == bb2.label.value 21 | for i1, i2 in zip(bb1.instructions, bb2.instructions): 22 | assert instructions_eq(i1, i2), (bb1, f"[{i1}] != [{i2}]") 23 | 24 | # assert after individual instruction checks, makes it easier to debug 25 | # if there is a difference. 26 | assert len(bb1.instructions) == len(bb2.instructions) 27 | 28 | 29 | def assert_fn_eq(fn1: IRFunction, fn2: IRFunction): 30 | assert fn1.name.value == fn2.name.value 31 | assert len(fn1._basic_block_dict) == len(fn2._basic_block_dict) 32 | 33 | for name1, bb1 in fn1._basic_block_dict.items(): 34 | assert name1 in fn2._basic_block_dict 35 | assert_bb_eq(bb1, fn2._basic_block_dict[name1]) 36 | 37 | # check function entry is the same 38 | assert fn1.entry.label == fn2.entry.label 39 | 40 | 41 | def assert_ctx_eq(ctx1: IRContext, ctx2: IRContext): 42 | for label1, fn1 in ctx1.functions.items(): 43 | assert label1 in ctx2.functions 44 | assert_fn_eq(fn1, ctx2.functions[label1]) 45 | assert len(ctx1.functions) == len(ctx2.functions) 46 | 47 | # check entry function is the same 48 | assert next(iter(ctx1.functions.keys())) == next(iter(ctx2.functions.keys())) 49 | assert ctx1.data_segment == ctx2.data_segment, ctx2.data_segment 50 | -------------------------------------------------------------------------------- /tests/unit/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/unit/ast/test_annotate_and_optimize_ast.py: -------------------------------------------------------------------------------- 1 | import ast as python_ast 2 | 3 | from vyper.ast.parse import PreParser, annotate_python_ast 4 | 5 | 6 | class AssertionVisitor(python_ast.NodeVisitor): 7 | def assert_about_node(self, node): 8 | raise AssertionError() 9 | 10 | def generic_visit(self, node): 11 | self.assert_about_node(node) 12 | 13 | super().generic_visit(node) 14 | 15 | 16 | TEST_CONTRACT_SOURCE_CODE = """ 17 | struct S: 18 | a: bool 19 | b: int128 20 | 21 | interface ERC20Contract: 22 | def name() -> String[64]: view 23 | 24 | @external 25 | def foo() -> int128: 26 | return -(-(-1)) 27 | """ 28 | 29 | 30 | def get_contract_info(source_code): 31 | pre_parser = PreParser() 32 | pre_parser.parse(source_code) 33 | py_ast = python_ast.parse(pre_parser.reformatted_code) 34 | 35 | annotate_python_ast(py_ast, pre_parser.reformatted_code, pre_parser) 36 | 37 | return py_ast, pre_parser.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.Constant) 67 | assert return_stmt.value.value == -1 68 | -------------------------------------------------------------------------------- /tests/unit/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 | -------------------------------------------------------------------------------- /vyper/venom/passes/load_elimination.py: -------------------------------------------------------------------------------- 1 | from vyper.venom.analysis import DFGAnalysis, LivenessAnalysis, VarEquivalenceAnalysis 2 | from vyper.venom.effects import Effects 3 | from vyper.venom.passes.base_pass import IRPass 4 | 5 | 6 | class LoadElimination(IRPass): 7 | """ 8 | Eliminate sloads, mloads and tloads 9 | """ 10 | 11 | # should this be renamed to EffectsElimination? 12 | 13 | def run_pass(self): 14 | self.equivalence = self.analyses_cache.request_analysis(VarEquivalenceAnalysis) 15 | 16 | for bb in self.function.get_basic_blocks(): 17 | self._process_bb(bb, Effects.MEMORY, "mload", "mstore") 18 | self._process_bb(bb, Effects.TRANSIENT, "tload", "tstore") 19 | self._process_bb(bb, Effects.STORAGE, "sload", "sstore") 20 | 21 | self.analyses_cache.invalidate_analysis(LivenessAnalysis) 22 | self.analyses_cache.invalidate_analysis(DFGAnalysis) 23 | 24 | def equivalent(self, op1, op2): 25 | return op1 == op2 or self.equivalence.equivalent(op1, op2) 26 | 27 | def _process_bb(self, bb, eff, load_opcode, store_opcode): 28 | # not really a lattice even though it is not really inter-basic block; 29 | # we may generalize in the future 30 | lattice = () 31 | 32 | for inst in bb.instructions: 33 | if eff in inst.get_write_effects(): 34 | lattice = () 35 | 36 | if inst.opcode == store_opcode: 37 | # mstore [val, ptr] 38 | val, ptr = inst.operands 39 | lattice = (ptr, val) 40 | 41 | if inst.opcode == load_opcode: 42 | prev_lattice = lattice 43 | (ptr,) = inst.operands 44 | lattice = (ptr, inst.output) 45 | if not prev_lattice: 46 | continue 47 | if not self.equivalent(ptr, prev_lattice[0]): 48 | continue 49 | inst.opcode = "store" 50 | inst.operands = [prev_lattice[1]] 51 | -------------------------------------------------------------------------------- /tests/functional/builtins/codegen/test_minmax_value.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from vyper.exceptions import OverflowException, TypeMismatch 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.int_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), TypeMismatch) 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), TypeMismatch) 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/functional/codegen/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(AttributeError): 67 | _ = int.storage_size_in_words 68 | -------------------------------------------------------------------------------- /tests/functional/builtins/codegen/test_method_id.py: -------------------------------------------------------------------------------- 1 | def test_method_id_test(get_contract): 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(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 | --------------------------------------------------------------------------------