ethereum / sourcify

Decentralized Solidity contract source code verification service
https://sourcify.dev
MIT License
780 stars 395 forks source link

Handling "Minimal Proxy Contract" pattern #1624

Open kuzdogan opened 1 month ago

kuzdogan commented 1 month ago

Looking at the verification requests failing, I see a large portion of them (esp. coming from Etherscan) are bytecode-only proxies. These are "Minimal Proxy Contract"s (a nice deep dive here) that don't have a source code but do simple proxying.

Their bytecodes are similar and contain the address they proxy to:

0x363d3d373d3d3d363d73 9ec1c3dcf667f2035fb4cd2eb42a1566fd54d2b7 5af43d82803e903d91602b57fd5bf3
0x363d3d373d3d3d363d73 787ec93dd71a90563979417879f5a3298389227f 5af43d82803e903d91602b57fd5bf3
0x363d3d373d3d3d363d73 af18644083151cf57f914cccc23c42a1892c218e 5af43d82803e903d91602b57fd5bf3

Some examples: https://optimistic.etherscan.io/address/0x6c5aDf5eDd2C77b63d6Fed3851c7C30E3f6D3C2b#code https://etherscan.io/address/0x5d81ba8ae27d1c5cd21beeec1221efcd98b64a61#code https://sepolia.etherscan.io/address/0x7dc5d4c46e231f9f85875da38afc100a035baffa#code

Now in principle, we don't resolve proxies because the implementation addresses may change. But in this case the implementation address is embedded in the bytecode and cannot change.

I suggest we handle this case in some way in our database and add a minimalProxy and implementation fields to the API responses. I'd say we skip it for the current API as I don't see this going anywhere in any of the responses but add it in APIv2 #1470 #1545.

This would also be a relief for the Etherscan calls if we first get the bytecode instead of first calling Etherscan, and directly mark these contracts as minimal proxies.

Now the questions are:

  1. How do we mark these contracts in the database?
    • Keep in mind this contract does not have a source code. It also means there are not files to save.
  2. How do we return this info in the APIv2 response? We can continue this discussion in #1545 after we resolve 1.
  3. When these contracts are requested with the current API, what do we return?
marcocastignoli commented 1 month ago

How do we mark these contracts in the database?

My take on this:

verified_contracts.compilation_id cannot be null, so we need to have a default compiled_contracts to use for such cases, for example:

compiled_contracts:
  id: 99999999999, // explicity id to use only for this case (if possible)
  compiler: ""
  version: ""
  language: ""
  name: ""
  fully_qualified_name: ""
  compiler_settings: {}
  compilation_artifacts: {}
  creation_code_hash: "\\x" // default NULL value
  creation_code_artifacts: {}
  runtime_code_hash: "\\x"  // default NULL value
  runtime_code_artifacts: {}

Then we can store the proxy contract as:

verified_contracts:
  deployment_id: create a `contract_deployments` normally and store its id here 
  compilation_id: use the `compiled_contract` defined above
  creation_match: true // from here use default or empty values
  creation_values: {}
  creation_transformations: []
  runtime_match: true
  runtime_values: {}
  runtime_transformations: []
  runtime_metadata_match: false
  creation_metadata_match: false

sourcify_matches:
  verified_contract_id: use the `verified_contract` above
  creation_match: "perfect"
  runtime_match: "perfect"
  metadata: {}
  proxy: "proxy_type1" // here I would specify the proxy type or null if it's not a proxy
  proxied_contract: verified_contract_id of the proxied contract // does this make sense?
kuzdogan commented 1 month ago

Well, actually the question is, do we even verify this contract? There's no source code to verify and it's actually the block explorers' job to find this pattern.

The start of the problem was when I saw such requests often in Etherscan requests that fail an overload the API key. It can easily be solved by first fetching the bytecode and discarding if it's a minimal proxy. Then I thought if we should save these contracts. Maybe not at all?

manuelwedler commented 2 weeks ago

This should be tackled after #1712