OpenZeppelin / openzeppelin-contracts

OpenZeppelin Contracts is a library for secure smart contract development.
https://openzeppelin.com/contracts
MIT License
24.81k stars 11.77k forks source link

Add puppet proxy #4821

Open CodeSandwich opened 8 months ago

CodeSandwich commented 8 months ago

🧐 Motivation

While working on another project, I designed an AFAIK new proxy pattern, I called "puppet". I think that it could be useful as a general purpose tool, and if you agree, I can open a PR adding a library for supporting this pattern to OZ. The implementation with the technical details is here and the tests here.

📝 Details

A puppet is a contract that when called, acts like an empty account, it doesn't do anything and it has no API. The only exception is that if it's called by the address that deployed it, it delegates the call to the address passed to it in calldata. This gives the deployer the ability to execute any logic they want in the context of the puppet.

The puppet's logic doesn't need to be ever upgraded, to change its behavior the deployer needs to change the address it passes to the puppet to delegate to, or the calldata it passes for delegation. The entire fleet of puppets deployed by a single contract can be upgraded by upgrading the contract that deployed them, without using beacons. A nice trick is that the deployer can make the puppet delegate to the address holding the deployer's own logic, so the puppet's logic is encapsulated in the deployer's.

Deploying a new puppet is almost as cheap as deploying a new clone proxy. Its whole deployed bytecode is 66 bytes, and its creation code is 62 bytes. Just like clone proxy, it can be deployed using just the scratch space in memory. The cost to deploy a puppet is 45K gas, only 4K more than a clone. Because the bytecode is not compiled, it can be reliably deployed under a predictable CREATE2 address regardless of the compiler version. The bytecode doesn't use PUSH0, because many chains don't support it yet. The bytecode is made to resemble clone proxy's wherever it makes sense to simplify auditing.

Because the puppet can be deployed under a predictable address despite having no fixed logic, in some cases it can be used as a CREATE3 alternative. It can be also used as a full replacement of the CREATE3 factory by using a puppet deployed using CREATE2 to deploy arbitrary code using plain CREATE.

Personally I'm using puppets to create multiple accounts under predictable addresses, each with a separate ETH balance and separate ERC-20 balances. The contract that deploys them is upgradeable, and using puppets simplifies the upgrading process. The API of the deployer is designed so that nobody needs to call puppets directly, all the operations are made on the deployer contract, which then orders puppets to perform operations.

Amxx commented 8 months ago

Hello @CodeSandwich

That is an interresting approach. I'm not sure I see the usecases for it, and I can imagine compatibility issues with ERC-721 and ERC-1155 safe transfer mechanism.

Before we consider adding that approach to the library, we would need to see clear interrest (and usage) from the community. Ideally, we would want 2 or 3 independant projects/protocoles that have a clear usage for it. An ERC standardisation would also be nice to have.

CodeSandwich commented 8 months ago

That's a good concern, a puppet is incompatible with the safeTransfer functionality.

I'm working on the formal ERC, I'll post a link it here. TBF I completely agree with your cautious reaction, but I'm glad that you see some value in this pattern.

CodeSandwich commented 7 months ago

Here's the full ERC: https://github.com/ethereum/ERCs/pull/236

CodeSandwich commented 6 days ago

Merged as ERC-7613.

I find it useful not only for managing multiple token balances, but also for integrations with 3rd party protocols when multiple address-based identities are needed.