Skip to content
Last updated: February 06, 2026

SCWE-134: Low-Level Call to Non-Contract Address

Stable Version v1.0

This content is in the version-(v1.0) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Relationships

Description

When using low-level call, staticcall, or delegatecall to a user-supplied or dynamic address, the target may be an EOA (externally owned account) with no contract code. In that case, the call returns success = true and empty return data, but no code executes. Contracts that assume the target performed logic (e.g., a callback, validation, or state change) can proceed under false assumptions, leading to bypassed checks, unexecuted logic, or incorrect accounting.

Remediation

  • Verify that the target address contains code using extcodesize (or address.code.length in Solidity 0.8.12+) before low-level calls.
  • Use allowlists for callable targets instead of accepting arbitrary addresses.
  • Prefer high-level interface calls when the target is known to be a contract.

Examples

Vulnerable

pragma solidity ^0.8.0;

contract PluginRegistry {
    function executeViaPlugin(address plugin, bytes calldata data) external {
        (bool ok, ) = plugin.call(data);
        require(ok, "plugin failed");
        // If plugin is an EOA, call "succeeds" but no code ran — logic may be bypassed
    }
}

Fixed

pragma solidity ^0.8.0;

contract PluginRegistry {
    function executeViaPlugin(address plugin, bytes calldata data) external {
        require(plugin.code.length > 0, "target has no code");
        (bool ok, ) = plugin.call(data);
        require(ok, "plugin failed");
    }
}