← Back to list

Ethereum Calldata and Bytecode: How the EVM Knows Which Function to Call

When you send a transaction to a smart contract, you’re not sending “commands” in plain text. You’re sending a precise sequence of bytes…

Andrey Obruchkov · 2025-11-08 15:03 · 2 claps · 3.8 min read
#blockchain #evm #calldata #bytecode #protocol
Open on Medium ↗
Wiki topics: CRY · Crypto & Web3 🥊 · Combat Sports

Ethereum Calldata and Bytecode: How the EVM Knows Which Function to Call

When you send a transaction to a smart contract, you’re not sending “commands” in plain text. You’re sending a precise sequence of bytes calldata, that tells the EVM exactly which function to execute and with what arguments.

Every function call you make through ethers.js, Foundry, or MetaMask is ultimately transformed into this calldata. It starts with a 4-byte function selector (derived from the function signature) followed by the ABI-encoded arguments.

In this post, we’ll break down calldata step by step and you will learn:

1. How the EVM determines which function to call

2. How the 4-byte selector is calculated

3. How arguments are encoded in 32-byte slots

4. What does the contract bytecode look Like

5. What happens inside the EVM when that calldata is executed

By the end, you’ll be able to read a transaction’s data field and understand exactly what it’s doing no black box, just bytes.

Collaboration

I’m currently open to collaborations and development projects across blockchain, smart contracts, and full-stack systems, feel free to connect on LinkedIn if you’re building something interesting

What is Calldata

Afrer we understood the stack of the EVM (from previous post) lets dive into calldata is a read-only data location in the EVM that holds the input arguments for external function calls. It’s passed with the transaction and cannot be modified during execution.

When a transaction is sent to a smart contract, the **data field** in the transaction tells the contract what function to run and with what arguments.

The first 4 bytes of the data field are known as the function selector. This value tells the EVM which function in the contract to call. (you can search famous selectors in 4byte-directory)

How the EVM determines which function to call

The function selector is the first 4 bytes of the Keccak-256 hash of the function signature.

Example:

function set(uint256 x)
  • Function signature (as a string): "set(uint256)"
  • Hash: keccak256("set(uint256)")0x60fe47b1...
  • First 4 bytes (8 hex chars): 0x60fe47b1 → This is the function selector

So if you see 0x60fe47b1 at the start of calldata, you know it’s a call to set(uint256).

Example Transaction Calldata:

Let’s say you call this function with the value 69420.

set(69420)

The calldata would look like:

0x60fe47b1
0000000000000000000000000000000000000000000000000000000000010f2c

Breakdown:

  • 0x60fe47b1 → Function selector (set(uint256))
  • The next 32 bytes: 0x...010f2c69420, encoded as a padded uint256

General Calldata Layout:

For any function call:

<4 bytes>    Function selector (first 4 bytes of keccak256)
<32 bytes>   Argument 1 (padded)
<32 bytes>   Argument 2 (padded)
...

Gotchas to Know

  • The selector must match exactly or the call will revert with fallback() or receive().
  • Solidity uses ABI encoding, which is standardized — multiple tools (e.g., ethers.js, Foundry) can decode/encode it.
  • When interacting with contracts manually (e.g. via eth_sendTransaction), you must build this calldata yourself.

Example in Foundry

cast calldata "set(uint256)" 69420
# Output:
# 0x60fe47b1000000000000000000000000000000000000000000000000000000010f2c

Example: from source code to bytecode

When you write a smart contract in Solidity, what you’re really creating is a high-level blueprint that the Ethereum Virtual Machine (EVM) will eventually execute as low-level instructions called opcodes.

Let’s walk through how Solidity code is compiled and how the EVM processes it.

pragma solidity >=0.4.16 <0.9.0;

contract MiniExample {
    uint data;
    function set(uint x) public {
        data = x;
    }
    function get() public view returns (uint) {
        return data;
    }
}

This is easy to read, but the EVM doesn’t understand Solidity. It needs bytecode, which is generated by the Solidity compiler (solc). The compilation process produces:

  • A .bin file containing the raw bytecode
  • An ABI file that defines the interface for interacting with the contract

If the contract has a constructor, that logic is bundled into deployment bytecode, which runs only once. After deployment, what’s stored on-chain is called the runtime bytecode.

What Does the Bytecode Look Like?

The compiled bytecode of the contract above starts like this

6080604052348015600e575f5ffd5b506101298061001c5f395ff3fe6080604052348015600e575f5ffd5b50600436106030575f3560e01c806360fe47b11460345780636d4ce63c14604c575b5f5ffd5b604a60048036038101906046919060a9565b6066565b005b6052606f565b604051605d919060dc565b60405180910390f35b805f8190555050565b5f5f54905090565b5f5ffd5b5f819050919050565b608b81607b565b81146094575f5ffd5b50565b5f8135905060a3816084565b92915050565b5f6020828403121560bb5760ba6077565b5b5f60c6848285016097565b91505092915050565b60d681607b565b82525050565b5f60208201905060ed5f83018460cf565b9291505056fea2646970667358221220ec163686bf86159ebb242a8ca38f68fe4e9bf9be12def4ec8af94737310b0c6364736f6c634300081e0033

This hex string is a direct representation of the contract’s logic. The EVM interprets it as a list of opcodes. For example:

  • 60PUSH1
  • 80 → pushes the value 0x80 onto the stack
  • 52MSTORE (store value in memory)

So the first few operations are:

[00]    PUSH1   80 // Push 1-byte of value 80 on the stack
[02]    PUSH1   40 // Push 1-byte of value 40 on the stack
[04]    MSTORE   // Memory store
[05]    CALLVALUE   // Get deposited value from call
[06]    DUP1    
[07]    ISZERO  // A conditional opcode 
[08]    PUSH1   0xR // Push 2-bytes
[0b]    JUMPI   // Jump to another location on the stack
...
...
[138]

Each opcode is a simple instruction executed by the EVM, and the Program Counter (PC) steps through the list one by one.

What Happens When You Send a Transaction:

A smart contract call is made through a transaction, which includes fields like:

{
  "to": "0x8a19ba...",
  "from": "0xf9db21...",
  "value": "0x0",
  "gasPrice": 700000,
  "gasLimit": 210000,
  "data": "0x60fe47b10000000000000000000000000000000000000000000000000000000000010f2c"
}

Here’s what happens inside the EVM:

  • The transaction is received and decoded.
  • The signature is verified using v, r, and s values.
  • The EVM sets up an isolated environment with a new stack and memory context.
  • It steps through the bytecode instruction-by-instruction, updating the stack, memory, and possibly storage.
  • If the execution completes successfully, it returns results or updates the state. If not, it reverts.

How Calldata Works in This Flow:

Let’s look at the data field from the transaction:

0x60fe47b10000000000000000000000000000000000000000000000000000000000010f2c
  • The first 4 bytes: 0x60fe47b1 → This is the function selector, a hash of set(uint256)
  • The remaining 32 bytes: → Encoded input: 0x...010f2c, which equals 69420 in decimal

This data tells the EVM: “Call the set function with the value 69420.” Inside the contract's bytecode, the function selector is matched, and execution begins at the correct code segment.

Summary

Understanding calldata gives you x-ray vision into what really happens when you call a contract. Whether you’re debugging a failed transaction or building custom tools, this knowledge turns raw hex into readable logic.


메타데이터
post_id
94f54fc28830
slug
ethereum-calldata-and-bytecode-how-the-evm-knows-which-function-to-call-94f54fc28830
url
https://medium.com/@andrey_obruchkov/ethereum-calldata-and-bytecode-how-the-evm-knows-which-function-to-call-94f54fc28830
canonical_url
https://medium.com/@andrey_obruchkov/ethereum-calldata-and-bytecode-how-the-evm-knows-which-function-to-call-94f54fc28830
author_url
https://medium.com/@andrey_obruchkov
status
ok
fetched_at
2026-08-06 05:40:07