Verification with Immutable Varibles
Solidity
While Sourcify can verify Solidity contracts containing immutable variables, the process is more intricate than verifying contracts that don't have immutables. First, Sourcify recompiles the contract, which produces the runtime bytecode and the immutableReferences
object. This object indicates the specific position within the execution bytecode where the immutable variables are located. After obtaining these, the next step is to replace all instances of the immutables in both the runtime and deployed bytecodes with zeros. Finally, a direct comparison is made between the two bytecodes to get a full or a partial match.
More details on how immutables affect verification below:
Immutable variables
It is not as straightforward to verify contracts with immutable variables because of their nature. Quoting from the Solidity docs:
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 by the values assigned to the them.
That means the runtime bytecode generated after recompiling will be different than the one already deployed on chain, because the references will have been "filled" with the values of the immutables when deploying the code.
For example in the bytecode excerpt from the Görli contract 0xbdde4d595f2cdda92ca274423374e0e1c7286426 (Etherscan, Sourcify) the reference of the simple immutable variable will be the 64 consecutive 0
in hex in the recompiled bytecode:
...282565b5050565b7f000000000000000000000000000000000000000000000000000000000000000081565b828054600181600116156101...
...which will be modified in runtime when deploying the contract to the value 2
. So the deployed bytecode will be:
...282565b5050565b7f000000000000000000000000000000000000000000000000000000000000000281565b828054600181600116156101...
☝️
Vyper
Sourcify supports verification of Vyper contracts containing immutable variables. Here's how it works:
CBOR auxdata: Vyper appends an array in CBOR format at the end of the creation bytecode. This array contains the following elements:
[
integrity_hash, // Hash of the contract's source code, included starting with Vyper 0.4.1
runtime_size, // Size of the runtime bytecode
data_sizes, // Array of data section sizes
immutable_size, // Total size of all immutable variables
compiler // Compiler information, e.g. {"vyper": [0, 4, 0]}
]Example CBOR data:
[167, [10], 96, {"vyper": [0, 4, 0]}]
Locating Immutables: To find the immutable values:
- Look at the deployed runtime bytecode
- Take the last
immutable_size
bytes - these contain the actual values of the immutables
Verification Process: To verify the contract:
- Remove the immutable values from the onchain runtime bytecode (by truncating the last
immutable_size
bytes) - Compare this with the recompiled bytecode
- Remove the immutable values from the onchain runtime bytecode (by truncating the last