Skip to content

Contract bytecode changes vastly when independent contracts added to the compiler  #14250

Description

@kuzdogan

Summary

I came across a contract that could not be verified on Sourcify because the Sourcify's compilation output bytecode is different than the author's (Hardhat). Diving deeper I've found out the difference comes to the surface when some other contracts that are unrelated to the compilation target are added to compilation.

Specifically these two standard JSON inputs yield different bytecode for the same contract CompoundLens.sol:
CompoundLens-solc-input-Sourcify-bytecode.json.txt
CompoundLens-solc-input-Hardhat-bytecode.json.txt

The only input sources differences are the following which are unrelated to CompoundLens:

[
  '@openzeppelin/contracts/interfaces/draft-IERC1822.sol',
  '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol',
  '@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol',
  '@openzeppelin/contracts/proxy/Proxy.sol',
  '@openzeppelin/contracts/proxy/beacon/IBeacon.sol',
  '@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol',
  '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol',
  '@openzeppelin/contracts/utils/StorageSlot.sol'
]

You'll notice the bytecodes differ even when the metadata hashes are the same. This is unexpected as the different sources listed above are not related to CompoundLens. CompoundLens already compiles without those contracts are input in CompoundLens-solc-input-Sourcify-bytecode.json but when added in CompoundLens-solc-input-Hardhat-bytecode.json the contract's bytecode changes.

To reproduce

Using Solidity version v0.8.19

Output the bytecodes

  1. Sourcify
cat CompoundLens-solc-input-Sourcify-bytecode.json.txt | solc --standard-json | jq '.contracts."contracts/Comptroller/CompoundLens.sol".CompoundLens.evm.bytecode.object' > CompoundLens-Sourcify-creation-bytecode.txt
  1. Hardhat
cat CompoundLens-solc-input-Hardhat-bytecode.json.txt | solc --standard-json | jq '.contracts."contracts/Comptroller/CompoundLens.sol".CompoundLens.evm.bytecode.object' > CompoundLens-Hardhat-creation-bytecode.txt

Compare the bytecodes:

git diff --word-diff --word-diff-regex=. CompoundLens-Sourcify-creation-bytecode.txt CompoundLens-Hardhat-creation-bytecode.txt

Background

The contracts are on this Github repo (verify branch): https://github.com/meterio/sumer-project/tree/verify

To compile (and deploy) the original sources:

git clone https://github.com/meterio/sumer-project/
cd sumer-project
rm -rf dist
yarn
yarn compile
npx hardhat dcl --rpc http://rpctest.meter.io/ --pk $PRIV_KEY

The contract is also deployed at: https://goerli.etherscan.io/address/0x46df081108b2e2FDf1bF1E84Eeb2D7ec3AdA0061

The bytecode diff between the Sourcify output and Hardhat output was not in the metadata hash or in a certain recognizable pattern for me:
image

CompoundLens-hardhat-recompiled-creation.txt
CompoundLens-recompiled-creation.txt

To generate the diff: git diff --word-diff --word-diff-regex=. CompoundLens-hardhat-recompiled-creation.txt CompoundLens-recompiled-creation.txt

What I tried to do was to start from the standard JSON input of Sourcify and try to reach the Hardhat output bytecode in iterations:
initial Sourcify JSON input: CompoundLens-solc-input.json.txt
Hardhat JSON input: CompoundLens-hardhat-solc-input.json.txt

  1. Tried using the full compilation settings from Hardhat.
    Using the same settings in Hardhat in the initial Sourcify input didn't change the bytecode.
  2. Tried using the whole sources from the Harhat input in the initial Sourcify input

Yes by using all of the sources from Hardhat, one generates the Hardhat's bytecode output.

Next, I iteratively copied sources from the Hardhat input to the Sourcify input to see adding which sources cause the change in the bytecode.

Sourcify's initial standard JSON input:
image

Hardhat's standard JSON input (clipped):
image

On each step I copied a contract that might be a potential cause of change, resolved the dependencies by also adding them, compiled the new iterated JSON input and compared the bytecodes.

Finally, I found a minimal standard JSON diff, that adding the specific sources would change the bytecode output. These are laid out in the above Summary section.

Environment

  • Compiler version: v0.8.19
  • Target EVM version (as per compiler settings): paris
  • Framework/IDE (e.g. Truffle or Remix): Hardhat and solc CLI
  • EVM execution environment / backend / blockchain client:
  • Operating system: MacOS

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions