VYPR
Moderate severityNVD Advisory· Published Jan 30, 2024· Updated Jun 16, 2025

raw_call `value=` kwargs not disabled for static and delegate calls

CVE-2024-24567

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.

PackageAffected versionsPatched versions
vyperPyPI
< 0.4.00.4.0

Affected products

1

Patches

1
a2df08888c31

fix: disallow `value=` passing for delegate and static raw_calls (#3755)

https://github.com/vyperlang/vyperCharles CooperJan 31, 2024via ghsa
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

News mentions

0

No linked articles in our index yet.