Nonpayable default functions are sometimes payable in vyper
Description
Vyper is a pythonic Smart Contract Language for the ethereum virtual machine. In contracts with more than one regular nonpayable function, it is possible to send funds to the default function, even if the default function is marked nonpayable. This applies to contracts compiled with vyper versions prior to 0.3.8. This issue was fixed by the removal of the global calldatasize check in commit 02339dfda. Users are advised to upgrade to version 0.3.8. Users unable to upgrade should avoid use of nonpayable default functions.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Vyper <0.3.8, contracts with multiple nonpayable functions allow funds to be sent to a nonpayable default function, bypassing intended restrictions.
Vulnerability
Vyper versions prior to 0.3.8 contain a flaw in the global calldatasize check that allows funds to be sent to a nonpayable default function when a contract has more than one regular nonpayable function [1]. The check intended to prevent ether transfers to nonpayable functions was improperly bypassed.
Exploitation
An attacker can exploit this by sending a transaction with arbitrary calldata (or empty data) to the contract, triggering the default function even if it is marked nonpayable. The issue occurs because the compiler only validated the calldatasize against the number of regular functions, not the default function [4].
Impact
This allows unintended ether transfers to the contract, potentially locking funds or violating the contract's economic invariants. The vulnerability undermines the guarantee that nonpayable functions cannot receive ether.
Mitigation
The fix was implemented in commit 02339dfda and released in Vyper 0.3.8 [4]. Users should upgrade to this version. As a workaround, avoid using nonpayable default functions in contracts with multiple other nonpayable functions [1].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
vyperPyPI | < 0.3.8 | 0.3.8 |
Affected products
1Patches
2903727006c1eMerge pull request from GHSA-vxmm-cwh2-q762
1 file changed · +21 −0
tests/parser/features/decorators/test_payable.py+21 −0 modified@@ -372,3 +372,24 @@ def __default__(): assert_tx_failed( lambda: w3.eth.send_transaction({"to": c.address, "value": 100, "data": "0x12345678"}) ) + + +def test_batch_nonpayable(get_contract, w3, assert_tx_failed): + code = """ +@external +def foo() -> bool: + return True + +@external +def __default__(): + pass + """ + + c = get_contract(code) + w3.eth.send_transaction({"to": c.address, "value": 0, "data": "0x12345678"}) + data = bytes([1, 2, 3, 4]) + for i in range(5): + calldata = "0x" + data[:i].hex() + assert_tx_failed( + lambda: w3.eth.send_transaction({"to": c.address, "value": 100, "data": calldata}) + )
02339dfda0f3refactor: optimize calldatasize check (#3104)
3 files changed · +24 −9
tests/parser/features/test_init.py+6 −5 modified@@ -15,12 +15,13 @@ def __init__(a: uint256): assert c.val() == 123 # Make sure the init code does not access calldata - opcodes = vyper.compile_code(code, ["opcodes"])["opcodes"].split(" ") - ir_return_idx = opcodes.index("JUMP") + assembly = vyper.compile_code(code, ["asm"])["asm"].split(" ") + ir_return_idx_start = assembly.index("{") + ir_return_idx_end = assembly.index("}") - assert "CALLDATALOAD" in opcodes - assert "CALLDATACOPY" not in opcodes[:ir_return_idx] - assert "CALLDATALOAD" not in opcodes[:ir_return_idx] + assert "CALLDATALOAD" in assembly + assert "CALLDATACOPY" not in assembly[:ir_return_idx_start] + assembly[ir_return_idx_end:] + assert "CALLDATALOAD" not in assembly[:ir_return_idx_start] + assembly[ir_return_idx_end:] def test_init_calls_internal(get_contract, assert_compile_failed, assert_tx_failed):
vyper/codegen/function_definitions/external_function.py+18 −1 modified@@ -123,7 +123,24 @@ def handler_for(calldata_kwargs, default_kwargs): ret.append(["goto", sig.external_function_base_entry_label]) - ret = ["if", ["eq", "_calldata_method_id", method_id], ret] + method_id_check = ["eq", "_calldata_method_id", method_id] + + # if there is a function whose selector is 0, it won't be distinguished + # from the case where nil calldata is supplied, b/c calldataload loads + # 0s past the end of physical calldata (cf. yellow paper). + # since supplying 0 calldata is expected to trigger the fallback fn, + # we check that calldatasize > 0, which distinguishes the 0 selector + # from the fallback function "selector" + # (equiv. to "all selectors not in the selector table"). + + # note: cases where not enough calldata is supplied (besides + # calldatasize==0) are not addressed here b/c a calldatasize + # well-formedness check is already present in the function body + # as part of abi validation + if method_id.value == 0: + method_id_check = ["and", ["gt", "calldatasize", 0], method_id_check] + + ret = ["if", method_id_check, ret] return ret ret = ["seq"]
vyper/codegen/module.py+0 −3 modified@@ -120,9 +120,6 @@ def _runtime_ir(runtime_functions, all_sigs, global_ctx): runtime = [ "seq", - # check that calldatasize is at least 4, otherwise - # calldataload will load zeros (cf. yellow paper). - ["if", ["lt", "calldatasize", 4], ["goto", "fallback"]], ["with", "_calldata_method_id", shr(224, ["calldataload", 0]), selector_section], close_selector_section, ["label", "fallback", ["var_list"], fallback_ir],
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-vxmm-cwh2-q762ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2023-32675ghsaADVISORY
- github.com/pypa/advisory-database/tree/main/vulns/vyper/PYSEC-2023-80.yamlghsaWEB
- github.com/vyperlang/vyper/commit/02339dfda0f3caabad142060d511d10bfe93c520ghsax_refsource_MISCWEB
- github.com/vyperlang/vyper/commit/02339dfda0f3caabad142060d511d10bfe93c520.ghsaWEB
- github.com/vyperlang/vyper/commit/903727006c1e5ebef99fa9fd5d51d62bd33d72a9ghsaWEB
- github.com/vyperlang/vyper/security/advisories/GHSA-vxmm-cwh2-q762ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.