Skip to main content
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.

Overview

Opcode: 0x3b Introduced: Frontier (EVM genesis) EXTCODESIZE returns the size in bytes of the code stored at a given address.

Specification

Stack Input:
address (uint160 as uint256)
Stack Output:
codeSize (uint256, in bytes)
Gas Cost: Variable (hardfork-dependent)
  • Frontier: 20 gas
  • Tangerine Whistle (EIP-150): 700 gas
  • Berlin (EIP-2929): 2600 gas (cold) / 100 gas (warm)

Behavior

Returns the bytecode length of the account at the given address. Returns 0 for:
  • EOAs (externally owned accounts)
  • Non-existent accounts
  • Contracts during construction (before constructor completes)

Examples

Basic Usage

import { extcodesize } from '@tevm/voltaire/evm/context';

const contractAddr = Address('0x...');
const frame = createFrame({ stack: [BigInt(contractAddr)] });

const err = extcodesize(frame, host);

console.log(frame.stack[0]); // Code size in bytes

Contract Detection

function isContract(address account) public view returns (bool) {
    uint256 size;
    assembly {
        size := extcodesize(account)
    }
    return size > 0;
}

Constructor Bypass

// INSUFFICIENT: Can be bypassed during construction
modifier onlyEOA() {
    uint256 size;
    assembly { size := extcodesize(caller()) }
    require(size == 0, "Contracts not allowed");
    _;
}

// Attack: Call from constructor where extcodesize returns 0

Gas Cost

Historical evolution:
HardforkColdWarm
Frontier20-
Tangerine Whistle700-
Berlin2600100

Common Usage

Proxy Implementation Check

function verifyImplementation(address impl) internal view {
    uint256 size;
    assembly { size := extcodesize(impl) }
    require(size > 0, "Implementation must be contract");
}

Security

Constructor Bypass

During construction, EXTCODESIZE returns 0:
contract Vulnerable {
    modifier noContracts() {
        uint256 size;
        assembly { size := extcodesize(caller()) }
        require(size == 0);
        _;
    }

    function restricted() public noContracts {
        // Attacker can call from constructor!
    }
}

contract Attacker {
    constructor(Vulnerable v) {
        v.restricted(); // extcodesize(this) == 0 in constructor!
    }
}

Better Pattern

Use tx.origin check or whitelist:
modifier onlyEOA() {
    require(msg.sender == tx.origin, "Only EOA");
    _;
}

Implementation

export function extcodesize(
  frame: FrameType,
  host: BrandedHost
): EvmError | null {
  const addrResult = popStack(frame);
  if (addrResult.error) return addrResult.error;

  const addr = fromNumber(addrResult.value);

  const gasErr = consumeGas(frame, 700n); // Simplified
  if (gasErr) return gasErr;

  const code = host.getCode(addr);
  const pushErr = pushStack(frame, BigInt(code.length));
  if (pushErr) return pushErr;

  frame.pc += 1;
  return null;
}

References