Is there a way to avoid using a factory with the Clone pattern? Do end users care either way?

We're considering moving an NFT contract to use an OZ clone approach.

It seems like this requires a factory pattern.

Two questions:

  1. Anecdotally, do users mind that they're not really deploying a contract directly? I realise this is a purely UX question, but in our experience some users really do care about stuff like this.

For example, if they're using metamask, a regular contract deployment looks like this:

But a factory pattern might look like this (I've mocked up the function name to be Deploy Contract here):

A second minor issue, is that on etherscan, the contract deployment information is a bit buried away. For a regular contract deployment the deployed contract appears very prominently on the transaction page:

But for a factory, that information is missing on the main page. Instead, you have to dive into the internal transactions to see it:

Do end users care about this kind of thing?

  1. Assuming for the sake of argument they do care, is there a way to use the clone library without a factory somehow?

John

Hey @John_Woodl,

Indeed the deployment using Clones or any other in-contract deployment won't show as a regular deployment, this is because the CREATE and CREATE2 opcodes work internally on the EVM and not as a consequence of a transaction to null as it would be in a regular deployment, so I don't see an easy workaround for how Metamask or Etherscan show it if that's what you want.

Answering your questions:

  1. Anecdotally, do users mind that they're not really deploying a contract directly? I realise this is a purely UX question, but in our experience some users really do care about stuff like this.

I don't think it should be a concern. For non-technical users, the contracts work exactly the same and if your users are technical they might be ok with looking for the address in the events.

Some examples of this pattern adopted by thousands of users are both Safe Multisig and Argent wallets.

  1. Assuming for the sake of argument they do care, is there a way to use the clone library without a factory somehow?

In order to make a clone you need:

  1. An implementation address already deployed
  2. Call Clones.clone(implementation)

The idea of a factory is just a place to deploy the implementation and use it for every clone, you can do this from any smart contract you need.

Thanks so much @ernestognw .

I think we came to the same conclusion that the factory approach should be fine for users :slight_smile:

Just on the second point, thanks for your detailed answer. I guess I was more asking if there was a way to generate the bytecode off-chain and then deploy that directly. So, for example, is there a way we could run the equivalent of a Clones.clone(implementation) offline to generate the raw bytes for contract deployment, and then initiate a contract deployment using the standard contract deployment approach? (e.g. new web3.eth.Contract().deploy(...) )

I realise we might as well just use the factory approach (and that is what we'll probably end up doing), but I was just curious if there was an alternative.

Thanks!

1 Like

Hey @John_Wood I see what you mean.

The Clones contract is based on the code of ERC1167 for Minimal Proxies. It's a specification of a minimal contract that forwards everything to a hardcoded implementation address, so you can definitely deploy it manually in a regular transaction although it's not encouraged because working with low-level EVM codes is prone to error.

In order to do so, you need to send a transaction with the to parameter set to null and the following bytecode:

0x3d602d80600a3d3981f3363d3d373d3d3d363d73<...>5af43d82803e903d91602b57fd5bf3

Where:

// Copies runtime bytecode to memory and returns it (setting the address code)
0x3d602d80600a3d3981f3
// Minimal Proxy code according to ERC 1167 (<...> is the implementation address)
0x363d3d373d3d3d363d73<...>5af43d82803e903d91602b57fd5bf3

In case you're going this route, please make sure you understand how these instructions operate.

Hope it helps!

Thanks @ernestognw - that's super useful.

I think we'll stick with the factory approach.

In fact, I think there is even an argument that factories are a better UX for users because you can clearly see you're calling a known function of a known contract rather than deploying a contract with some anonymous bytes which is kind of incomprehensible for users :slight_smile:

Amazing! I would also go for the factory approach as well.

factories are a better UX for users because you can clearly see you're calling a known function of a known contract rather than deploying a contract with some anonymous bytes which is kind of incomprehensible for users

That's a very fair point! The users can see a verified contract an methods behind the deployment.