Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

bug(verify-bytecode) - Incorrect constructor args being returned from Etherscan's Get Source Code Endpoint #8545

Closed
2 tasks done
blmalone opened this issue Jul 28, 2024 · 3 comments · Fixed by #8547
Closed
2 tasks done
Labels
C-forge Command: forge Cmd-forge-verify Command: forge verify-contract/check T-bug Type: bug
Milestone

Comments

@blmalone
Copy link

blmalone commented Jul 28, 2024

Component

Forge

Have you ensured that all of these are up to date?

  • Foundry
  • Foundryup

What version of Foundry are you on?

forge 0.2.0 (bdd1137 2024-07-28T00:21:06.817030000Z)

What command(s) is the bug in?

forge verify-bytecode

Operating System

macOS (Apple Silicon)

Describe the bug

This issue is a follow-up to the previously closed issue foundry-rs/foundry#8505. Although the PR fix has ensured that the --constructor-args functionality now works, I believe Etherscan is returning incorrect constructor arguments for some contracts via the Get Contract Source Code for Verified Contract Source Codes API.

As a user, when I execute forge verify-bytecode with the --constructor-args flag, a warning message appears and the verification succeeds (init and runtime code partially verifies):

The provided constructor args do not match the constructor args from etherscan (0x000000000000000000000000e07ea0436100918f157df35d01dce5c11b16d1f1).

When I remove the --constructor-args flag, the warning message disappears but the verification fails (init code partially succeeds but runtime code fails).

I believe that this is expected behavior for forge verify-bytecode because Etherscan seems to be returning the wrong constructor arguments for 0x0d3495a95eC5720453C0d70a88Bf14fe13ebe969 . However, is there perhaps a different API we can use? Or an alternative, more accurate way to retrieve the constructor arguments?

Reproducing the failure

The quickest way to reproduce this failure is using the Optimism Github repo.

Verification fails when we remove the --constructor-args flag. This is because Etherscan is returning incorrect constructor arguments for 0x0d3495a95eC5720453C0d70a88Bf14fe13ebe969.

git clone https://github.com/ethereum-optimism/optimism.git
cd optimism
git checkout c93958755b4f6ab7f95cc0b2459f39ca95c06684
nvm install 16.0.0 && nvm use 16.0.0
yarn install
cd packages/contracts-bedrock
FOUNDRY_EVM_VERSION=london forge verify-bytecode 0x0d3495a95eC5720453C0d70a88Bf14fe13ebe969 OptimismMintableERC20Factory
Screenshot 2024-07-27 at 20 26 16

Verifying the issue

Verification succeeds because we pass through the correct --constructor-args which takes preference over the Etherscan constructor args. Notice the warning message in the screenshot.

git clone https://github.com/ethereum-optimism/optimism.git
cd optimism
git checkout c93958755b4f6ab7f95cc0b2459f39ca95c06684
cd packages/contracts-bedrock
FOUNDRY_EVM_VERSION=london forge verify-bytecode 0x0d3495a95eC5720453C0d70a88Bf14fe13ebe969 OptimismMintableERC20Factory --constructor-args 0x6d0f65D59b55B0FEC5d2d15365154DcADC140BF3
Screenshot 2024-07-27 at 20 20 15

If we convert the value printed from the warning message to an address, we get 0xe07ea0436100918f157df35d01dce5c11b16d1f1 which is an L1StandardBridgeProxy contract of an entirely different rollup chain.

When hitting Etherscan's API:

https://api.etherscan.io/api?module=contract&action=getsourcecode&address=0x0d3495a95eC5720453C0d70a88Bf14fe13ebe969&apikey=$ETHERSCAN_API_KEY

We can see that the ConstructorArguments field is indeed incorrect:

...
"Runs": "999999",
"ConstructorArguments": "000000000000000000000000e07ea0436100918f157df35d01dce5c11b16d1f1",
"EVMVersion": "london",
...
Screenshot 2024-07-27 at 20 44 59

We can verify this is incorrect by inspecting 0x0d3495a95eC5720453C0d70a88Bf14fe13ebe969 's creation transaction and looking at the last 20 bytes. The value you'll see is 6d0f65d59b55b0fec5d2d15365154dcadc140bf3. This is the address that Etherscan should return as part of the API call.

I think that if a similar contract has been deployed before, Etherscan references that contract source code and constructor arguments instead of the contract a user asks for.
See screenshot below (the address referenced in the screenshot 0xa2f9fcf9108d870d4a8249bf3b74e812a3bd39fb, contains the incorrect constructor args being returned i.e. 000000000000000000000000e07ea0436100918f157df35d01dce5c11b16d1f1:
Screenshot 2024-07-27 at 21 05 10

Possible Resolution

One possible workaround while still using Etherscan could be as follows:

  1. Use Etherscan's API to return the contract's creation transaction.
  2. Once returned, extract the transaction's input data.
  3. Using the data compiled in forge-artifacts (e.g., OptimismMintableERC20Factory.json file), extract the init code from bytecode.object:
    "abi": [
        ...
    ],
    "bytecode": {
        "object": "0x.....",
        ...
    },
    ...
  1. With the transaction input data from step 2, remove the init code retrieved in step 3. The remaining part will be the constructor arguments. Here’s how to do it in TypeScript:
const constructorArgs = inputDataFromStep2.replace(initCodeStep3, '');
@blmalone blmalone added the T-bug Type: bug label Jul 28, 2024
@blmalone
Copy link
Author

blmalone commented Jul 28, 2024

@klkvr tagging you because you recently worked in this part of the code 🙏🏻

If this was prioritized we would really appreciated it! This feature will help us deliver Superchain features faster!

cc: @zerosnacks @mattsse @yash-atreya

@klkvr
Copy link
Member

klkvr commented Jul 28, 2024

yeah, I think we just need to expose --guess-constructor-args option for forge vb. It does exactly what you've mentioned in the issue. Though this would only be possible in cases of full creation code match

@blmalone
Copy link
Author

blmalone commented Jul 28, 2024

@klkvr So I understand, would the default usage be to always pass constructor args via --constructor-ags and the alternative approach be to explicitly pass the --guess-contstructor-args argument, for when you don't have the constructor args in situ and you want forge vb to perform a best effort to guess them?

Also, I reran the command:

FOUNDRY_EVM_VERSION=london forge verify-bytecode 0x0d3495a95eC5720453C0d70a88Bf14fe13ebe969 OptimismMintableERC20Factory
Screenshot 2024-07-28 at 16 44 42

Both init and runtime code match with 'full' verification. Is this expected? Before the init code matched with 'partial'.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
C-forge Command: forge Cmd-forge-verify Command: forge verify-contract/check T-bug Type: bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants