Ethereum Initcode vs Runtime Bytecode: What Actually Gets Deployed
Learn how Ethereum initcode runs once, returns runtime bytecode, and affects constructor arguments, gas, size limits, verification, and CREATE2.

You compile a Solidity contract, copy a long hexadecimal payload into a deployment transaction, and later find a different byte string at the contract address. Nothing necessarily went wrong. Ethereum contract creation uses two related programs: initcode, which runs once, and runtime bytecode, which remains afterward. That distinction connects ordinary blockchain basics to deployment gas, constructor behavior, source verification, and deterministic addresses.
Think of initcode as a construction crew carrying plans and materials onto an empty site. The crew can initialize storage and assemble the building, but it does not live there. Runtime bytecode is the finished building handed over when construction ends. If the crew fails or returns an invalid result, Ethereum does not keep a half-built contract.
What are initcode and runtime bytecode?
Initcode, also called creation code or initialization code, is EVM code supplied to the contract-creation process. For a top-level deployment transaction, the transaction has no recipient address and its data carries the initcode. A running contract can also supply initcode from memory to the CREATE or CREATE2 opcode.
The EVM executes that temporary program in the context of the new account. It can run constructor logic, initialize persistent storage, create other contracts, call external accounts, and then return a byte array. That returned array becomes the new account's runtime bytecode—the program used when someone later calls the contract.
The Ethereum Yellow Paper's contract-creation rules formalize the same sequence: execute initialization code, treat its output as the body code, charge the code-deposit cost, and persist the resulting state only if creation succeeds. Solidity's constructor documentation gives the compiler-facing view: constructor code executes during creation, while the final deployed code contains callable functions but excludes code used only by the constructor.
creation transaction or CREATE/CREATE2
|
v
initcode + constructor arguments
|
execute once in the EVM
/ \
failure RETURN bytes
| |
no contract state runtime bytecode storedThis is why “the bytecode” is too vague during debugging. Always ask whether a tool is showing creation bytecode, the complete initcode payload, or deployed runtime bytecode.
How a Solidity deployment becomes onchain code
Solidity compiler output separates the two main artifacts. In Standard JSON output, evm.bytecode.object represents creation bytecode, while evm.deployedBytecode.object represents deployed bytecode. The compiler output specification documents both fields.
A typical deployment follows these steps:
- The compiler produces creation bytecode and expected deployed bytecode.
- A deployment tool ABI-encodes the constructor arguments and appends them to the creation bytecode.
- A transaction with an empty
tofield carries that combined initcode, or a factory passes it toCREATEorCREATE2. - The EVM creates a temporary execution context for the prospective address and runs the initcode.
- Constructor logic may write storage, assign immutable values, emit logs, make calls, or revert.
- Successful initcode returns the runtime bytecode.
- Ethereum charges for depositing that returned code. If all creation checks pass, the runtime code and constructor-produced state persist.
Constructor arguments belong to the input side of this pipeline. They are available to creation logic, but they are not automatically stored as a readable suffix of the runtime code. A constructor may copy a value into storage, bake it into an immutable reference in runtime code, use it only for a check, or ignore it.
Solidity also explains that creation code patches assigned immutable values into the runtime code before returning it. Consequently, compiler-predicted deployed bytecode may contain placeholders or reference ranges that differ from the exact bytes stored after a particular deployment.
Why initcode can be much larger than runtime code
Initcode must contain everything required to construct the final program. That may include constructor-only branches, routines that copy bytes into memory, encoded constructor parameters, library-link placeholders, and data used to initialize state. Much of it disappears after construction.
Imagine flat-pack furniture. The shipping box includes instructions, protective packing, and assembly tools, but the finished desk is smaller than the package. Likewise, a contract with substantial setup logic can have large initcode and compact runtime code.
The reverse assumption is unsafe too: small constructor logic does not guarantee cheap deployment. Runtime code itself costs gas to store, and constructor storage writes or external calls can dominate the receipt's gas use. For the broader fee model, see how Ethereum gas fees work.
The two protocol size limits
Ethereum applies separate limits to the temporary program and the code it returns.
| Object | Protocol limit | What happens when exceeded |
|---|---|---|
| Initcode | 49,152 bytes | An oversized creation transaction is invalid; oversized input to CREATE or CREATE2 exceptionally aborts |
| Runtime bytecode | 24,576 bytes | Contract creation fails when initialization returns more than the maximum code size |
EIP-3860 defines MAX_INITCODE_SIZE as twice the code-size limit and charges 2 gas for each started 32-byte word of initcode. The rule meters client work such as jump-destination analysis and gives creation code an explicit upper bound.
EIP-170 defines the 24,576-byte runtime-code cap. The limit concerns the bytes returned for storage, not the total Solidity source length and not the hexadecimal string's character count. One byte is represented by two hex characters, and tools may add a 0x prefix.
These caps are consensus constraints, not targets. A contract close to either boundary can be harder to audit and deploy, while upgrades or compiler-setting changes may push it over unexpectedly. Measure the compiled artifacts produced by the exact compiler configuration you plan to use.
Why the distinction matters for CREATE2
CREATE2 derives the future address partly from keccak256(init_code), not from the runtime code alone. Constructor arguments appended to the creation payload therefore affect the address. A compiler version, optimizer setting, linked-library address, or metadata change can affect creation bytecode and produce a different address even when the source looks nearly identical.
Runtime equivalence does not rescue an incorrect prediction. Two different initcode programs might return identical runtime code, yet their CREATE2 addresses differ because their initcode hashes differ. Conversely, a constructor can observe external state, so a fixed deployment recipe deserves verification of both the inputs and the final state.
Verification and debugging traps
Comparing the wrong artifact
Block explorers read code stored at the contract address, so the primary comparison is against runtime bytecode. Comparing eth_getCode with a deployment transaction's full input will fail because the latter includes initcode and constructor arguments.
Ignoring compiler metadata
Solidity normally appends CBOR-encoded information to runtime bytecode. Its contract metadata documentation notes that source or compiler-setting changes can alter the metadata hash and therefore the resulting bytes. Match compiler version, optimizer runs, EVM target, libraries, sources, and metadata settings rather than stripping unexplained bytes until a comparison appears to work.
Forgetting immutables and libraries
Immutable values can be patched into runtime code during creation. Linked library addresses can change bytecode as well. Use the compiler's immutable and link-reference metadata to explain differences, then compare the fully linked, deployment-specific result.
Looking only at code for a proxy
The runtime bytecode at a proxy address may be a small delegating program rather than the application's implementation. Storage remains associated with the proxy while logic executes through another address. Trace the proxy contract and its upgrade authority before claiming which source governs behavior.
Risks and limitations
Creation is executable behavior. A constructor can call untrusted contracts, create dependencies, or make assumptions about external state. Review creation paths with the same care as callable runtime functions.
A successful transaction is not proof of intended code. Confirm the chain, address, runtime code hash, constructor arguments, initialized storage, ownership, and emitted events. A correct deployment of the wrong configuration is still wrong.
Failed deployment can consume gas. Reverts and out-of-gas conditions roll back the contract creation, but the sender still pays for computation the network performed. Simulate first and retain a realistic gas margin.
Verification has a scope. Matching source and compiler settings to runtime bytes supports reproducibility; it does not prove the source is safe, the constructor initialized the right values, or a proxy cannot later change implementation.
Factories expand the trust boundary. A factory decides which initcode it supplies and may impose permissions or initialization logic. Audit the factory, salt handling, and deployment events—not only the child contract.
Deployment checklist
- Pin the compiler version, optimizer configuration, EVM target, metadata settings, and linked libraries
- Distinguish creation bytecode, appended constructor arguments, complete initcode, and runtime bytecode
- Measure initcode and expected runtime code in bytes, not hex characters
- Simulate constructor calls and failure paths on the intended chain or a faithful fork
- For
CREATE2, hash the exact initcode and independently recompute the destination - After deployment, compare
eth_getCodewith the deployment-specific runtime artifact - Verify immutable values, storage initialization, owner or admin roles, and external dependencies
- If a proxy is involved, inspect the implementation pointer and upgrade authority separately
FAQ
Is initcode stored on Ethereum forever?
Not as the new account's executable code. The deployment transaction may remain in historical chain data, but only the runtime bytes returned by successful initcode become the account's code.
Are constructor arguments part of runtime bytecode?
Not automatically. They are appended to the creation payload. Constructor logic decides whether their effects appear in storage, immutable values, emitted logs, or nowhere persistent.
Why does eth_getCode differ from my compiler's bytecode?
You may be comparing runtime code with creation code, or your compiler settings, library addresses, metadata, and immutable values may differ. A proxy can add another layer of indirection.
Does runtime code size determine total deployment gas?
No. Code deposit is one component. Initcode execution, constructor storage writes, calldata, memory expansion, external calls, and the initcode word charge can all contribute.
Can two contracts have the same runtime bytecode at different addresses?
Yes. Addresses and code are separate properties. Different senders, nonces, salts, or initcode can produce different addresses even when the resulting runtime program is identical.
Treat deployment as a transformation
The clean mental model is a transformation, not a file copy: Ethereum executes initcode once, receives runtime bytecode as output, charges to store it, and commits the resulting account state only on success. Keeping those stages separate makes gas estimates, CREATE2 predictions, explorer verification, and constructor audits much easier to reason about.
This article is educational, not financial advice. Smart-contract interactions can be irreversible and crypto assets are volatile; test with only funds you can afford to lose, verify current primary documentation, and do your own research (DYOR).
Primary sources
Keep learning

What Are Smart Contracts? How They Work and Real Use Cases
Discover what smart contracts are, how they work on the blockchain, and their real-world use cases in DeFi, NFTs, and RWA — plus risks, limits, and FAQ.

Ethereum CREATE2 Explained: Deterministic Contract Addresses
Learn how Ethereum CREATE2 predicts a contract address from a factory, salt, and init code, with Solidity examples, use cases, and security checks.

Gas Fees Explained: A Complete Guide to Blockchain Transaction Costs
Learn how blockchain gas fees work, what drives costs up or down, practical tips to save on every transaction, and the risks every user should understand.
Explore related topics

Ethereum Glamsterdam Upgrade: ePBS, BALs, and What Is Actually Planned
Ethereum Glamsterdam is expected in Q4 2026. Learn its frozen scope, Sepolia milestone, ePBS, block-level access lists, and remaining uncertainties.
Crypto Address Poisoning: How to Verify Wallet Addresses Before You Send
Crypto address poisoning plants a lookalike address in your transaction history. Learn how it works and follow a safer verification checklist.