0

I'm currently delving into EVM and contract bytecode.
My understanding is that by sending a transaction to the NULL address with both the initcode and contract code, and ensuring that the init code returns the contract code (runtime code) in the final instruction, a contract with an initialized state is created.

Given that my creation code (initcode + runtimecode) includes the runtimecode, I would expect that once my contract is deployed, its runtime code would be contained within the input data of the transaction that created the contract.

To validate my comprehension, I attempted to inspect the contract of a project I like, but I seem to be overlooking something.

Contract creation:

https://etherscan.io/tx/0x3b8ad64a25f39773d5fa4ce5c167414ade5d2d9b4edd98f69c04240060224675

In this transaction, the contract is created using the CREATE2 opcode, utilizing the contract 0x4e59b44847b379578588920cA78FbF26c0B4956C. Clicking on "more details" allows for viewing the input data sent to this contract.

Created contract:

https://etherscan.io/address/0x1bbe973bef3a977fc51cbed703e8ffdefe001fed#code

Here is the resulting contract. By examining the Contract Creation Code section, I discovered precisely the same data present in the input data section of the preceding transaction (minus 32 bytes, which are used as salt for the create2 opcode).

Runtime bytecode:

So far so good: the input data matched the Contract Creation Code. Therefore, I expected the Deployed Bytecode section of Etherscan to display the same bytecode as the Contract Creation Code, minus the constructor and constructor parameters.

However, upon comparison, some discrepancies emerged:

Differences between creation and runtime bytecode

As you can see, a lot of zeros present in the creation code are replaced with some other data in the runtime bytecode.

I cannot wrap my head around this.

Can you help me to understand this behaviour?
I thought that some kind of optimization kicked in to use free spaces (zeros) but I cannot understand how... the compile phase is before submitting the transaction, not after.

Thanks

1 Answer 1

0

To deploy a contract the EVM executes the constructor's bytecode. Usually the constructor initializes the contract storage and returns the contract's runtime bytecode, but that isn't required.

Recent version of solc implemented the immutables variables. Those variables behave like constant variables after they are initialized.

https://docs.soliditylang.org/en/v0.8.25/contracts.html#immutable

The contract creation code generated by the compiler will modify the contract’s runtime code before it is returned by replacing all references to immutables with the values assigned to them.

In general the constructor isn't required to include the runtime bytecode. It could generate it during the execution.

2
  • Thank you! That could be a valid explanation! I just don't understand one last thing: I have compiled che contract with Remix and I'm able to reproduce 100% of the creation code, even the CBOR section with IPFS hash, but if I go to "compilation details"->"Runtime bytecode", remix shows me a different runtime bytecode. It shows me the creation minus constructor. Is that a bug? Shouldn't shows me what is really on chain? I mark your answer as the solution btw, thank you again! Commented Mar 29 at 10:08
  • 1
    Ok, I'll answer myself: it's normal that remix shows runtime bytecode without the immutables applied, since some immutables could depends from constructor parameters. Moreover I verified that all the modification that I have on the runtime bytecode are related to immutables. Commented Mar 30 at 17:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.