raw_call `value=` kwargs not disabled for static and delegate calls
Description
Vyper is a pythonic Smart Contract Language for the ethereum virtual machine. Vyper compiler allows passing a value in builtin raw_call even if the call is a delegatecall or a staticcall. But in the context of delegatecall and staticcall the handling of value is not possible due to the semantics of the respective opcodes, and vyper will silently ignore the value= argument. If the semantics of the EVM are unknown to the developer, he could suspect that by specifying the value kwarg, exactly the given amount will be sent along to the target. This vulnerability affects 0.3.10 and earlier versions.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Vyper compiler 0.3.10 and earlier fails to enforce that `value` cannot be passed in `raw_call` when `is_delegate_call=True` or `is_static_call=True`, silently ignoring the ether value instead of reverting or warning.
Vulnerability
Description
CVE-2024-24567 affects the Vyper smart contract compiler, a Pythonic language for the Ethereum Virtual Machine (EVM). The vulnerability lies in the raw_call built-in function, which is used to make low-level calls. The compiler accepts a value keyword argument even when the call is specified as a delegatecall or staticcall via the is_delegate_call=True or is_static_call=True flags. Due to the semantics of the respective EVM opcodes (DELEGATECALL and STATICCALL), passing ether is not possible, and Vyper silently ignores the value= argument instead of rejecting the code at compile time or runtime [1][3].
Exploitation
Scenario
While no vulnerable contracts were found in production during a search, the impact is primarily on developers who may not fully grasp the distinction between EVM call types. A developer might write a multicall contract that attempts to forward ether using raw_call(self, call_data, is_delegate_call=True, value=msg.value). In such a scenario, the developer would expect the specified amount of ether to be transferred, but the value is ignored because delegate calls do not change the call value. This could lead to a silent loss of intended ether transfer, potentially allowing an attacker to exploit the misalignment of accounting (e.g., in a multicall pattern that tracks accumulated msg.value) [3].
Impact
The core impact is that ether intended to be sent with a delegatecall or staticcall is never actually transferred. This can break contract invariants, especially in systems that perform balance checks or value accounting internal to the calling contract. The bug is a violation of the principle of least surprise—a developer relying on the value argument in these contexts will produce contracts that behave differently than intended, potentially leading to financial loss or incorrect protocol state [1][3].
Mitigation
The issue is fixed in Vyper version 0.3.10 (the fix was included shortly after the advisory). Developers are advised to upgrade the compiler to a version beyond 0.3.10. For existing contracts compiled with vulnerable versions, they should be reviewed and redeployed with a patched compiler. The GitHub advisory (GHSA-x2c2-q32w-4w6m) provides the detailed analysis and links to the commit that introduced the validation [3]. No workaround exists other than ensuring the value keyword is not used in combination with is_delegate_call or is_static_call [2][4].
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.4.0 | 0.4.0 |
Affected products
1Patches
1a2df08888c31fix: disallow `value=` passing for delegate and static raw_calls (#3755)
2 files changed · +22 −3
tests/functional/builtins/codegen/test_raw_call.py+16 −0 modified@@ -608,6 +608,22 @@ def foo(_addr: address): ( """ @external +def foo(_addr: address): + raw_call(_addr, method_id("foo()"), is_delegate_call=True, value=1) + """, + ArgumentException, + ), + ( + """ +@external +def foo(_addr: address): + raw_call(_addr, method_id("foo()"), is_static_call=True, value=1) + """, + ArgumentException, + ), + ( + """ +@external @view def foo(_addr: address): raw_call(_addr, 256)
vyper/builtins/functions.py+6 −3 modified@@ -1114,13 +1114,16 @@ def build_IR(self, expr, args, kwargs, context): if delegate_call and static_call: raise ArgumentException( - "Call may use one of `is_delegate_call` or `is_static_call`, not both", expr + "Call may use one of `is_delegate_call` or `is_static_call`, not both" ) + + if (delegate_call or static_call) and value.value != 0: + raise ArgumentException("value= may not be passed for static or delegate calls!") + if not static_call and context.is_constant(): raise StateAccessViolation( f"Cannot make modifying calls from {context.pp_constancy()}," - " use `is_static_call=True` to perform this action", - expr, + " use `is_static_call=True` to perform this action" ) if data.value == "~calldata":
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-x2c2-q32w-4w6mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-24567ghsaADVISORY
- github.com/pypa/advisory-database/tree/main/vulns/vyper/PYSEC-2024-151.yamlghsaWEB
- github.com/vyperlang/vyper/blob/9136169468f317a53b4e7448389aa315f90b95ba/vyper/builtins/functions.pyghsax_refsource_MISCWEB
- github.com/vyperlang/vyper/commit/a2df08888c318713742c57f71465f32a1c27ed72ghsaWEB
- github.com/vyperlang/vyper/pull/3755ghsaWEB
- github.com/vyperlang/vyper/security/advisories/GHSA-x2c2-q32w-4w6mghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.