eth_getCode: Read the Bytecode at an Address

eth_getCodeRead methodEthereum JSON-RPC

eth_getCode returns the bytecode deployed at an address, or 0x when there is none. It is the standard way to answer "is this a contract?", the first thing to check when a call mysteriously returns empty data, and since the Pectra fork it is also how you discover that an ordinary wallet has delegated its behaviour to a contract.

1 credit
per call on BLAZED.sh
Read
call type
2
parameters
All clients
client support

Try eth_getCode

Request as curl
curl -s https://ethereum-rpc.publicnode.com \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "eth_getCode",
  "params": [
    "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
    "latest"
  ]
}'

Runs the example straight from your browser against the endpoint above. The prefilled URL is a third-party public endpoint, not run by BLAZED.sh.

What eth_getCode does

What you get back is runtime bytecode: the code the EVM executes when the address is called. It is not the creation bytecode you sent in the deployment transaction, and it is certainly not Solidity source. Verified source lives with explorers, not on chain; from the node's point of view a contract is this byte string plus its storage.

The most valuable thing the method tells you is presence. Calling a function on an address with no code does not fail, it succeeds and returns nothing, so a typo in an address or a contract that was never deployed on this network presents as an empty result rather than an error. Checking eth_getCode first turns that silent failure into a clear one.

Two modern wrinkles change the reading of the result. Most production contracts are proxies, so the bytecode you see is a short delegating stub and the logic lives at an implementation address held in a storage slot. And EIP-7702, live since Pectra, lets an externally owned account point at contract code: such an account returns exactly 23 bytes, 0xef0100 followed by the 20-byte address it delegates to. Code at an address no longer implies the address is a contract.

Parameters

#NameTypeDescription
1addressstring20-byte address whose code you want to read.
2blockParameterstringHex block number or one of latest, pending, safe, finalized, earliest. Code is state, so historical reads need the node to still hold that block's state.

What it returns

A hex string holding the runtime bytecode, or 0x when the address has none. A contract's code is immutable once deployed, so for a given address and block the answer never changes; what changes across blocks is whether code is there at all.

0x covers several situations: an externally owned account, an address where nothing has been deployed yet (including a CREATE2 address computed in advance), and a contract that self-destructed back when that was possible. Since EIP-6780, selfdestruct only clears code when it runs in the same transaction that created the contract, so newer contracts effectively keep their code forever.

Example response

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": "0x60806040526004361061006d576000357c010000...633659cfe6...5c60da1b...0029"
}

Heavily truncated: the real answer is 2,186 bytes of hex on one line. This is USDC's proxy, and the selectors in the dispatch table give it away (3659cfe6 is upgradeTo, 5c60da1b is implementation). The token's logic lives at 0x43506849D7C04F9138D1A2050bbF3A0c054402dd, which you find by reading the proxy's implementation storage slot with eth_getStorageAt.

eth_getCode with ethers.js

import { JsonRpcProvider } from "ethers";

const provider = new JsonRpcProvider("https://ethereum-rpc.publicnode.com");

async function classify(address) {
  const code = await provider.getCode(address);
  if (code === "0x") return "EOA or nothing deployed";
  // EIP-7702: 0xef0100 || 20-byte target, 23 bytes total
  if (code.startsWith("0xef0100") && code.length === 2 + 46)
    return `EOA delegated to 0x${code.slice(8)}`;
  return `contract, ${(code.length - 2) / 2} bytes`;
}

console.log(await classify("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"));
console.log(await classify("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"));

Gotchas and common errors

Code no longer means contract

Since EIP-7702 shipped with Pectra, a normal wallet can set a delegation indicator: 23 bytes of 0xef0100 followed by an address. Anything that treats a non-empty eth_getCode result as "this is a contract, refuse the withdrawal" or "this is a contract, skip the EOA path" now misclassifies delegated accounts. Check for the 0xef0100 prefix explicitly before deciding.

You are reading the proxy, not the logic

For any upgradeable contract the bytecode is a thin delegator, and the code you actually care about sits at the implementation address. EIP-1967 standardises where that address is stored, in the slot keccak256("eip1967.proxy.implementation") - 1, so eth_getStorageAt on that slot gets you the target. Proxies older than the standard keep it elsewhere: USDC, for instance, stores its implementation at keccak256("org.zeppelinos.proxy.implementation"), and reading the EIP-1967 slot on it returns zero. Comparing proxy bytecode across two addresses tells you they share a proxy pattern, not that they behave alike.

Runtime code is not creation code

The deployment transaction's input contains constructor logic and arguments that never make it into the deployed code, so runtime bytecode alone will not let you redeploy an identical contract, and constructor arguments have to be recovered from the creation transaction instead. Verification tools compare a compiler's runtime output against this result, which is why an unverified contract is still perfectly readable at the bytecode level.

0x is the answer to several different questions

An empty result means no code at this address in this block: a plain wallet, a wrong address, the right address on the wrong network, a CREATE2 address that has not been deployed yet, or a contract deployed after the block you queried. When a call returns 0x unexpectedly, check the code first and the ABI second.

Old blocks need state

Code lives in state, so asking what a proxy pointed at last year is subject to the same pruning limits as any historical state read and fails with "missing trie node" on a node that no longer holds it. Tracking an upgrade timeline is an archive workload, or a job for the upgrade events the proxy emits.

What eth_getCode costs on BLAZED.sh

eth_getCode costs 1 credit per call on BLAZED.sh. Most results are a few kilobytes and irrelevant to billing, though the largest contracts approach the 24KB EIP-170 deployment limit and a batch of such reads still stays comfortably under the 100KB threshold where responses add 50 credits per MB. The realistic use is as a guard before other calls: one cheap read that stops you from debugging an ABI when the actual problem is an address with nothing behind it.

See the full credit price list

eth_getCode: frequently asked questions

How do I check whether an address is a contract?

Call eth_getCode and compare with 0x. Since EIP-7702 you also need to exclude the 23-byte delegation indicator starting with 0xef0100, which marks a wallet that delegated to contract code rather than a contract itself.

Can I get the Solidity source with eth_getCode?

No. Nodes only hold compiled runtime bytecode. Source comes from explorers and verification services that match a compilation against exactly this bytecode.

Why is the bytecode so short for a big contract?

It is almost certainly a proxy. The real logic sits at an implementation address stored in the EIP-1967 slot, which you read with eth_getStorageAt and then query with eth_getCode in turn.

Why does eth_call return 0x when eth_getCode shows code?

The address has code but not the function you called: a wrong ABI, a proxy whose implementation lacks the selector, or a fallback that returns nothing. Calls to a missing function hit the fallback path rather than failing loudly.

Does contract code ever change at the same address?

The bytecode at an address is fixed once deployed. Upgradeability comes from proxies pointing at new implementations, and EIP-7702 delegations on wallets can be reassigned. Both change behaviour without rewriting the code you read here.

Call eth_getCode from the node itself

Deploy your container or script next to a synced Ethereum node. No rate limits, no compute units, sub-10ms local RPC.