sourcify icon indicating copy to clipboard operation
sourcify copied to clipboard

Support every EVM chain + counterfactual contracts via CREATE2

Open jtsiskin opened this issue 4 years ago • 5 comments

CREATE2 allows sourcify to be useful on any EVM chain. It's extremely useful to be able to verify counterfactual addresses.

It would look similar to https://sourcify.dev/, just with two additional fields: sender address and salt. The "contract address" field would be then be the output instead of the input.

For convenience the source code could come from an already verified contract (as this infrastructure is already there), or any url. In this case a custom constructor data field would be useful.

jtsiskin avatar Jan 03 '22 23:01 jtsiskin

Thinking about this more, deploying and verifying a contract similar to https://solidity-by-example.org/app/create2/ is an easy way to do this. Perhaps this could be standardized

jtsiskin avatar Jan 03 '22 23:01 jtsiskin

Thanks for the suggestion. We can maybe have a chainId of 0 to indicate the CREATE2 addresses.

As far as I understood from the EIP, the init_code is the contract creation code (i.e. tx.data) and this also includes the constructor arguments. So if the user adds the constructor arguments they will be appended to the init_code and will be part of the address computation. I.e. different constructor arguments result in different contract addresses, right?

One thing crossed my mind is if we should allow verification of counterfactual contracts or if we should require the contract to be deployed alrea at one network. On the one hand, this is the whole idea of the CREATE2 opcode, on the other hand, I can see this being a kind of an attack vector. If we accept all CREATE2 addresses, anyone can bloat the repository with non-existing contracts by iterating the salt.

Can you also elaborate more on "standardization"?

kuzdogan avatar Jan 04 '22 07:01 kuzdogan

Yes, we would not want to allow non-existant contracts. Here's a quick example that hopefully demonstrates better (and the need for some 'standardization'): Let's say I want to use CREATE2 for a user onboarding scenario for a contract based wallet - give a user address x, but don't deploy the contract until necessary. But before a user sends some ether, NFTs, etc. to this address, the user wants to verify the counterfactual address's code, even if it's not deployed.

As an example, lets say my desired Wallet code is Wallet.sol. I can upload and deploy it: 0x05e2a930aa689cded59a10c7f0b8a8c0967beb4a. I verify the source in the standard way.

Now, lets say I want to prove to you that counterfactual address 0x58CD30e7c8d65b90f2224034F597231eDd0D0c72 = new Wallet(0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B, 0x6262998Ced04146fA42253a5C0AF90CA02dfd2A3), so you can start using it - on any EVM chain, and without needing to deploy it.

Here's an example of how sourcify could do that:

Verify
address of a verified, deployed contract
OR
contract source code
after above is entered, constructor arguments, from ABI
deployer address
salt
Verify example
verified contract address 0x05e2a930aa689cded59a10c7f0b8a8c0967beb4a
constructor argument - owner 0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B
constructor argument - admin 0x6262998Ced04146fA42253a5C0AF90CA02dfd2A3
deployer address 0xe8c16c17d879c9bd52a855241b21e4ce5fe5dcf3
salt 0x01
output 0x58CD30e7c8d65b90f2224034F597231eDd0D0c72

Here is an example showing this can kind of be done via etherscan's UI, see code for more comments. It hardcodes the 'verified contract's initCode and ABI to match the Wallet.sol from above, but in reality sourcify would fetch these using either the verified contract or the uploaded files. https://ropsten.etherscan.io/address/0xe8c16c17d879c9bd52a855241b21e4ce5fe5dcf3#readContract image

This verifies 0x58CD30e7c8d65b90f2224034F597231eDd0D0c72 = new Wallet(0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B, 0x6262998Ced04146fA42253a5C0AF90CA02dfd2A3), or any a = new Wallet(x,y), while only needing to verify Wallet once - only one copy in the repository.

Sure enough, when I run deployWalletExample, 0x58CD30e7c8d65b90f2224034F597231eDd0D0c72 is equal to new Wallet(0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B, 0x6262998Ced04146fA42253a5C0AF90CA02dfd2A3)

jtsiskin avatar Jan 04 '22 17:01 jtsiskin

Thanks for the detailed description and the contract example! It is much clearer

One thing we need to make sure is to ensure we have the correct contract creation code as the creation code after compilation might differ from the one being deployed, e.g. when there are immutables in the contract which will be injected into the creation code in deploy time. One way can be to use make use of the Javascript EVM as outlined here: https://ethereum.stackexchange.com/questions/94396/how-to-get-the-actual-runtime-bytecode-from-creation-bytecode-and-constructor-ar by @FabijanC

Another thing, again, is if or when we store the counterfactual contract in the repository. I'd imagine we have a form with:

  • deployer address
  • salt
  • constructor arguments
  • An already verified contract address+network OR contract source code

Then the output is the counterfactual address 0x58CD30e7c8d65b90f2224034F597231eDd0D0c72 in your example. This way any contract at the same address on any EVM chain is guaranteed to have this source code deployed. About saving to repository I have some thoughts:

  • Immediately save Wallet.sol to /contracts/(full | partial)_match/0/0x58CD30e7c8d65b90f2224034F597231eDd0D0c72 after user generates the counterfactual address. My concern was there are nearly infinite many addresses possible by changing the salt. Question is how much of a concern is this? Maybe can we ask for a CAPTCHA?

  • Save to /contracts/(full | partial)_match/0/0x58CD30e7c8d65b90f2224034F597231eDd0D0c72 if there is a contract deployed at any of the EVM chains supported. This way we avoid storing contracts that will never be used. Users still can check if the same counterfactual contract address is generated by the inputs they gave, but we won't store it. Question: Will looking for all EVM chains be a problem?

  • Save to /contracts/(full | partial)_match/0/0x58CD30e7c8d65b90f2224034F597231eDd0D0c72 if there is a contract deployed at the given address and network. We ask the user exactly where the contract is deployed at but we still save it as chainId=0 i.e. any chain.

I'd also like to know what are @chriseth 's thoughts on this when he's back.

kuzdogan avatar Jan 17 '22 11:01 kuzdogan

CREATE2 is depends on the creation bytecode, not the runtime bytecode, so no EVM should be needed. Nothing new even needs to be stored in the sourcify repo - just ideally there is a nice API+UI+URL for providing the creation bytecode hash, constructor arguments, salt, and deployer address, and getting back the CREATE2 address + verified source code. ("Nothing new needs to be stored" if the creation bytecode hash is given as a pointer to an existing verified contract). Technically this could all be done any wallet/explorer itself using what sourcify already offers; it would just be nice to, for example, give a link sourcify.dev/create2?baseContract=X&salt=Y&deployer=Z, and have that jump straight to a page with the verified source code + verified address.

jtsiskin avatar Jan 24 '22 11:01 jtsiskin

A brief recap about this activity:

  • new api /verify/create2
    • with the following parameters (need to choose either one of the last two parameters)
      • deployerAddress: address: the factory contract that will deploy using create2
      • salt: bytes32: the salt given to create2
      • constructorArgs: json<type,value>[]: the arguments of the constructor
      • [baseContract: address]: the address of the already Sourcify-verified contract to deploy using create2
      • [files: json]: a json containing the files used to generate the bytecode to pass to create2
    • that will verify a counterfactual contract and save it using chainId 0
  • new page on sourcify sourcify.dev/create2
    • users need to choose if they want to upload files or to put an address of a Sourcify-verified smart contract
    • the ui will generate inputs rappresenting the constructor argument
    • salt is asked
    • deployer contract is asked
    • on form submit /verify/create2 api is called

marcocastignoli avatar Oct 07 '22 17:10 marcocastignoli

@ligi above you see a recap of this activity. If we allow anyone to call /verify/create2, there can be potentially a spam problem. One of the solution could be to use a Captcha. @kuzdogan told me that you already used captcha in the past, and maybe you could suggest us a good library to use

marcocastignoli avatar Oct 10 '22 21:10 marcocastignoli