Does Etherscan display the Creation Code or Runtime Code of a Contract?

Does Etherscan display the Creation Code or Runtime Code of a Contract?

This is gonna be a quick short post on some findings regarding how etherscan displays the bytecode of a deployed and verified smart contract.

After we deploy a contract on the ethereum blockchain, we know for a fact that the creation code(init code) is executed and it returns the runtime bytecode which is then stored on-chain.

It must be noted that only the runtime bytecode of a smart contract is stored on-chain for further execution of the smart contract.

However, here is a quick question:

When we deploy a smart contract on Ethereum and verify it on Etherscan, what exactly is the bytecode that is attached to the verified contract?

A very intuitive answer to this question is the Runtime Bytecode because that's the portion of bytecode that is stored on-chain. Therefore, its easier to assume that etherscan displays just the runtime bytecode.

Well, that does not seem to be the case.

So the question remains - the bytecode that we see on etherscan, What exactly is it?

  • Is it the entire bytecode, i.e., (Creation code + Runtime Code)?
  • Or, Is it just the Runtime code?
  • Or, Is it Runtime + Constructor arguments only?

Let's do a quick experiment to figure this out.

A Quick Experiment

We gonna deploy 2 different contracts:

  • First one without any constructor arguments, and
  • Second one with constructor arguments

First Deployment: Without Constructor Arguments

We use the following Test Contract 👇

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.17;

/**
 * @title Test
 * @dev Sets and Gets a uint variable called Pointer
 */
contract Test{

    uint256 public pointery;

    constructor() {
        pointery = 100;
    }

    function setPointer(uint256 _num) public {
        pointery = _num;
    }

    /**
     * @dev Return owner address 
     * @return address of owner
     */
    function getPointer() external view returns (uint256) {
        return pointery;
    }
}
Test Contract with no constructor args

Actual Creation Code

Creation Code
608060405234801561001057600080fd5b50606460008190555061017f806100286000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632f5f3b3c14610046578063a32a3ee414610064578063acfee28314610082575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61006c6100a4565b60405161007991906100d0565b60405180910390f35b61009c6004803603810190610097919061011c565b6100ad565b005b60005481565b60008054905090565b8060008190555050565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea264697066735822122095cb79f37fbcae031f3faea184aa57e0c8cd49ed26e7a6d17a48cf3858e56d9f64736f6c63430008110033

Actual Runtime Code

Runtime Bytecode
608060405234801561001057600080fd5b50600436106100415760003560e01c80632f5f3b3c14610046578063a32a3ee414610064578063acfee28314610082575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61006c6100a4565b60405161007991906100d0565b60405180910390f35b61009c6004803603810190610097919061011c565b6100ad565b005b60005481565b60008054905090565b8060008190555050565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea264697066735822122095cb79f37fbcae031f3faea184aa57e0c8cd49ed26e7a6d17a48cf3858e56d9f64736f6c6343000811003300

Etherscan's Bytecode After Contract Verification

608060405234801561001057600080fd5b50606460008190555061017f806100286000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632f5f3b3c14610046578063a32a3ee414610064578063acfee28314610082575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61006c6100a4565b60405161007991906100d0565b60405180910390f35b61009c6004803603810190610097919061011c565b6100ad565b005b60005481565b60008054905090565b8060008190555050565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea264697066735822122095cb79f37fbcae031f3faea184aa57e0c8cd49ed26e7a6d17a48cf3858e56d9f64736f6c63430008110033

Second Deployment: With Constructor Arguments

We use the following Test Contract 👇 and pass 1000 as the argument.

/**
 *Submitted for verification at Etherscan.io on 2023-02-15
*/

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.17;

/**
 * @title Test
 * @dev Sets and Gets a uint variable called Pointer
 */
contract Test{

    uint256 public pointer;

    constructor(uint256 _num) {
        pointer = _num;
    }

    function setPointer(uint256 _num) public {
        pointer = _num;
    }

    /**
     * @dev Return owner address 
     * @return address of owner
     */
    function getPointer() external view returns (uint256) {
        return pointer;
    }
} 
Test Contract with 1 constructor args

Actual Creation Code

0x608060405234801561001057600080fd5b506040516102353803806102358339818101604052810190610032919061007a565b80600081905550506100a7565b600080fd5b6000819050919050565b61005781610044565b811461006257600080fd5b50565b6000815190506100748161004e565b92915050565b6000602082840312156100905761008f61003f565b5b600061009e84828501610065565b91505092915050565b61017f806100b66000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632f5f3b3c14610046578063a32a3ee414610064578063acfee28314610082575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61006c6100a4565b60405161007991906100d0565b60405180910390f35b61009c6004803603810190610097919061011c565b6100ad565b005b60005481565b60008054905090565b8060008190555050565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea2646970667358221220e258c2c50f60615be13c1352bb92ad8365dcb6b971ca21882a504f1beb4167fc64736f6c6343000811003300000000000000000000000000000000000000000000000000000000000003e8

Actual Runtime Code

608060405234801561001057600080fd5b50600436106100415760003560e01c80632f5f3b3c14610046578063a32a3ee414610064578063acfee28314610082575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61006c6100a4565b60405161007991906100d0565b60405180910390f35b61009c6004803603810190610097919061011c565b6100ad565b005b60005481565b60008054905090565b8060008190555050565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea2646970667358221220e258c2c50f60615be13c1352bb92ad8365dcb6b971ca21882a504f1beb4167fc64736f6c6343000811003300

Etherscan's Bytecode After Contract Verification

608060405234801561001057600080fd5b506040516102353803806102358339818101604052810190610032919061007a565b80600081905550506100a7565b600080fd5b6000819050919050565b61005781610044565b811461006257600080fd5b50565b6000815190506100748161004e565b92915050565b6000602082840312156100905761008f61003f565b5b600061009e84828501610065565b91505092915050565b61017f806100b66000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80632f5f3b3c14610046578063a32a3ee414610064578063acfee28314610082575b600080fd5b61004e61009e565b60405161005b91906100d0565b60405180910390f35b61006c6100a4565b60405161007991906100d0565b60405180910390f35b61009c6004803603810190610097919061011c565b6100ad565b005b60005481565b60008054905090565b8060008190555050565b6000819050919050565b6100ca816100b7565b82525050565b60006020820190506100e560008301846100c1565b92915050565b600080fd5b6100f9816100b7565b811461010457600080fd5b50565b600081359050610116816100f0565b92915050565b600060208284031215610132576101316100eb565b5b600061014084828501610107565b9150509291505056fea2646970667358221220e258c2c50f60615be13c1352bb92ad8365dcb6b971ca21882a504f1beb4167fc64736f6c6343000811003300000000000000000000000000000000000000000000000000000000000003e8

Findings

After the above deployments and bytecode comparisons of the actual bytecode and etherscan's bytecode, here are the findings that I came across:

  1. Although the Runtime code is the only part of the bytecode that is stored on-chain, etherscan doesn't display just the runtime code.
  2. Etherscan displays the entire bytecode (Init Code + Runtime Code) of the smart contract appended with the constructor arguments at the end, if they exist.
    So technically etherscan displays:
Init Code (executed during contract deployment ) + Runtime Code (part that is stored on-chain) + Constructor Arguments

An additional detail: The bytecode of smart contracts that are not verified yet on Etherscan is just the runtime bytecode.